1. Integerated DashBoard, #97
app
package-lock.jsonpackage.json
src
app.tsx
assets/image
components
Dashboard
DashboardCard.tsxDashboardHome.tsxDashboardNavBar.tsxDashboardProjects.tsxDashboardTrash.tsxDashboardTutorial.tsxMarketPlaceBanner.tsxSidePannel.tsx
functions
socket
layout
Dashboard
sidebarRight
properties
visualization/IotInputCards
templates
temporary
ui
modules
builder
IntialLoad
aisle
asset
builder.tsxdfx
eventDeclaration
geomentries
aisles
floors
layers
lines
points
walls
groups
line
point
wall
collaboration
scene
camera
controls
|
@ -10,6 +10,8 @@
|
|||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.2",
|
||||
"@fingerprintjs/fingerprintjs-pro-react": "^2.6.3",
|
||||
"@react-three/csg": "^3.2.0",
|
||||
"@react-three/drei": "^9.113.0",
|
||||
"@react-three/fiber": "^8.17.7",
|
||||
|
@ -27,10 +29,11 @@
|
|||
"@use-gesture/react": "^10.3.1",
|
||||
"chart.js": "^4.4.8",
|
||||
"chartjs-plugin-annotation": "^3.1.0",
|
||||
"dxf-parser": "^1.1.2",
|
||||
"glob": "^11.0.0",
|
||||
"gsap": "^3.12.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"immer": "^10.1.1",
|
||||
"immer": "^9.0.21",
|
||||
"leva": "^0.10.0",
|
||||
"mqtt": "^5.10.4",
|
||||
"postprocessing": "^6.36.4",
|
||||
|
@ -2537,6 +2540,45 @@
|
|||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fingerprintjs/fingerprintjs": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.6.2.tgz",
|
||||
"integrity": "sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw==",
|
||||
"license": "BUSL-1.1",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fingerprintjs/fingerprintjs-pro": {
|
||||
"version": "3.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro/-/fingerprintjs-pro-3.11.10.tgz",
|
||||
"integrity": "sha512-zuQWT0YQLT0T//KcjEnyn4YBPlxXuXCtPiwEt2eTv03I/k2m370X+pCOfgU26AEjkmPrhCE77FLpRnm0IwiWrg==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fingerprintjs/fingerprintjs-pro-react": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro-react/-/fingerprintjs-pro-react-2.6.3.tgz",
|
||||
"integrity": "sha512-/axCq/cfjZkIM+WFZM/05FQvqtNfdKbIFKU6b2yrwPKlgT8BqWkAq8XvFX6JCPlq8/udVLJjFEDCK+1JQh1L6g==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fingerprintjs/fingerprintjs-pro-spa": "^1.3.1",
|
||||
"fast-deep-equal": "3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@fingerprintjs/fingerprintjs-pro-spa": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro-spa/-/fingerprintjs-pro-spa-1.3.2.tgz",
|
||||
"integrity": "sha512-s1YGsx1XQLmjU+av4UrUHNxyzwPHyZRB0GXJQFOJK8ZHCYc2SNukxnJmZA++bNBa8twU3wW+QgSJhA4Prjnd0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fingerprintjs/fingerprintjs-pro": "^3.11.0",
|
||||
"tslib": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz",
|
||||
|
@ -4142,7 +4184,6 @@
|
|||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
||||
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
|
@ -10089,6 +10130,15 @@
|
|||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
|
||||
},
|
||||
"node_modules/dxf-parser": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/dxf-parser/-/dxf-parser-1.1.2.tgz",
|
||||
"integrity": "sha512-GPTumUvRkounlIazLIyJMmTWt+nlg+ksS0Hdm8jWvejmZKBTz6gvHTam76wRm4PQMma5sgKLThblQyeIJcH79Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loglevel": "^1.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/earcut": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
|
||||
|
@ -12748,10 +12798,9 @@
|
|||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||
"license": "MIT",
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
|
@ -15204,6 +15253,18 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/loglevel": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
|
||||
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/loglevel"
|
||||
}
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
|
@ -18013,16 +18074,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dev-utils/node_modules/immer": {
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dev-utils/node_modules/loader-utils": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz",
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.2",
|
||||
"@fingerprintjs/fingerprintjs-pro-react": "^2.6.3",
|
||||
"@react-three/csg": "^3.2.0",
|
||||
"@react-three/drei": "^9.113.0",
|
||||
"@react-three/fiber": "^8.17.7",
|
||||
|
@ -22,10 +24,11 @@
|
|||
"@use-gesture/react": "^10.3.1",
|
||||
"chart.js": "^4.4.8",
|
||||
"chartjs-plugin-annotation": "^3.1.0",
|
||||
"dxf-parser": "^1.1.2",
|
||||
"glob": "^11.0.0",
|
||||
"gsap": "^3.12.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"immer": "^10.1.1",
|
||||
"immer": "^9.0.21",
|
||||
"leva": "^0.10.0",
|
||||
"mqtt": "^5.10.4",
|
||||
"postprocessing": "^6.36.4",
|
||||
|
|
|
@ -13,7 +13,7 @@ const App: React.FC = () => {
|
|||
<Routes>
|
||||
<Route path="/" element={<UserAuth />} />
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/project" element={<Project />} />
|
||||
<Route path="/:projectId" element={<Project />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</LoggerProvider>
|
||||
|
|
Binary file not shown.
After ![]() (image error) Size: 237 KiB |
Binary file not shown.
After ![]() (image error) Size: 251 KiB |
|
@ -0,0 +1,280 @@
|
|||
import React, { useState, useRef, useEffect } from "react";
|
||||
import img from "../../assets/image/image.png";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useProjectName, useSocketStore } from "../../store/builder/store";
|
||||
import { viewProject } from "../../services/dashboard/viewProject";
|
||||
import OuterClick from "../../utils/outerClick";
|
||||
import { KebabIcon } from "../icons/ExportCommonIcons";
|
||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
|
||||
interface DashBoardCardProps {
|
||||
projectName: string;
|
||||
thumbnail: any;
|
||||
projectId: string;
|
||||
createdAt?: string;
|
||||
handleDeleteProject?: (projectId: string) => Promise<void>;
|
||||
handleTrashDeleteProject?: (projectId: string) => Promise<void>;
|
||||
handleRestoreProject?: (projectId: string) => Promise<void>;
|
||||
handleDuplicateWorkspaceProject?: (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
) => Promise<void>;
|
||||
handleDuplicateRecentProject?: (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
) => Promise<void>;
|
||||
active?: string;
|
||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
||||
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
||||
}
|
||||
type RelativeTimeFormatUnit = any;
|
||||
|
||||
const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
projectName,
|
||||
thumbnail,
|
||||
projectId,
|
||||
active,
|
||||
handleDeleteProject,
|
||||
handleRestoreProject,
|
||||
handleTrashDeleteProject,
|
||||
handleDuplicateWorkspaceProject,
|
||||
handleDuplicateRecentProject,
|
||||
createdAt,
|
||||
setRecentDuplicateData,
|
||||
setProjectDuplicateData,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const { setProjectName } = useProjectName();
|
||||
const { userId, organization, userName } = getUserData();
|
||||
const [isKebabOpen, setIsKebabOpen] = useState(false);
|
||||
const [renameValue, setRenameValue] = useState(projectName);
|
||||
const [isRenaming, setIsRenaming] = useState(false);
|
||||
const { projectSocket } = useSocketStore();
|
||||
const kebabRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const navigateToProject = async (e: any) => {
|
||||
if (active && active == "trash") return;
|
||||
try {
|
||||
const viewedProject = await viewProject(organization, projectId, userId);
|
||||
console.log("Viewed project:", viewedProject);
|
||||
} catch (error) {
|
||||
console.error("Error opening project:", error);
|
||||
}
|
||||
|
||||
setProjectName(projectName);
|
||||
console.log('projectId: ', projectId);
|
||||
navigate(`/${projectId}`);
|
||||
};
|
||||
|
||||
const handleOptionClick = async (option: string) => {
|
||||
switch (option) {
|
||||
case "delete":
|
||||
if (handleDeleteProject) {
|
||||
await handleDeleteProject(projectId);
|
||||
} else if (handleTrashDeleteProject) {
|
||||
await handleTrashDeleteProject(projectId);
|
||||
}
|
||||
break;
|
||||
case "restore":
|
||||
if (handleRestoreProject) {
|
||||
await handleRestoreProject(projectId);
|
||||
}
|
||||
break;
|
||||
case "open in new tab":
|
||||
try {
|
||||
await viewProject(organization, projectId, userId);
|
||||
setProjectName(projectName);
|
||||
setIsKebabOpen(false);
|
||||
} catch (error) {
|
||||
console.error("Error opening project in new tab:", error);
|
||||
}
|
||||
window.open(`/${projectId}`, "_blank");
|
||||
break;
|
||||
case "rename":
|
||||
setIsRenaming(true);
|
||||
break;
|
||||
case "duplicate":
|
||||
if (handleDuplicateWorkspaceProject) {
|
||||
setProjectDuplicateData &&
|
||||
setProjectDuplicateData({
|
||||
projectId,
|
||||
projectName,
|
||||
thumbnail,
|
||||
});
|
||||
await handleDuplicateWorkspaceProject(projectId, projectName, thumbnail);
|
||||
} else if (handleDuplicateRecentProject) {
|
||||
setRecentDuplicateData &&
|
||||
setRecentDuplicateData({
|
||||
projectId,
|
||||
projectName,
|
||||
thumbnail,
|
||||
});
|
||||
await handleDuplicateRecentProject(projectId, projectName, thumbnail);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setIsKebabOpen(false);
|
||||
};
|
||||
|
||||
OuterClick({
|
||||
contextClassName: ["kebab-wrapper", "kebab-options-wrapper"],
|
||||
setMenuVisible: () => setIsKebabOpen(false),
|
||||
});
|
||||
|
||||
const handleProjectName = async (projectName: string) => {
|
||||
setRenameValue(projectName);
|
||||
if (!projectId) return;
|
||||
try {
|
||||
const projects = await getAllProjects(userId, organization);
|
||||
console.log("projects: ", projects);
|
||||
let projectUuid = projects.Projects.find(
|
||||
(val: any) => val.projectUuid === projectId || val._id === projectId
|
||||
);
|
||||
const updateProjects = {
|
||||
projectId: projectUuid,
|
||||
organization,
|
||||
userId,
|
||||
projectName,
|
||||
thumbnail: undefined,
|
||||
};
|
||||
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:update", updateProjects);
|
||||
}
|
||||
} catch (error) { }
|
||||
};
|
||||
|
||||
function getRelativeTime(dateString: string): string {
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
|
||||
|
||||
const intervals: Record<RelativeTimeFormatUnit, number> = {
|
||||
year: 31536000,
|
||||
month: 2592000,
|
||||
week: 604800,
|
||||
day: 86400,
|
||||
hour: 3600,
|
||||
minute: 60,
|
||||
second: 1,
|
||||
};
|
||||
|
||||
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
||||
|
||||
for (const key in intervals) {
|
||||
const unit = key as RelativeTimeFormatUnit;
|
||||
const diff = Math.floor(diffInSeconds / intervals[unit]);
|
||||
if (diff >= 1) {
|
||||
return rtf.format(-diff, unit);
|
||||
}
|
||||
}
|
||||
return "just now";
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className="dashboard-card-container"
|
||||
onClick={navigateToProject}
|
||||
title={projectName}
|
||||
>
|
||||
<div className="dashboard-card-wrapper">
|
||||
<div className="preview-container">
|
||||
{thumbnail ? <img src={thumbnail} alt="" /> : <img src={img} alt="" />}
|
||||
</div>
|
||||
<div className="project-details-container">
|
||||
<div className="project-details">
|
||||
{isRenaming ? (
|
||||
<input
|
||||
value={renameValue}
|
||||
onChange={(e) => {
|
||||
e.stopPropagation();
|
||||
handleProjectName(e.target.value);
|
||||
}}
|
||||
onBlur={() => {
|
||||
setIsRenaming(false);
|
||||
setProjectName(renameValue);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setProjectName(renameValue);
|
||||
setIsRenaming(false);
|
||||
}
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
) : (
|
||||
<span>{renameValue}</span>
|
||||
)}
|
||||
{createdAt && (
|
||||
<div className="project-data">
|
||||
{active && active == "trash" ? `Trashed by you` : `Edited `}{" "}
|
||||
{getRelativeTime(createdAt)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="users-list-container" ref={kebabRef}>
|
||||
<div className="user-profile">
|
||||
{userName ? userName.charAt(0).toUpperCase() : "A"}
|
||||
</div>
|
||||
<button
|
||||
className="kebab-wrapper"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
console.log("Kebab menu clicked");
|
||||
setIsKebabOpen((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
<KebabIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isKebabOpen && active !== "trash" && (
|
||||
<div className="kebab-options-wrapper">
|
||||
{["rename", "delete", "duplicate", "open in new tab"].map(
|
||||
(option) => (
|
||||
<button
|
||||
key={option}
|
||||
className="option"
|
||||
title={""}
|
||||
onClick={(e) => {
|
||||
console.log(option);
|
||||
e.stopPropagation();
|
||||
handleOptionClick(option);
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{isKebabOpen && active && active == "trash" && (
|
||||
<div className="kebab-options-wrapper">
|
||||
{["restore", "delete"].map((option) => (
|
||||
<button
|
||||
key={option}
|
||||
className="option"
|
||||
onClick={(e) => {
|
||||
console.log("option", option);
|
||||
e.stopPropagation();
|
||||
handleOptionClick(option);
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardCard;
|
|
@ -0,0 +1,166 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import MarketPlaceBanner from "./MarketPlaceBanner";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
import { recentlyViewed } from "../../services/dashboard/recentlyViewed";
|
||||
import { searchProject } from "../../services/dashboard/searchProjects";
|
||||
import { deleteProject } from "../../services/dashboard/deleteProject";
|
||||
import ProjectSocketRes from "./socket/projectSocketRes.dev";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface RecentProjectsData {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardHome: React.FC = () => {
|
||||
const [recentProjects, setRecentProjects] = useState<RecentProjectsData>({});
|
||||
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectSocket } = useSocketStore();
|
||||
const [recentDuplicateData, setRecentDuplicateData] = useState<Object>({});
|
||||
console.log("duplicateData: ", recentDuplicateData);
|
||||
|
||||
const fetchRecentProjects = async () => {
|
||||
try {
|
||||
const projects = await recentlyViewed(organization, userId);
|
||||
console.log("RecentlyViewed: ", projects);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) {
|
||||
setRecentProjects(projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching recent projects:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRecentProjectSearch = async (inputValue: string) => {
|
||||
if (!inputValue.trim()) {
|
||||
setIsSearchActive(false);
|
||||
return;
|
||||
}
|
||||
const filterRecentProcess = await searchProject(
|
||||
organization,
|
||||
userId,
|
||||
inputValue
|
||||
);
|
||||
setIsSearchActive(true);
|
||||
setRecentProjects(filterRecentProcess.message ? {} : filterRecentProcess);
|
||||
};
|
||||
|
||||
const handleDeleteProject = async (projectId: any) => {
|
||||
console.log("projectId:delete ", projectId);
|
||||
try {
|
||||
//API for delete project
|
||||
// const deletedProject = await deleteProject(
|
||||
// projectId,
|
||||
// userId,
|
||||
// organization
|
||||
// );
|
||||
// console.log('deletedProject: ', deletedProject);
|
||||
|
||||
//socket for delete Project
|
||||
const deleteProject = {
|
||||
projectId,
|
||||
organization,
|
||||
userId,
|
||||
};
|
||||
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:delete", deleteProject);
|
||||
}
|
||||
|
||||
setRecentProjects((prevDiscardedProjects: RecentProjectsData) => {
|
||||
if (!Array.isArray(prevDiscardedProjects?.RecentlyViewed)) {
|
||||
return prevDiscardedProjects;
|
||||
}
|
||||
const updatedProjectDatas = prevDiscardedProjects.RecentlyViewed.filter(
|
||||
(project) => project._id !== projectId
|
||||
);
|
||||
return {
|
||||
...prevDiscardedProjects,
|
||||
RecentlyViewed: updatedProjectDatas,
|
||||
};
|
||||
});
|
||||
setIsSearchActive(false);
|
||||
} catch (error) {
|
||||
console.error("Error deleting project:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDuplicateRecentProject = async (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
) => {
|
||||
const duplicateRecentProjectData = {
|
||||
userId,
|
||||
thumbnail,
|
||||
organization,
|
||||
projectUuid: projectId,
|
||||
projectName,
|
||||
};
|
||||
projectSocket.emit("v1:project:Duplicate", duplicateRecentProjectData);
|
||||
};
|
||||
|
||||
const renderProjects = () => {
|
||||
const projectList = recentProjects[Object.keys(recentProjects)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No recent projects found</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
projectList &&
|
||||
projectList.map((project) => (
|
||||
<DashboardCard
|
||||
key={project._id}
|
||||
projectName={project.projectName}
|
||||
thumbnail={project.thumbnail}
|
||||
projectId={project._id}
|
||||
createdAt={project.createdAt}
|
||||
handleDeleteProject={handleDeleteProject}
|
||||
handleDuplicateRecentProject={handleDuplicateRecentProject}
|
||||
setRecentDuplicateData={setRecentDuplicateData}
|
||||
/>
|
||||
))
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSearchActive) {
|
||||
fetchRecentProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page="home"
|
||||
handleRecentProjectSearch={handleRecentProjectSearch}
|
||||
/>
|
||||
<MarketPlaceBanner />
|
||||
<div className="container">
|
||||
<h2 className="section-header">Recents</h2>
|
||||
<div className="cards-container">{renderProjects()}</div>
|
||||
</div>
|
||||
{recentDuplicateData && Object.keys(recentDuplicateData).length > 0 && (
|
||||
<ProjectSocketRes
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
setRecentProjects={setRecentProjects}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHome;
|
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
import Search from "../ui/inputs/Search";
|
||||
import { CartIcon } from "../icons/ExportModuleIcons";
|
||||
|
||||
interface DashboardNavBarProps {
|
||||
page: React.ReactNode;
|
||||
handleProjectsSearch?: (inputValue: string) => Promise<void>;
|
||||
handleTrashSearch?: (inputValue: string) => Promise<void>;
|
||||
handleRecentProjectSearch?: (inputValue: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const DashboardNavBar: React.FC<DashboardNavBarProps> = ({
|
||||
page,
|
||||
handleProjectsSearch,
|
||||
handleTrashSearch,
|
||||
handleRecentProjectSearch,
|
||||
}) => {
|
||||
const handleSearch = async (inputValue: string) => {
|
||||
try {
|
||||
if (handleProjectsSearch) {
|
||||
await handleProjectsSearch(inputValue);
|
||||
} else if (handleTrashSearch) {
|
||||
await handleTrashSearch(inputValue);
|
||||
} else if (handleRecentProjectSearch) {
|
||||
await handleRecentProjectSearch(inputValue);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Search failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="dashboard-navbar-container">
|
||||
<div className="title">{page}</div>
|
||||
<div className="market-place-button">
|
||||
<CartIcon isActive /> Market Place
|
||||
</div>
|
||||
<Search onChange={handleSearch} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavBar;
|
|
@ -0,0 +1,185 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
import { searchProject } from "../../services/dashboard/searchProjects";
|
||||
import { deleteProject } from "../../services/dashboard/deleteProject";
|
||||
import ProjectSocketRes from "./socket/projectSocketRes.dev";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface WorkspaceProjects {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardProjects: React.FC = () => {
|
||||
const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>(
|
||||
{}
|
||||
);
|
||||
const [projectDuplicateData, setProjectDuplicateData] = useState<Object>({});
|
||||
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
|
||||
const [activeFolder, setActiveFolder] = useState<string>("myProjects");
|
||||
const { projectSocket } = useSocketStore();
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
const fetchAllProjects = async () => {
|
||||
try {
|
||||
const projects = await getAllProjects(userId, organization);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) {
|
||||
setWorkspaceProjects(projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching projects:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleProjectsSearch = async (inputValue: string) => {
|
||||
if (!inputValue.trim()) {
|
||||
setIsSearchActive(false);
|
||||
return;
|
||||
}
|
||||
if (!setWorkspaceProjects || !setIsSearchActive) return;
|
||||
|
||||
const searchedProject = await searchProject(organization, userId, inputValue);
|
||||
setIsSearchActive(true);
|
||||
setWorkspaceProjects(searchedProject.message ? {} : searchedProject);
|
||||
};
|
||||
|
||||
const handleDeleteProject = async (projectId: any) => {
|
||||
try {
|
||||
// const deletedProject = await deleteProject(
|
||||
// projectId,
|
||||
// userId,
|
||||
// organization
|
||||
// );
|
||||
// console.log('deletedProject: ', deletedProject);
|
||||
const deleteProjects = {
|
||||
projectId,
|
||||
organization: organization,
|
||||
userId,
|
||||
};
|
||||
|
||||
//socket for deleting the project
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:delete", deleteProjects);
|
||||
} else {
|
||||
console.error("Socket is not connected.");
|
||||
}
|
||||
setWorkspaceProjects((prevDiscardedProjects: WorkspaceProjects) => {
|
||||
if (!Array.isArray(prevDiscardedProjects?.Projects)) {
|
||||
return prevDiscardedProjects;
|
||||
}
|
||||
const updatedProjectDatas = prevDiscardedProjects.Projects.filter(
|
||||
(project) => project._id !== projectId
|
||||
);
|
||||
return {
|
||||
...prevDiscardedProjects,
|
||||
Projects: updatedProjectDatas,
|
||||
};
|
||||
});
|
||||
setIsSearchActive(false);
|
||||
} catch (error) {
|
||||
console.error("Error deleting project:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDuplicateWorkspaceProject = async (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
) => {
|
||||
// await handleDuplicateProjects({
|
||||
// userId,
|
||||
// organization,
|
||||
// projectId,
|
||||
// projectName,
|
||||
// projectSocket,
|
||||
// thumbnail,
|
||||
// setWorkspaceProjects,
|
||||
// setIsSearchActive
|
||||
// });
|
||||
const duplicateProjectData = {
|
||||
userId,
|
||||
thumbnail,
|
||||
organization,
|
||||
projectUuid: projectId,
|
||||
projectName,
|
||||
};
|
||||
projectSocket.emit("v1:project:Duplicate", duplicateProjectData);
|
||||
};
|
||||
|
||||
const renderProjects = () => {
|
||||
if (activeFolder !== "myProjects") return null;
|
||||
|
||||
const projectList = workspaceProjects[Object.keys(workspaceProjects)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No projects found</div>;
|
||||
}
|
||||
|
||||
return projectList.map((project) => (
|
||||
<DashboardCard
|
||||
key={project._id}
|
||||
projectName={project.projectName}
|
||||
thumbnail={project.thumbnail}
|
||||
projectId={project._id}
|
||||
createdAt={project.createdAt}
|
||||
handleDeleteProject={handleDeleteProject}
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject}
|
||||
setProjectDuplicateData={setProjectDuplicateData}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSearchActive) {
|
||||
fetchAllProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page="projects"
|
||||
handleProjectsSearch={handleProjectsSearch}
|
||||
/>
|
||||
|
||||
<div className="container" style={{ height: "calc(100% - 87px)" }}>
|
||||
<div className="header-wrapper" style={{ display: "flex", gap: "7px" }}>
|
||||
<button
|
||||
className={`header ${activeFolder === "myProjects" && "active"}`}
|
||||
onClick={() => setActiveFolder("myProjects")}
|
||||
>
|
||||
My Projects
|
||||
</button>
|
||||
<button
|
||||
className={`header ${activeFolder === "shared" && "active"}`}
|
||||
onClick={() => setActiveFolder("shared")}
|
||||
>
|
||||
Shared with me
|
||||
</button>
|
||||
</div>
|
||||
<div className="cards-container">{renderProjects()}</div>
|
||||
{projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && (
|
||||
<ProjectSocketRes
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
setWorkspaceProjects={setWorkspaceProjects}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardProjects;
|
|
@ -0,0 +1,160 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { trashSearchProject } from "../../services/dashboard/trashSearchProject";
|
||||
import { restoreTrash } from "../../services/dashboard/restoreTrash";
|
||||
import { getTrash } from "../../services/dashboard/getTrash";
|
||||
import { deleteTrash } from "../../services/dashboard/deleteTrash";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
DeletedAt: string;
|
||||
}
|
||||
|
||||
interface DiscardedProjects {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardTrash: React.FC = () => {
|
||||
const [discardedProjects, setDiscardedProjects] = useState<DiscardedProjects>(
|
||||
{}
|
||||
);
|
||||
const [isSearchActive, setIsSearchActive] = useState(false);
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectSocket } = useSocketStore();
|
||||
|
||||
const fetchTrashProjects = async () => {
|
||||
try {
|
||||
const projects = await getTrash(organization);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(discardedProjects)) {
|
||||
setDiscardedProjects(projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching trash projects:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTrashSearch = async (inputValue: string) => {
|
||||
if (!inputValue.trim()) {
|
||||
setIsSearchActive(false);
|
||||
return;
|
||||
}
|
||||
if (!setDiscardedProjects || !setIsSearchActive) return;
|
||||
|
||||
const filterTrashedProcess = await trashSearchProject(
|
||||
organization,
|
||||
userId,
|
||||
inputValue
|
||||
);
|
||||
setIsSearchActive(true);
|
||||
setDiscardedProjects(
|
||||
filterTrashedProcess.message ? {} : filterTrashedProcess
|
||||
);
|
||||
};
|
||||
|
||||
const handleRestoreProject = async (projectId: any) => {
|
||||
console.log("projectId: ", projectId);
|
||||
try {
|
||||
const restoreProject = await restoreTrash(organization, projectId);
|
||||
// console.log('restoreProject: ', restoreProject);
|
||||
|
||||
setDiscardedProjects((prevDiscardedProjects: DiscardedProjects) => {
|
||||
// Check if TrashDatas exists and is an array
|
||||
if (!Array.isArray(prevDiscardedProjects?.TrashDatas)) {
|
||||
console.error("TrashDatas is not an array", prevDiscardedProjects);
|
||||
return prevDiscardedProjects;
|
||||
}
|
||||
const updatedTrashDatas = prevDiscardedProjects.TrashDatas.filter(
|
||||
(project) => project._id !== projectId
|
||||
);
|
||||
return {
|
||||
...prevDiscardedProjects,
|
||||
TrashDatas: updatedTrashDatas,
|
||||
};
|
||||
});
|
||||
setIsSearchActive(false);
|
||||
} catch (error) {
|
||||
console.error("Error deleting project:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTrashDeleteProject = async (projectId: any) => {
|
||||
try {
|
||||
// const deletedProject = await deleteTrash(
|
||||
// organization, projectId
|
||||
// );
|
||||
|
||||
const deleteProjectTrash = {
|
||||
projectId,
|
||||
organization,
|
||||
userId,
|
||||
};
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:trash:delete", deleteProjectTrash);
|
||||
}
|
||||
setDiscardedProjects((prevDiscardedProjects: DiscardedProjects) => {
|
||||
if (!Array.isArray(prevDiscardedProjects?.TrashDatas)) {
|
||||
return prevDiscardedProjects;
|
||||
}
|
||||
const updatedProjectDatas = prevDiscardedProjects.TrashDatas.filter(
|
||||
(project) => project._id !== projectId
|
||||
);
|
||||
// console.log('updatedProjectDatas: ', updatedProjectDatas);
|
||||
return {
|
||||
...prevDiscardedProjects,
|
||||
TrashDatas: updatedProjectDatas,
|
||||
};
|
||||
});
|
||||
setIsSearchActive(false);
|
||||
} catch (error) {
|
||||
console.error("Error deleting project:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const renderTrashProjects = () => {
|
||||
const projectList = discardedProjects[Object.keys(discardedProjects)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No deleted projects found</div>;
|
||||
}
|
||||
|
||||
return projectList.map((project) => (
|
||||
<DashboardCard
|
||||
key={project._id}
|
||||
projectName={project.projectName}
|
||||
thumbnail={project.thumbnail}
|
||||
projectId={project._id}
|
||||
handleRestoreProject={handleRestoreProject}
|
||||
handleTrashDeleteProject={handleTrashDeleteProject}
|
||||
active={"trash"}
|
||||
createdAt={project.DeletedAt}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSearchActive) {
|
||||
fetchTrashProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar page="trash" handleTrashSearch={handleTrashSearch} />
|
||||
|
||||
<div className="container" style={{ height: "calc(100% - 87px)" }}>
|
||||
<div className="header" style={{ display: "flex", gap: "7px" }}></div>
|
||||
<div className="cards-container">{renderTrashProjects()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardTrash;
|
|
@ -0,0 +1,62 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import DashboardNavBar from './DashboardNavBar';
|
||||
import DashboardCard from './DashboardCard';
|
||||
import { projectTutorial } from '../../services/dashboard/projectTutorial';
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
}
|
||||
|
||||
interface DiscardedProjects {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardTutorial = () => {
|
||||
const [tutorialProject, setTutorialProject] = useState<DiscardedProjects>({})
|
||||
const handleIcon = async () => {
|
||||
try {
|
||||
let tutorial = await projectTutorial()
|
||||
setTutorialProject(tutorial)
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
handleIcon()
|
||||
}, [])
|
||||
const renderTrashProjects = () => {
|
||||
const projectList = tutorialProject[Object.keys(tutorialProject)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No deleted projects found</div>;
|
||||
}
|
||||
|
||||
return projectList.map((tutorials: any) => (
|
||||
<DashboardCard
|
||||
key={tutorials._id}
|
||||
projectName={tutorials.projectName}
|
||||
thumbnail={tutorials.thumbnail}
|
||||
projectId={tutorials._id}
|
||||
/>
|
||||
));
|
||||
};
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page="tutorial"
|
||||
/>
|
||||
|
||||
<div className="container" style={{ height: "calc(100% - 87px)" }}>
|
||||
<div className="header" style={{ display: 'flex', gap: '7px' }}></div>
|
||||
<div className="cards-container">
|
||||
{renderTrashProjects()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardTutorial;
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import banner from "../../../assets/image/banner.png";
|
||||
import banner from "../../assets/image/banner.png";
|
||||
|
||||
const MarketPlaceBanner = () => {
|
||||
return (
|
|
@ -0,0 +1,180 @@
|
|||
import React from "react";
|
||||
import {
|
||||
DocumentationIcon,
|
||||
HelpIcon,
|
||||
HomeIcon,
|
||||
LogoutIcon,
|
||||
NotificationIcon,
|
||||
ProjectsIcon,
|
||||
TutorialsIcon,
|
||||
} from "../icons/DashboardIcon";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import darkThemeImage from "../../assets/image/darkThemeProject.png";
|
||||
import lightThemeImage from "../../assets/image/lightThemeProject.png";
|
||||
import { SettingsIcon, TrashIcon } from "../icons/ExportCommonIcons";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useLoadingProgress, useSocketStore } from "../../store/builder/store";
|
||||
import { createProject } from "../../services/dashboard/createProject";
|
||||
|
||||
interface SidePannelProps {
|
||||
setActiveTab: React.Dispatch<React.SetStateAction<string>>;
|
||||
activeTab: string;
|
||||
}
|
||||
|
||||
const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
const { email, userName, userId, organization } = getUserData();
|
||||
const navigate = useNavigate();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { projectSocket } = useSocketStore();
|
||||
const savedTheme = localStorage.getItem("theme") ?? "light";
|
||||
|
||||
function generateProjectId() {
|
||||
const randomBytes = new Uint8Array(12);
|
||||
crypto.getRandomValues(randomBytes);
|
||||
return Array.from(randomBytes, (byte) =>
|
||||
byte.toString(16).padStart(2, "0")
|
||||
).join("");
|
||||
}
|
||||
|
||||
const handleCreateNewProject = async () => {
|
||||
const token = localStorage.getItem("token");
|
||||
try {
|
||||
const projectId = generateProjectId();
|
||||
useSocketStore.getState().initializeSocket(email, organization, token);
|
||||
|
||||
|
||||
//API for creating new Project
|
||||
// const project = await createProject(
|
||||
// projectId,
|
||||
// userId,
|
||||
// savedTheme === "dark" ? darkThemeImage : lightThemeImage,
|
||||
// organization
|
||||
// );
|
||||
// console.log('Created project: ', project);
|
||||
const addProject = {
|
||||
userId,
|
||||
thumbnail: savedTheme === "dark" ? darkThemeImage : lightThemeImage,
|
||||
organization: organization,
|
||||
projectUuid: projectId,
|
||||
};
|
||||
console.log("projectSocket: ", projectSocket);
|
||||
if (projectSocket) {
|
||||
// console.log('addProject: ', addProject);
|
||||
const handleResponse = (data: any) => {
|
||||
console.log('Project add response:', data);
|
||||
if (data.message === "Project created successfully") {
|
||||
setLoadingProgress(1)
|
||||
navigate(`/${data.data.projectId}`);
|
||||
}
|
||||
projectSocket.off("v1-project:response:add", handleResponse); // Clean up
|
||||
};
|
||||
projectSocket.on("v1-project:response:add", handleResponse);
|
||||
|
||||
console.log('addProject: ', addProject);
|
||||
projectSocket.emit("v1:project:add", addProject);
|
||||
} else {
|
||||
console.error("Socket is not connected.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating project:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="side-pannel-container">
|
||||
<div className="side-pannel-header">
|
||||
<div className="user-container">
|
||||
<div className="user-profile">
|
||||
{userName?.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div className="user-name">
|
||||
{userName
|
||||
? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()
|
||||
: "Anonymous"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="notifications-container">
|
||||
<NotificationIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="new-project-button"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={handleCreateNewProject}
|
||||
>
|
||||
+ New project
|
||||
</div>
|
||||
<div className="side-bar-content-container">
|
||||
<div className="side-bar-options-container">
|
||||
<div
|
||||
className={
|
||||
activeTab === "Home" ? "option-list active" : "option-list"
|
||||
}
|
||||
onClick={() => setActiveTab("Home")}
|
||||
>
|
||||
<HomeIcon />
|
||||
Home
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
activeTab === "Projects" ? "option-list active" : "option-list"
|
||||
}
|
||||
title="Projects"
|
||||
onClick={() => setActiveTab("Projects")}
|
||||
>
|
||||
<ProjectsIcon />
|
||||
Projects
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
activeTab === "Trash" ? "option-list active" : "option-list"
|
||||
}
|
||||
title="Trash"
|
||||
onClick={() => setActiveTab("Trash")}
|
||||
>
|
||||
<TrashIcon />
|
||||
Trash
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
activeTab === "Tutorials" ? "option-list active" : "option-list"
|
||||
}
|
||||
title="coming soon"
|
||||
onClick={() => setActiveTab("Tutorials")}
|
||||
>
|
||||
<TutorialsIcon />
|
||||
Tutorials
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
activeTab === "Documentation"
|
||||
? "option-list active"
|
||||
: "option-list"
|
||||
}
|
||||
title="coming soon"
|
||||
onClick={() => setActiveTab("Documentation")}
|
||||
>
|
||||
<DocumentationIcon />
|
||||
Documentation
|
||||
</div>
|
||||
</div>
|
||||
<div className="side-bar-options-container" title="coming soon">
|
||||
<div className="option-list">
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</div>
|
||||
<div className="option-list" style={{ cursor: "pointer" }}>
|
||||
<LogoutIcon />
|
||||
Log out
|
||||
</div>
|
||||
<div className="option-list">
|
||||
<HelpIcon />
|
||||
Help & Feedback
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidePannel;
|
|
@ -0,0 +1,31 @@
|
|||
interface UserData {
|
||||
email: string;
|
||||
userId: string;
|
||||
userName?: string; // Optional
|
||||
organization: string;
|
||||
}
|
||||
|
||||
export const getUserData = (): UserData => {
|
||||
const email = localStorage.getItem("email");
|
||||
const userId = localStorage.getItem("userId");
|
||||
const userName = localStorage.getItem("userName");
|
||||
|
||||
if (!email || !userId) {
|
||||
throw new Error("User data not found in localStorage");
|
||||
}
|
||||
|
||||
const [_, emailDomain] = email.split("@");
|
||||
|
||||
if (!emailDomain) {
|
||||
throw new Error("Invalid email format");
|
||||
}
|
||||
|
||||
const [organization] = emailDomain.split(".");
|
||||
|
||||
return {
|
||||
email: email,
|
||||
userId: userId,
|
||||
userName: userName ?? undefined,
|
||||
organization,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
import React, { useEffect } from 'react';
|
||||
import { useSocketStore } from '../../../store/builder/store';
|
||||
import { getUserData } from '../functions/getUserData';
|
||||
import { getAllProjects } from '../../../services/dashboard/getAllProjects';
|
||||
import { recentlyViewed } from '../../../services/dashboard/recentlyViewed';
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface ProjectsData {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
interface ProjectSocketResProps {
|
||||
setRecentProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
|
||||
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
|
||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const ProjectSocketRes = ({
|
||||
setRecentProjects,
|
||||
setWorkspaceProjects,
|
||||
setIsSearchActive,
|
||||
}: ProjectSocketResProps) => {
|
||||
const { projectSocket } = useSocketStore();
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectSocket) return;
|
||||
|
||||
const handleAdd = (data: any) => {
|
||||
// console.log('Add:', data);
|
||||
};
|
||||
|
||||
const handleDelete = (data: any) => {
|
||||
// console.log('Delete:', data);
|
||||
};
|
||||
|
||||
const handleUpdate = (data: any) => {
|
||||
// console.log('Update:', data);
|
||||
};
|
||||
|
||||
const handleTrashDelete = (data: any) => {
|
||||
// console.log('Trash Delete:', data);
|
||||
};
|
||||
|
||||
const handleDuplicate = async (data: any) => {
|
||||
console.log("Project duplicate response:", data);
|
||||
if (data?.message === "Project Duplicated successfully") {
|
||||
if (setWorkspaceProjects) {
|
||||
const allProjects = await getAllProjects(userId, organization);
|
||||
console.log('allProjects: ', allProjects);
|
||||
setWorkspaceProjects(allProjects);
|
||||
} else if (setRecentProjects) {
|
||||
const recentProjects = await recentlyViewed(organization, userId);
|
||||
setRecentProjects && setRecentProjects(recentProjects);
|
||||
}
|
||||
setIsSearchActive && setIsSearchActive(false);
|
||||
} else {
|
||||
console.warn("Duplication failed or unexpected response.");
|
||||
}
|
||||
};
|
||||
|
||||
projectSocket.on("v1-project:response:add", handleAdd);
|
||||
projectSocket.on("v1-project:response:delete", handleDelete);
|
||||
projectSocket.on("v1-project:response:update", handleUpdate);
|
||||
projectSocket.on("v1-project:response:Duplicate", handleDuplicate);
|
||||
projectSocket.on("v1:trash:response:delete", handleTrashDelete);
|
||||
|
||||
return () => {
|
||||
projectSocket.off("v1-project:response:add", handleAdd);
|
||||
projectSocket.off("v1-project:response:delete", handleDelete);
|
||||
projectSocket.off("v1-project:response:update", handleUpdate);
|
||||
projectSocket.off("v1-project:response:Duplicate", handleDuplicate);
|
||||
projectSocket.off("v1:trash:response:delete", handleTrashDelete);
|
||||
};
|
||||
}, [projectSocket, userId, organization]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ProjectSocketRes;
|
|
@ -1,25 +0,0 @@
|
|||
import React from "react";
|
||||
import { KebabIcon } from "../../icons/ExportCommonIcons";
|
||||
import img from "../../../assets/image/image.png"
|
||||
|
||||
const DashboardCard:React.FC = () => {
|
||||
return (
|
||||
<div className="dashboard-card-container">
|
||||
<div className="preview-container">
|
||||
<img src={img} alt="" />
|
||||
</div>
|
||||
<div className="project-details-container">
|
||||
<div className="project-details">
|
||||
<div className="project-name">Untitled</div>
|
||||
<div className="project-data">24-12-2025</div>
|
||||
</div>
|
||||
<div className="users-list-container">
|
||||
<div className="user-profile">V</div>
|
||||
<KebabIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardCard;
|
|
@ -1,21 +0,0 @@
|
|||
import React from "react";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import MarketPlaceBanner from "./MarketPlaceBanner";
|
||||
|
||||
const DashboardHome: React.FC = () => {
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar page={"home"} />
|
||||
<MarketPlaceBanner />
|
||||
<div className="container">
|
||||
<div className="header">Recents</div>
|
||||
<div className="cards-container">
|
||||
<DashboardCard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHome;
|
|
@ -1,21 +0,0 @@
|
|||
import React from "react";
|
||||
import { CartIcon } from "../../icons/ExportModuleIcons";
|
||||
import Search from "../../ui/inputs/Search";
|
||||
|
||||
interface DashboardNavBarProps {
|
||||
page: React.ReactNode;
|
||||
}
|
||||
|
||||
const DashboardNavBar: React.FC<DashboardNavBarProps> = ({ page }) => {
|
||||
return (
|
||||
<div className="dashboard-navbar-container">
|
||||
<div className="title">{page}</div>
|
||||
<div className="market-place-button">
|
||||
<CartIcon isActive /> Market Place
|
||||
</div>
|
||||
<Search onChange={() => {}} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavBar;
|
|
@ -1,69 +0,0 @@
|
|||
import React from "react";
|
||||
import {
|
||||
DocumentationIcon,
|
||||
HelpIcon,
|
||||
HomeIcon,
|
||||
LogoutIcon,
|
||||
NotificationIcon,
|
||||
ProjectsIcon,
|
||||
TutorialsIcon,
|
||||
} from "../../icons/DashboardIcon";
|
||||
import { SettingsIcon, TrashIcon } from "../../icons/ExportCommonIcons";
|
||||
|
||||
const SidePannel: React.FC = () => {
|
||||
const userName = localStorage.getItem("userName") ?? "Anonymous";
|
||||
return (
|
||||
<div className="side-pannel-container">
|
||||
<div className="side-pannel-header">
|
||||
<div className="user-container">
|
||||
<div className="user-profile">{userName[0]}</div>
|
||||
<div className="user-name">{userName}</div>
|
||||
</div>
|
||||
<div className="notifications-container">
|
||||
<NotificationIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="new-project-button">+ New project</div>
|
||||
<div className="side-bar-content-container">
|
||||
<div className="side-bar-options-container">
|
||||
<div className="option-list active">
|
||||
<HomeIcon />
|
||||
Home
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<ProjectsIcon />
|
||||
Projects
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<TrashIcon />
|
||||
Trash
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<TutorialsIcon />
|
||||
Tutorials
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<DocumentationIcon />
|
||||
Documentation
|
||||
</div>
|
||||
</div>
|
||||
<div className="side-bar-options-container" title="coming soon">
|
||||
<div className="option-list">
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</div>
|
||||
<div className="option-list" style={{cursor: "pointer"}}>
|
||||
<LogoutIcon />
|
||||
Log out
|
||||
</div>
|
||||
<div className="option-list">
|
||||
<HelpIcon />
|
||||
Help & Feedback
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidePannel;
|
|
@ -12,10 +12,11 @@ import Directional from "../../../../assets/image/aisleTypes/Directional.png";
|
|||
import Dotted from "../../../../assets/image/aisleTypes/Dotted.png";
|
||||
import Solid from "../../../../assets/image/aisleTypes/Solid.png";
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import InputToggle from "../../../ui/inputs/InputToggle";
|
||||
|
||||
interface TextureItem {
|
||||
color: AisleColors;
|
||||
id: string;
|
||||
color: string;
|
||||
id: AisleColors;
|
||||
brief: string;
|
||||
texture: string;
|
||||
}
|
||||
|
@ -24,17 +25,17 @@ const AisleProperties: React.FC = () => {
|
|||
const [collapsePresets, setCollapsePresets] = useState(false);
|
||||
const [collapseTexture, setCollapseTexture] = useState(true);
|
||||
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength } = useBuilderStore();
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength, setIsFlipped } = useBuilderStore();
|
||||
|
||||
const aisleTextureList: TextureItem[] = [
|
||||
{ color: "yellow", id: "yellow1", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "yellow", id: "yellow", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "gray", id: "gray", brief: "basic", texture: "" },
|
||||
{ color: "green", id: "green1", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "green", id: "green", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "orange", id: "orange", brief: "material flow", texture: "" },
|
||||
{ color: "blue", id: "blue", brief: "vehicle paths", texture: "" },
|
||||
{ color: "purple", id: "purple", brief: "material flow", texture: "" },
|
||||
{ color: "red", id: "red", brief: "safety zone", texture: "" },
|
||||
{ color: "bright green", id: "bright-green", brief: "safety zone", texture: "" },
|
||||
{ color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" },
|
||||
{ color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" },
|
||||
{ color: "white-black", id: "white-black", brief: "utility aisles", texture: "" },
|
||||
];
|
||||
|
@ -90,6 +91,10 @@ const AisleProperties: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleIsFlippedChange = () => {
|
||||
setIsFlipped(!aisleIsFlipped)
|
||||
};
|
||||
|
||||
const dashLengthValue = useMemo(() => {
|
||||
return dashLength.toString();
|
||||
}, [aisleType, dashLength]);
|
||||
|
@ -110,6 +115,10 @@ const AisleProperties: React.FC = () => {
|
|||
return aisleLength.toString();
|
||||
}, [aisleType, aisleLength]);
|
||||
|
||||
const aisleIsFlipped = useMemo(() => {
|
||||
return isFlipped;
|
||||
}, [aisleType, isFlipped]);
|
||||
|
||||
const renderAdvancedProperties = () => {
|
||||
switch (aisleType) {
|
||||
case 'dashed-aisle':
|
||||
|
@ -157,7 +166,8 @@ const AisleProperties: React.FC = () => {
|
|||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleGapLengthChange}
|
||||
/></>
|
||||
/>
|
||||
</>
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
@ -181,7 +191,21 @@ const AisleProperties: React.FC = () => {
|
|||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleGapLengthChange}
|
||||
/></>
|
||||
/>
|
||||
</>
|
||||
}
|
||||
</>
|
||||
);
|
||||
case 'junction-aisle': case 'arc-aisle':
|
||||
return (
|
||||
<>
|
||||
{aisleType &&
|
||||
<InputToggle
|
||||
inputKey="Flip Ailse"
|
||||
label="Flip Aisle"
|
||||
value={aisleIsFlipped}
|
||||
onClick={handleIsFlippedChange}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
@ -258,7 +282,7 @@ const AisleProperties: React.FC = () => {
|
|||
key={val.id}
|
||||
title={val.brief || val.id}
|
||||
className={`aisle-list ${aisleColor === val.color ? "selected" : ""}`}
|
||||
onClick={() => setAisleColor(val.color)}
|
||||
onClick={() => setAisleColor(val.id)}
|
||||
aria-pressed={aisleColor === val.id}
|
||||
>
|
||||
<div className="texture-display">{val.texture}</div>
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from "../../../../store/builder/store";
|
||||
import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment";
|
||||
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||
import { validateBBox } from "@turf/helpers";
|
||||
import { useParams } from "react-router-dom";
|
||||
const GlobalProperties: React.FC = () => {
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
|
||||
|
@ -37,6 +37,7 @@ const GlobalProperties: React.FC = () => {
|
|||
|
||||
const [limitGridDistance, setLimitGridDistance] = useState(false);
|
||||
const [gridDistance, setGridDistance] = useState<number>(3);
|
||||
const { projectId } = useParams();
|
||||
|
||||
const optimizeScene = async (value: any) => {
|
||||
const email = localStorage.getItem("email");
|
||||
|
@ -49,7 +50,8 @@ const GlobalProperties: React.FC = () => {
|
|||
roofVisibility,
|
||||
shadows,
|
||||
30,
|
||||
true
|
||||
true,
|
||||
projectId
|
||||
);
|
||||
setRenderDistance(30);
|
||||
setLimitDistance(true);
|
||||
|
@ -67,7 +69,8 @@ const GlobalProperties: React.FC = () => {
|
|||
roofVisibility,
|
||||
shadows,
|
||||
75,
|
||||
!limitDistance
|
||||
!limitDistance,
|
||||
projectId
|
||||
);
|
||||
setRenderDistance(75);
|
||||
} else {
|
||||
|
@ -78,7 +81,8 @@ const GlobalProperties: React.FC = () => {
|
|||
roofVisibility,
|
||||
shadows,
|
||||
renderDistance,
|
||||
!limitDistance
|
||||
!limitDistance,
|
||||
projectId
|
||||
);
|
||||
}
|
||||
setLimitDistance(!limitDistance);
|
||||
|
@ -111,7 +115,8 @@ const GlobalProperties: React.FC = () => {
|
|||
roofVisibility,
|
||||
shadows,
|
||||
value,
|
||||
limitDistance
|
||||
limitDistance,
|
||||
projectId
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -128,7 +133,8 @@ const GlobalProperties: React.FC = () => {
|
|||
!roofVisibility,
|
||||
shadows,
|
||||
renderDistance,
|
||||
limitDistance
|
||||
limitDistance,
|
||||
projectId
|
||||
);
|
||||
//
|
||||
|
||||
|
@ -157,7 +163,7 @@ const GlobalProperties: React.FC = () => {
|
|||
roofVisibility,
|
||||
shadows,
|
||||
renderDistance,
|
||||
limitDistance
|
||||
limitDistance,projectId
|
||||
);
|
||||
//
|
||||
|
||||
|
@ -186,7 +192,8 @@ const GlobalProperties: React.FC = () => {
|
|||
roofVisibility,
|
||||
!shadows,
|
||||
renderDistance,
|
||||
limitDistance
|
||||
limitDistance,
|
||||
projectId
|
||||
);
|
||||
//
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ const ZoneProperties: React.FC = () => {
|
|||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
let zonesdata = {
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
viewPortposition: zonePosition,
|
||||
viewPortCenter: zoneTarget,
|
||||
};
|
||||
|
@ -52,7 +52,7 @@ const ZoneProperties: React.FC = () => {
|
|||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const zonesdata = {
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
zoneName: newName,
|
||||
};
|
||||
// Call your API to update the zone
|
||||
|
@ -61,7 +61,7 @@ const ZoneProperties: React.FC = () => {
|
|||
if (response.message === "updated successfully") {
|
||||
setZones((prevZones: any[]) =>
|
||||
prevZones.map((zone) =>
|
||||
zone.zoneId === selectedZone.zoneId
|
||||
zone.zoneUuid === selectedZone.zoneUuid
|
||||
? { ...zone, zoneName: newName }
|
||||
: zone
|
||||
)
|
||||
|
@ -80,7 +80,7 @@ const ZoneProperties: React.FC = () => {
|
|||
return zones.some(
|
||||
(zone: any) =>
|
||||
zone.zoneName.trim().toLowerCase() === name.trim().toLowerCase() &&
|
||||
zone.zoneId !== selectedZone.zoneId
|
||||
zone.zoneUuid !== selectedZone.zoneUuid
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -8,11 +8,9 @@ import { upsertProductOrEventApi } from "../../../../../../services/simulation/p
|
|||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore";
|
||||
import * as THREE from 'three';
|
||||
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
||||
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
|
||||
|
||||
function StorageMechanics() {
|
||||
const { storageUnitStore } = useSceneContext();
|
||||
const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default");
|
||||
const [selectedPointData, setSelectedPointData] = useState<StoragePointSchema | undefined>();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
|
@ -20,7 +18,6 @@ function StorageMechanics() {
|
|||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
|
||||
const { setCurrentMaterials, clearCurrentMaterials, updateCurrentLoad, getStorageUnitById } = storageUnitStore();
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
@ -92,22 +89,6 @@ function StorageMechanics() {
|
|||
);
|
||||
updateSelectedPointData();
|
||||
}
|
||||
|
||||
if (option === "spawn") {
|
||||
const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid);
|
||||
if (storageUnit) {
|
||||
const materialType = selectedPointData.action.materialType || "Default material";
|
||||
const materials = Array.from({ length: selectedPointData.action.storageCapacity }, () =>
|
||||
createNewMaterial(materialType)
|
||||
).filter(Boolean) as { materialType: string; materialId: string }[];
|
||||
|
||||
setCurrentMaterials(selectedEventData.data.modelUuid, materials);
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, selectedPointData.action.storageCapacity);
|
||||
}
|
||||
} else {
|
||||
clearCurrentMaterials(selectedEventData.data.modelUuid);
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRenameAction = (newName: string) => {
|
||||
|
@ -141,21 +122,6 @@ function StorageMechanics() {
|
|||
event
|
||||
);
|
||||
updateSelectedPointData();
|
||||
|
||||
if (activeOption === "spawn") {
|
||||
const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid);
|
||||
if (storageUnit) {
|
||||
const materialType = selectedPointData.action.materialType || "Default material";
|
||||
const materials = Array.from({ length: selectedPointData.action.storageCapacity }, () =>
|
||||
createNewMaterial(materialType)
|
||||
).filter(Boolean) as { materialType: string; materialId: string }[];
|
||||
|
||||
setCurrentMaterials(selectedEventData.data.modelUuid, materials);
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, selectedPointData.action.storageCapacity);
|
||||
}
|
||||
} else {
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -183,17 +149,6 @@ function StorageMechanics() {
|
|||
event
|
||||
);
|
||||
updateSelectedPointData();
|
||||
|
||||
if (activeOption === "spawn") {
|
||||
const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid);
|
||||
if (storageUnit) {
|
||||
const materials = Array.from({ length: storageUnit.currentMaterials.length }, () =>
|
||||
createNewMaterial(value)
|
||||
).filter(Boolean) as { materialType: string; materialId: string }[];
|
||||
|
||||
setCurrentMaterials(selectedEventData.data.modelUuid, materials);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -6,17 +6,14 @@ import Trigger from "../trigger/Trigger";
|
|||
import {
|
||||
useSelectedAction,
|
||||
useSelectedEventData,
|
||||
useSelectedEventSphere,
|
||||
} from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import TravelAction from "../actions/TravelAction";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
||||
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
|
||||
|
||||
function VehicleMechanics() {
|
||||
const { vehicleStore } = useSceneContext();
|
||||
const [activeOption, setActiveOption] = useState<"default" | "travel">("default");
|
||||
const [selectedPointData, setSelectedPointData] = useState<VehiclePointSchema | undefined>();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
|
@ -24,7 +21,6 @@ function VehicleMechanics() {
|
|||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
|
||||
const { getVehicleById } = vehicleStore();
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
@ -192,35 +188,16 @@ function VehicleMechanics() {
|
|||
? selectedPointData.action.unLoadDuration.toString()
|
||||
: "1";
|
||||
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
|
||||
function handleClearPoints() {
|
||||
const updatedVehicle = getVehicleById(
|
||||
selectedEventSphere?.userData.modelUuid
|
||||
);
|
||||
|
||||
if (
|
||||
!selectedProduct?.productId ||
|
||||
!selectedEventSphere?.userData?.modelUuid ||
|
||||
!updatedVehicle?.point
|
||||
)
|
||||
return;
|
||||
if (!selectedEventData || !selectedPointData?.action.actionUuid) return;
|
||||
|
||||
const event = updateEvent(
|
||||
selectedProduct.productId,
|
||||
selectedEventSphere.userData.modelUuid,
|
||||
{
|
||||
point: {
|
||||
...updatedVehicle.point,
|
||||
action: {
|
||||
...updatedVehicle.point?.action,
|
||||
const event = updateAction(
|
||||
selectedProduct.productId, selectedPointData.action.actionUuid, {
|
||||
pickUpPoint: null,
|
||||
unLoadPoint: null,
|
||||
steeringAngle: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
})
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone
|
|||
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
||||
import axios from "axios";
|
||||
import RenameInput from "../../../../ui/inputs/RenameInput";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
type Props = {};
|
||||
|
||||
|
@ -24,6 +25,8 @@ const BarChartInput = (props: Props) => {
|
|||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const [isLoading, setLoading] = useState<boolean>(true);
|
||||
const { projectId } = useParams();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
|
@ -50,7 +53,15 @@ const BarChartInput = (props: Props) => {
|
|||
if (selectedChartId.id !== "") {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setSelections(response.data.Data.measurements);
|
||||
|
@ -83,10 +94,18 @@ const BarChartInput = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
},
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
panel: selectedChartId.panel,
|
||||
|
@ -123,7 +142,7 @@ const BarChartInput = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -87,10 +87,10 @@ const FleetEfficiencyInputComponent = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/floatWidget/save`,
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
header: inputName,
|
||||
|
@ -127,7 +127,7 @@ const FleetEfficiencyInputComponent = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -86,10 +86,10 @@ const FlotingWidgetInput = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/floatWidget/save`,
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
header: inputName,
|
||||
|
@ -126,7 +126,7 @@ const FlotingWidgetInput = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone
|
|||
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
||||
import axios from "axios";
|
||||
import RenameInput from "../../../../ui/inputs/RenameInput";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
type Props = {};
|
||||
|
||||
|
@ -140,6 +141,7 @@ const LineGrapInput = (props: Props) => {
|
|||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const [isLoading, setLoading] = useState<boolean>(true);
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
|
@ -166,7 +168,15 @@ const LineGrapInput = (props: Props) => {
|
|||
if (selectedChartId.id !== "") {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setSelections(response.data.Data.measurements);
|
||||
|
@ -199,10 +209,18 @@ const LineGrapInput = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
},
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
panel: selectedChartId.panel,
|
||||
|
@ -239,7 +257,7 @@ const LineGrapInput = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone
|
|||
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
||||
import axios from "axios";
|
||||
import RenameInput from "../../../../ui/inputs/RenameInput";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
type Props = {};
|
||||
|
||||
|
@ -24,6 +25,8 @@ const PieChartInput = (props: Props) => {
|
|||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const [isLoading, setLoading] = useState<boolean>(true);
|
||||
const { projectId } = useParams();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
|
@ -50,7 +53,15 @@ const PieChartInput = (props: Props) => {
|
|||
if (selectedChartId.id !== "") {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setSelections(response.data.Data.measurements);
|
||||
|
@ -83,10 +94,18 @@ const PieChartInput = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
},
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
panel: selectedChartId.panel,
|
||||
|
@ -123,7 +142,7 @@ const PieChartInput = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone
|
|||
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
||||
import axios from "axios";
|
||||
import RenameInput from "../../../../ui/inputs/RenameInput";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
type Props = {};
|
||||
|
||||
|
@ -24,6 +25,8 @@ const Progress1Input = (props: Props) => {
|
|||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const [isLoading, setLoading] = useState<boolean>(true);
|
||||
const { projectId } = useParams();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
|
@ -50,7 +53,15 @@ const Progress1Input = (props: Props) => {
|
|||
if (selectedChartId.id !== "") {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setSelections(response.data.Data.measurements);
|
||||
|
@ -83,10 +94,18 @@ const Progress1Input = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
},
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
panel: selectedChartId.panel,
|
||||
|
@ -123,7 +142,7 @@ const Progress1Input = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone
|
|||
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
||||
import axios from "axios";
|
||||
import RenameInput from "../../../../ui/inputs/RenameInput";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
type Props = {};
|
||||
|
||||
|
@ -24,6 +25,8 @@ const Progress2Input = (props: Props) => {
|
|||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const [isLoading, setLoading] = useState<boolean>(true);
|
||||
const { projectId } = useParams();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
|
@ -50,7 +53,15 @@ const Progress2Input = (props: Props) => {
|
|||
if (selectedChartId.id !== "") {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setSelections(response.data.Data.measurements);
|
||||
|
@ -83,10 +94,18 @@ const Progress2Input = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>", // Replace with actual token
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
},
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
panel: selectedChartId.panel,
|
||||
|
@ -123,7 +142,7 @@ const Progress2Input = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -84,10 +84,10 @@ const WarehouseThroughputInputComponent = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/floatWidget/save`,
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
header: inputName,
|
||||
|
@ -123,7 +123,7 @@ const WarehouseThroughputInputComponent = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -83,10 +83,10 @@ const Widget2InputCard3D = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget3d/save`,
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
widgetName: inputName,
|
||||
|
@ -122,7 +122,7 @@ const Widget2InputCard3D = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -80,10 +80,10 @@ const Widget3InputCard3D = () => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget3d/save`,
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
widgetName: inputName,
|
||||
|
@ -119,7 +119,7 @@ const Widget3InputCard3D = () => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -83,10 +83,10 @@ const Widget4InputCard3D = (props: Props) => {
|
|||
) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`,
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget3d/save`,
|
||||
{
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
widgetName: inputName,
|
||||
|
@ -122,7 +122,7 @@ const Widget4InputCard3D = (props: Props) => {
|
|||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
// console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
|
|
|
@ -1,19 +1,56 @@
|
|||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import RenderOverlay from "./Overlay";
|
||||
import { LogoIconLarge } from "../icons/Logo";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useProjectName } from "../../store/builder/store";
|
||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
|
||||
interface LoadingPageProps {
|
||||
progress: number; // Expect progress as a percentage (0-100)
|
||||
}
|
||||
|
||||
const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
|
||||
const { projectName, setProjectName } = useProjectName();
|
||||
const { projectId } = useParams();
|
||||
const validatedProgress = Math.min(100, Math.max(0, progress));
|
||||
const generateThumbnail = async () => {
|
||||
const email = localStorage.getItem("email");
|
||||
const userId = localStorage.getItem("userId");
|
||||
try {
|
||||
if (!email || !userId) {
|
||||
console.error("User data not found in localStorage");
|
||||
return;
|
||||
}
|
||||
|
||||
const emailParts = email.split("@");
|
||||
if (emailParts.length < 2) {
|
||||
console.error("Invalid email format");
|
||||
return;
|
||||
}
|
||||
|
||||
const domainParts = emailParts[1].split(".");
|
||||
const Organization = domainParts[0];
|
||||
const projects = await getAllProjects(
|
||||
userId, Organization
|
||||
);
|
||||
const filterProject = projects?.Projects.find((val: any) => val.projectUuid === projectId || val._id
|
||||
=== projectId)
|
||||
|
||||
setProjectName(filterProject.projectName)
|
||||
|
||||
}
|
||||
catch {
|
||||
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
generateThumbnail();
|
||||
}, []);
|
||||
return (
|
||||
<RenderOverlay>
|
||||
<div className="loading-wrapper">
|
||||
<div className="loading-container">
|
||||
<div className="project-name">Untitled</div>
|
||||
<div className="project-name">{projectName}</div>
|
||||
<div className="loading-hero-container">
|
||||
<div className="logo">
|
||||
<LogoIconLarge />
|
||||
|
|
|
@ -1,28 +1,119 @@
|
|||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import useLayoutStore from "../../store/builder/uselayoutStore";
|
||||
|
||||
import { useDfxUpload } from "../../store/builder/store";
|
||||
import DxfParser from "dxf-parser";
|
||||
import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint";
|
||||
import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree";
|
||||
type DXFData = any
|
||||
const SelectFloorPlan: React.FC = () => {
|
||||
// Access layout state and state setters
|
||||
const { currentLayout, setLayout } = useLayoutStore();
|
||||
// Access DXF-related states and setters
|
||||
const { setDfxUploaded, setDfxGenerate, setObjValue, objValue } = useDfxUpload();
|
||||
|
||||
// Local state to store the parsed DXF file
|
||||
const [parsedFile, setParsedFile] = useState<DXFData | undefined>(undefined);
|
||||
|
||||
// Flag to trigger generation after file upload
|
||||
const [generate, setGenerate] = useState<boolean>(false);
|
||||
|
||||
// Handles file upload and DXF parsing
|
||||
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setLayout(null); // Reset current layout
|
||||
setParsedFile(undefined); // Clear any previously parsed file
|
||||
const file = event.target.files?.[0];
|
||||
if (!file || !file.name.endsWith(".dxf")) {
|
||||
alert("Please upload a valid .dxf file.");
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = async (e) => {
|
||||
const dxfContent = e.target?.result as string;
|
||||
|
||||
try {
|
||||
const parser = new DxfParser();
|
||||
const parsedDatas = parser.parse(dxfContent) as DXFData;
|
||||
const geometries = convertDXFToThree(parsedDatas);
|
||||
setParsedFile(parsedDatas);
|
||||
setObjValue({ x: 0, y: 0, z: 0 });
|
||||
setDfxUploaded(geometries);
|
||||
} catch (error) {
|
||||
console.error("Failed to import your .dxf file", error);
|
||||
} finally {
|
||||
// ✅ Reset input AFTER processing
|
||||
event.target.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsText(file); // Read the uploaded file as text
|
||||
};
|
||||
|
||||
// Trigger wall point generation when `generate` flag changes
|
||||
useEffect(() => {
|
||||
if (parsedFile !== undefined) {
|
||||
getWallPointsFromBlueprint({ parsedData: parsedFile, setDfxGenerate, objValue });
|
||||
}
|
||||
}, [generate]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="select-floorplane-wrapper">
|
||||
Preset Layouts
|
||||
|
||||
<div className="presets-container">
|
||||
<button
|
||||
className={`preset ${currentLayout === "layout1" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setLayout("layout1");
|
||||
setDfxUploaded([]);
|
||||
setGenerate(false);
|
||||
}}
|
||||
>
|
||||
Preset 1
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`preset ${currentLayout === "layout2" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setLayout("layout2");
|
||||
setDfxUploaded([]);
|
||||
setGenerate(false);
|
||||
}}
|
||||
>
|
||||
Preset 2
|
||||
</button>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
id="file-upload"
|
||||
accept=".dxf"
|
||||
style={{ display: "none", width: "10px" }}
|
||||
onChange={handleFileUpload}
|
||||
/>
|
||||
|
||||
<label
|
||||
htmlFor="file-upload"
|
||||
className="preset"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
padding: "10px 48px",
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</label>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
id="generate-upload"
|
||||
onClick={() => {
|
||||
setDfxUploaded([]);
|
||||
setLayout(null);
|
||||
setGenerate(!generate);
|
||||
}}
|
||||
>
|
||||
Generate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,11 +3,18 @@ import RenameInput from "./inputs/RenameInput";
|
|||
import { ArrowIcon } from "../icons/ExportCommonIcons";
|
||||
import MenuBar from "./menu/menu";
|
||||
import { ProjectIcon } from "../icons/HeaderIcons";
|
||||
import { useProjectName, useSocketStore } from "../../store/builder/store";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
import { updateProject } from "../../services/dashboard/updateProject";
|
||||
|
||||
const FileMenu: React.FC = () => {
|
||||
const [openMenu, setOpenMenu] = useState(false);
|
||||
const containerRef = useRef<HTMLButtonElement>(null);
|
||||
let clickTimeout: NodeJS.Timeout | null = null;
|
||||
const { projectName, setProjectName } = useProjectName();
|
||||
const { dashBoardSocket } = useSocketStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const handleClick = () => {
|
||||
if (clickTimeout) return;
|
||||
|
@ -32,21 +39,79 @@ const FileMenu: React.FC = () => {
|
|||
}, []);
|
||||
|
||||
// project
|
||||
const [projectName, setProjectName] = useState("Demo Project");
|
||||
// const [projectName, setProjectName] = useState("Demo Project");
|
||||
|
||||
// Load project name from localStorage on mount
|
||||
useEffect(() => {
|
||||
const savedName = localStorage.getItem("projectName");
|
||||
if (savedName) {
|
||||
setProjectName(savedName);
|
||||
// useEffect(() => {
|
||||
// const savedName = localStorage.getItem("projectName");
|
||||
// if (savedName) {
|
||||
// setProjectName(savedName);
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
// const handleProjectRename = (newName: string) => {
|
||||
// setProjectName(newName);
|
||||
// localStorage.setItem("projectName", newName);
|
||||
// };
|
||||
const handleProjectRename = async (projectName: string) => {
|
||||
setProjectName(projectName);
|
||||
if (!projectId) return
|
||||
// localStorage.setItem("projectName", newName);
|
||||
try {
|
||||
const email = localStorage.getItem("email");
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
if (!email || !userId) {
|
||||
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleProjectRename = (newName: string) => {
|
||||
setProjectName(newName);
|
||||
localStorage.setItem("projectName", newName);
|
||||
const emailParts = email.split("@");
|
||||
if (emailParts.length < 2) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const domainParts = emailParts[1].split(".");
|
||||
const Organization = domainParts[0];
|
||||
const projects = await getAllProjects(
|
||||
userId, Organization
|
||||
);
|
||||
console.log('projects: ', projects);
|
||||
let projectUuid = projects.Projects.find((val: any) => val.projectUuid === projectId || val._id
|
||||
=== projectId)
|
||||
const updateProjects = {
|
||||
projectId: projectUuid,
|
||||
organization: Organization,
|
||||
userId,
|
||||
projectName,
|
||||
thumbnail: undefined
|
||||
}
|
||||
|
||||
// if (dashBoardSocket) {
|
||||
// const handleResponse = (data: any) => {
|
||||
// console.log('Project update response:', data);
|
||||
// dashBoardSocket.off("v1-project:response:update", handleResponse); // Clean up
|
||||
// };
|
||||
|
||||
// dashBoardSocket.on("v1-project:response:update", handleResponse);
|
||||
|
||||
// dashBoardSocket.emit("v1:project:update", updateProjects);
|
||||
// }
|
||||
|
||||
//API for projects rename
|
||||
const updatedProjectName = await updateProject(
|
||||
projectId,
|
||||
userId,
|
||||
Organization,
|
||||
undefined,
|
||||
projectName
|
||||
);
|
||||
//
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
id="project-dropdown-button"
|
||||
|
|
|
@ -37,6 +37,7 @@ import {
|
|||
use3DWidget,
|
||||
useFloatingWidget,
|
||||
} from "../../store/visualization/useDroppedObjectsStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
// Utility component
|
||||
const ToolButton = ({
|
||||
|
@ -89,6 +90,7 @@ const Tools: React.FC = () => {
|
|||
|
||||
const dropdownRef = useRef<HTMLButtonElement>(null);
|
||||
const [openDrop, setOpenDrop] = useState(false);
|
||||
const { projectId } = useParams();
|
||||
|
||||
// 1. Set UI toggles on initial render
|
||||
useEffect(() => {
|
||||
|
@ -257,6 +259,7 @@ const Tools: React.FC = () => {
|
|||
selectedZone,
|
||||
templates,
|
||||
visualizationSocket,
|
||||
projectId
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -12,7 +12,7 @@ import Scene from "../../../modules/scene/scene";
|
|||
import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
|
||||
|
||||
const CompareLayOut = () => {
|
||||
const { comparisonProduct, setComparisonProduct } = useComparisonProduct();
|
||||
const { comparisonProduct, setComparisonProduct, clearComparisonProduct } = useComparisonProduct();
|
||||
const { products } = useProductStore();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const [width, setWidth] = useState("50vw");
|
||||
|
@ -63,6 +63,7 @@ const CompareLayOut = () => {
|
|||
if (finalWidthVw <= 10) {
|
||||
setWidth("0px");
|
||||
setIsVersionSaved(false);
|
||||
clearComparisonProduct();
|
||||
} else {
|
||||
setWidth(`${finalWidthVw}vw`);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
useLeftData,
|
||||
useTopData,
|
||||
} from "../../../store/visualization/useZone3DWidgetStore";
|
||||
import { useRenameModeStore } from "../../../store/builder/store";
|
||||
|
||||
type RenameTooltipProps = {
|
||||
name: string;
|
||||
|
@ -13,12 +14,14 @@ type RenameTooltipProps = {
|
|||
const RenameTooltip: React.FC<RenameTooltipProps> = ({ name, onSubmit }) => {
|
||||
const [value, setValue] = useState(name);
|
||||
|
||||
const { top } = useTopData();
|
||||
const { left } = useLeftData();
|
||||
const { top, setTop } = useTopData();
|
||||
const { left, setLeft } = useLeftData();
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
onSubmit(value.trim());
|
||||
setTop(0);
|
||||
setLeft(0);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -18,7 +18,7 @@ interface DropDownListProps {
|
|||
}
|
||||
|
||||
interface Zone {
|
||||
zoneId: string;
|
||||
zoneUuid: string;
|
||||
zoneName: string;
|
||||
points: [number, number, number][]; // polygon vertices
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
|
|||
}));
|
||||
|
||||
return {
|
||||
id: zone.zoneId,
|
||||
id: zone.zoneUuid,
|
||||
name: zone.zoneName,
|
||||
assets: assetsInZone,
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
|
||||
import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
||||
import OuterClick from "../../../utils/outerClick";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useAssetsStore } from "../../../store/builder/useAssetStore";
|
||||
|
||||
interface Asset {
|
||||
|
@ -49,6 +50,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
const [expandedZones, setExpandedZones] = useState<Record<string, boolean>>(
|
||||
{}
|
||||
);
|
||||
const { projectId } = useParams();
|
||||
|
||||
const { setName } = useAssetsStore();
|
||||
|
||||
|
@ -58,23 +60,23 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
zoneId: "",
|
||||
zoneUuid: "",
|
||||
zoneViewPortTarget: [],
|
||||
zoneViewPortPosition: [],
|
||||
widgets: [],
|
||||
});
|
||||
}, [activeModule]);
|
||||
|
||||
const toggleZoneExpansion = (zoneId: string) => {
|
||||
const toggleZoneExpansion = (zoneUuid: string) => {
|
||||
setExpandedZones((prev) => ({
|
||||
...prev,
|
||||
[zoneId]: !prev[zoneId],
|
||||
[zoneUuid]: !prev[zoneUuid],
|
||||
}));
|
||||
};
|
||||
|
||||
async function handleSelectZone(id: string) {
|
||||
try {
|
||||
if (selectedZone?.zoneId === id) {
|
||||
if (selectedZone?.zoneUuid === id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,14 +85,14 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
const email = localStorage.getItem("email");
|
||||
const organization = email?.split("@")[1]?.split(".")[0] ?? "";
|
||||
|
||||
let response = await getZoneData(id, organization);
|
||||
let response = await getZoneData(id, organization, projectId);
|
||||
setSelectedZone({
|
||||
zoneName: response?.zoneName,
|
||||
activeSides: response?.activeSides ?? [],
|
||||
panelOrder: response?.panelOrder ?? [],
|
||||
lockedPanels: response?.lockedPanels ?? [],
|
||||
widgets: response?.widgets ?? [],
|
||||
zoneId: response?.zoneId,
|
||||
zoneUuid: response?.zoneUuid,
|
||||
zoneViewPortTarget: response?.viewPortCenter ?? [],
|
||||
zoneViewPortPosition: response?.viewPortposition ?? [],
|
||||
});
|
||||
|
@ -110,7 +112,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
const isDuplicate = zones.some(
|
||||
(zone: any) =>
|
||||
zone.zoneName.trim().toLowerCase() === newName.trim().toLowerCase() &&
|
||||
zone.zoneId !== selectedZone.zoneId
|
||||
zone.zoneUuid !== selectedZone.zoneUuid
|
||||
);
|
||||
|
||||
if (isDuplicate) {
|
||||
|
@ -119,7 +121,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
}
|
||||
|
||||
const zonesdata = {
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneUuid: selectedZone.zoneUuid,
|
||||
zoneName: newName,
|
||||
};
|
||||
|
||||
|
@ -129,7 +131,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
|
||||
setZones((prevZones: any[]) =>
|
||||
prevZones.map((zone) =>
|
||||
zone.zoneId === selectedZone.zoneId
|
||||
zone.zoneUuid === selectedZone.zoneUuid
|
||||
? { ...zone, zoneName: newName }
|
||||
: zone
|
||||
)
|
||||
|
@ -156,7 +158,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
return zones.some(
|
||||
(zone: any) =>
|
||||
zone.zoneName.trim().toLowerCase() === name.trim().toLowerCase() &&
|
||||
zone.zoneId !== selectedZone.zoneId
|
||||
zone.zoneUuid !== selectedZone.zoneUuid
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -194,7 +196,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
if (isOutsideClick(evt.target)) {
|
||||
// Clear selected zone
|
||||
setSelectedZone({
|
||||
zoneId: '',
|
||||
zoneUuid: '',
|
||||
zoneName: '',
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
|
@ -236,7 +238,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
toggleZoneExpansion(item.id);
|
||||
}}
|
||||
>
|
||||
<div className={`list-item ${selectedZone.zoneId === item.id ? "active" : ""}`}>
|
||||
<div className={`list-item ${selectedZone.zoneUuid === item.id ? "active" : ""}`}>
|
||||
<div className="zone-header">
|
||||
<button className="value" id="zone-name">
|
||||
<RenameInput
|
||||
|
|
|
@ -7,6 +7,7 @@ import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
|
|||
|
||||
async function loadInitialWallItems(
|
||||
setWallItems: Types.setWallItemSetState,
|
||||
projectId?:string
|
||||
): Promise<void> {
|
||||
try {
|
||||
const email = localStorage.getItem('email');
|
||||
|
@ -15,7 +16,7 @@ async function loadInitialWallItems(
|
|||
}
|
||||
|
||||
const organization = email.split("@")[1].split(".")[0];
|
||||
const items = await getWallItems(organization);
|
||||
const items = await getWallItems(organization,projectId);
|
||||
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ function AisleInstances() {
|
|||
|
||||
aisles.forEach(aisle => {
|
||||
aisle.points.forEach(point => {
|
||||
if (!seenUuids.has(point.uuid)) {
|
||||
seenUuids.add(point.uuid);
|
||||
if (!seenUuids.has(point.pointUuid)) {
|
||||
seenUuids.add(point.pointUuid);
|
||||
points.push(point);
|
||||
}
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ function AisleInstances() {
|
|||
}, [aisles]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('aisles: ', aisles);
|
||||
// console.log('aisles: ', aisles);
|
||||
}, [aisles]);
|
||||
|
||||
return (
|
||||
|
@ -35,7 +35,7 @@ function AisleInstances() {
|
|||
{toggleView &&
|
||||
<group name='Aisle-Points-Group'>
|
||||
{allPoints.map((point) => (
|
||||
<Point key={point.uuid} point={point} />
|
||||
<Point key={point.pointUuid} point={point} />
|
||||
))}
|
||||
</group>
|
||||
}
|
||||
|
@ -46,13 +46,13 @@ function AisleInstances() {
|
|||
const distance = new Vector3(...aisle.points[0].position).distanceTo(new Vector3(...aisle.points[1].position));
|
||||
|
||||
return (
|
||||
< React.Fragment key={aisle.uuid}>
|
||||
<AisleInstance aisle={aisle} key={aisle.uuid} />
|
||||
< React.Fragment key={aisle.aisleUuid}>
|
||||
<AisleInstance aisle={aisle} key={aisle.aisleUuid} />
|
||||
|
||||
{toggleView &&
|
||||
<Html
|
||||
// data
|
||||
key={`${aisle.points[0].uuid}_${aisle.points[1].uuid}`}
|
||||
key={`${aisle.points[0].pointUuid}_${aisle.points[1].pointUuid}`}
|
||||
userData={aisle}
|
||||
position={[textPosition.x, 1, textPosition.z]}
|
||||
// class
|
||||
|
@ -64,8 +64,8 @@ function AisleInstances() {
|
|||
sprite
|
||||
>
|
||||
<div
|
||||
key={aisle.uuid}
|
||||
className={`distance ${aisle.uuid}`}
|
||||
key={aisle.aisleUuid}
|
||||
className={`distance ${aisle.aisleUuid}`}
|
||||
>
|
||||
{distance.toFixed(2)} m
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import ArcAisle from './aisleTypes/arcAisle';
|
||||
import ArrowAisle from './aisleTypes/arrowAisle';
|
||||
import ArrowsAisle from './aisleTypes/arrowsAisle';
|
||||
import CircleAisle from './aisleTypes/circleAisle';
|
||||
import DashedAisle from './aisleTypes/dashedAisle';
|
||||
import DottedAisle from './aisleTypes/dottedAisle';
|
||||
import JunctionAisle from './aisleTypes/junctionAisle';
|
||||
import SolidAisle from './aisleTypes/solidAisle';
|
||||
|
||||
function AisleInstance({ aisle }: { readonly aisle: Aisle }) {
|
||||
|
@ -22,6 +26,22 @@ function AisleInstance({ aisle }: { readonly aisle: Aisle }) {
|
|||
{aisle.type.aisleType === 'arrows-aisle' && (
|
||||
<ArrowsAisle aisle={aisle} />
|
||||
)}
|
||||
|
||||
{aisle.type.aisleType === 'arrow-aisle' && (
|
||||
<ArrowAisle aisle={aisle} />
|
||||
)}
|
||||
|
||||
{aisle.type.aisleType === 'circle-aisle' && (
|
||||
<CircleAisle aisle={aisle} />
|
||||
)}
|
||||
|
||||
{aisle.type.aisleType === 'junction-aisle' && (
|
||||
<JunctionAisle aisle={aisle} />
|
||||
)}
|
||||
|
||||
{aisle.type.aisleType === 'arc-aisle' && (
|
||||
<ArcAisle aisle={aisle} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||
|
||||
function ArcAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const arc = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arc-aisle') return null;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.5;
|
||||
const isFlipped = aisle.type.isFlipped || false;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start);
|
||||
const length = direction.length();
|
||||
direction.normalize();
|
||||
|
||||
const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||
if (!isFlipped) perpendicular.negate();
|
||||
|
||||
const arcHeight = length * 0.25;
|
||||
const midPoint = new THREE.Vector3().lerpVectors(start, end, 0.5);
|
||||
const controlPoint = new THREE.Vector3().copy(midPoint).addScaledVector(perpendicular, arcHeight);
|
||||
|
||||
const widthOffset = perpendicular.clone().multiplyScalar(width / 2);
|
||||
|
||||
const p1 = new THREE.Vector3().copy(start).add(widthOffset);
|
||||
const p2 = new THREE.Vector3().copy(end).add(widthOffset);
|
||||
const p3 = new THREE.Vector3().copy(end).sub(widthOffset);
|
||||
const p4 = new THREE.Vector3().copy(start).sub(widthOffset);
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.moveTo(p1.x, p1.z);
|
||||
|
||||
shape.quadraticCurveTo(
|
||||
controlPoint.x + widthOffset.x,
|
||||
controlPoint.z + widthOffset.z,
|
||||
p2.x, p2.z
|
||||
);
|
||||
|
||||
shape.lineTo(p3.x, p3.z);
|
||||
|
||||
shape.quadraticCurveTo(
|
||||
controlPoint.x - widthOffset.x,
|
||||
controlPoint.z - widthOffset.z,
|
||||
p4.x, p4.z
|
||||
);
|
||||
|
||||
shape.lineTo(p1.x, p1.z);
|
||||
|
||||
return {
|
||||
shape,
|
||||
position: new THREE.Vector3(0, 0, 0),
|
||||
rotationY: 0
|
||||
};
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arc) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
name='Arc-Aisle'
|
||||
ref={aisleRef}
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
userData={aisle}
|
||||
onClick={handleClick}
|
||||
onPointerMissed={() => {
|
||||
setSelectedAisle(null);
|
||||
}}
|
||||
>
|
||||
<Extrude
|
||||
args={[arc.shape, {
|
||||
depth: 0.01,
|
||||
bevelEnabled: false,
|
||||
curveSegments: 32
|
||||
}]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default ArcAisle;
|
|
@ -0,0 +1,88 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||
|
||||
function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const arrow = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrow-aisle') return null;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.1;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start);
|
||||
const length = direction.length();
|
||||
direction.normalize();
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
const arrowHeadLength = width * 2;
|
||||
const shaftLength = length - arrowHeadLength;
|
||||
|
||||
if (shaftLength > 0) {
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(width, -arrowHeadLength);
|
||||
shape.lineTo(width / 2, -arrowHeadLength);
|
||||
shape.lineTo(width / 2, -length);
|
||||
shape.lineTo(-width / 2, -length);
|
||||
shape.lineTo(-width / 2, -arrowHeadLength);
|
||||
shape.lineTo(-width, -arrowHeadLength);
|
||||
shape.lineTo(0, 0);
|
||||
} else {
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(width, -length);
|
||||
shape.lineTo(-width, -length);
|
||||
shape.lineTo(0, 0);
|
||||
}
|
||||
|
||||
shape.closePath();
|
||||
|
||||
const position = end;
|
||||
const angle = Math.atan2(direction.x, direction.z);
|
||||
|
||||
return { shape, position, rotationY: angle };
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arrow) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
name='Arrow-Aisle'
|
||||
ref={aisleRef}
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
userData={aisle}
|
||||
onClick={handleClick}
|
||||
onPointerMissed={() => {
|
||||
setSelectedAisle(null);
|
||||
}}
|
||||
>
|
||||
<group position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
|
||||
<Extrude
|
||||
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default ArrowAisle;
|
|
@ -0,0 +1,82 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||
|
||||
function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const circle = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'circle-aisle') return null;
|
||||
|
||||
const center = new THREE.Vector3(...aisle.points[0].position);
|
||||
const widthCenter = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.1;
|
||||
|
||||
const middleRadius = center.distanceTo(widthCenter);
|
||||
|
||||
const innerRadius = Math.max(0, middleRadius - width / 2);
|
||||
const outerRadius = middleRadius + width / 2;
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);
|
||||
|
||||
if (innerRadius > 0) {
|
||||
const hole = new THREE.Path();
|
||||
hole.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);
|
||||
shape.holes.push(hole);
|
||||
}
|
||||
|
||||
return {
|
||||
shape,
|
||||
position: center,
|
||||
rotationY: 0
|
||||
};
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
if (!circle) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
name='Circle-Aisle'
|
||||
ref={aisleRef}
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
userData={aisle}
|
||||
onClick={handleClick}
|
||||
onPointerMissed={() => {
|
||||
setSelectedAisle(null);
|
||||
}}
|
||||
>
|
||||
<group position={[circle.position.x, circle.position.z, 0]}>
|
||||
<Extrude
|
||||
args={[circle.shape, {
|
||||
depth: 0.01,
|
||||
bevelEnabled: false,
|
||||
steps: 1,
|
||||
curveSegments: 64
|
||||
}]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default CircleAisle;
|
|
@ -0,0 +1,125 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||
|
||||
function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const arrows = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'junction-aisle') return null;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.1;
|
||||
const isFlipped = aisle.type.isFlipped || false;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start);
|
||||
const length = direction.length();
|
||||
direction.normalize();
|
||||
|
||||
const mainShape = new THREE.Shape();
|
||||
const arrowHeadLength = width * 2;
|
||||
const shaftLength = length - arrowHeadLength;
|
||||
|
||||
if (shaftLength > 0) {
|
||||
mainShape.moveTo(0, 0);
|
||||
mainShape.lineTo(width, -arrowHeadLength);
|
||||
mainShape.lineTo(width / 2, -arrowHeadLength);
|
||||
mainShape.lineTo(width / 2, -length);
|
||||
mainShape.lineTo(-width / 2, -length);
|
||||
mainShape.lineTo(-width / 2, -arrowHeadLength);
|
||||
mainShape.lineTo(-width, -arrowHeadLength);
|
||||
mainShape.lineTo(0, 0);
|
||||
} else {
|
||||
mainShape.moveTo(0, 0);
|
||||
mainShape.lineTo(width, -length);
|
||||
mainShape.lineTo(-width, -length);
|
||||
mainShape.lineTo(0, 0);
|
||||
}
|
||||
mainShape.closePath();
|
||||
|
||||
const secondaryLength = length / 4;
|
||||
const secondaryShape = new THREE.Shape();
|
||||
const secondaryHeadLength = width * 2;
|
||||
const secondaryShaftLength = secondaryLength - secondaryHeadLength;
|
||||
|
||||
if (secondaryShaftLength > 0) {
|
||||
secondaryShape.moveTo(0, 0);
|
||||
secondaryShape.lineTo(width / 2, 0);
|
||||
secondaryShape.lineTo(width / 2, secondaryShaftLength);
|
||||
secondaryShape.lineTo(width, secondaryShaftLength);
|
||||
secondaryShape.lineTo(0, secondaryLength);
|
||||
secondaryShape.lineTo(-width, secondaryShaftLength);
|
||||
secondaryShape.lineTo(-width / 2, secondaryShaftLength);
|
||||
secondaryShape.lineTo(-width / 2, 0);
|
||||
secondaryShape.lineTo(0, 0);
|
||||
} else {
|
||||
secondaryShape.moveTo(0, 0);
|
||||
secondaryShape.lineTo(width, 0);
|
||||
secondaryShape.lineTo(0, secondaryLength);
|
||||
secondaryShape.lineTo(-width, 0);
|
||||
secondaryShape.lineTo(0, 0);
|
||||
}
|
||||
secondaryShape.closePath();
|
||||
|
||||
const mainPosition = end;
|
||||
const mainAngle = Math.atan2(direction.x, direction.z);
|
||||
|
||||
const perpendicularDirection = isFlipped
|
||||
? new THREE.Vector3(direction.z, 0, -direction.x).normalize()
|
||||
: new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||
|
||||
const secondaryAngle = Math.atan2(perpendicularDirection.x, perpendicularDirection.z);
|
||||
|
||||
const secondaryPosition = new THREE.Vector3().lerpVectors(start, end, 0.75);
|
||||
|
||||
return [
|
||||
{ shape: mainShape, position: mainPosition, rotationY: mainAngle },
|
||||
{ shape: secondaryShape, position: secondaryPosition, rotationY: secondaryAngle + Math.PI }
|
||||
];
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arrows) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
name='Junction-Aisle'
|
||||
ref={aisleRef}
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
userData={aisle}
|
||||
onClick={handleClick}
|
||||
onPointerMissed={() => {
|
||||
setSelectedAisle(null);
|
||||
}}
|
||||
>
|
||||
{arrows.map((arrow, index) => (
|
||||
<group key={index} position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
|
||||
<Extrude
|
||||
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
))}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default JunctionAisle;
|
|
@ -9,7 +9,6 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const shape = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import * as THREE from 'three'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useAisleStore } from '../../../../store/builder/useAisleStore';
|
||||
import ReferenceAisle from './referenceAisle';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import ReferencePoint from '../../point/referencePoint';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
|
||||
function AisleCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
|
@ -15,45 +15,37 @@ function AisleCreator() {
|
|||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { addAisle, getAislePointById } = useAisleStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
|
||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint } = useBuilderStore();
|
||||
|
||||
// useEffect(() => {
|
||||
// if (tempPoints.length > 0) {
|
||||
// setTempPoints([]);
|
||||
// setIsCreating(false);
|
||||
// }
|
||||
// }, [aisleType]);
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, snappedPosition, snappedPoint } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
isLeftMouseDown.current = true;
|
||||
drag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
isLeftMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
drag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseClick = () => {
|
||||
if (drag || !toggleView) return;
|
||||
if (drag.current || !toggleView) return;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
|
@ -63,14 +55,14 @@ function AisleCreator() {
|
|||
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point');
|
||||
|
||||
const newPoint: Point = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
pointUuid: THREE.MathUtils.generateUUID(),
|
||||
pointType: 'Aisle',
|
||||
position: [position.x, position.y, position.z],
|
||||
layer: activeLayer
|
||||
};
|
||||
|
||||
if (snappedPosition && snappedPoint) {
|
||||
newPoint.uuid = snappedPoint.uuid;
|
||||
newPoint.pointUuid = snappedPoint.pointUuid;
|
||||
newPoint.position = snappedPosition;
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
@ -82,7 +74,7 @@ function AisleCreator() {
|
|||
if (intersects && !snappedPoint) {
|
||||
const point = getAislePointById(intersects.object.uuid);
|
||||
if (point) {
|
||||
newPoint.uuid = point.uuid;
|
||||
newPoint.pointUuid = point.pointUuid;
|
||||
newPoint.position = point.position;
|
||||
newPoint.layer = point.layer;
|
||||
}
|
||||
|
@ -95,7 +87,7 @@ function AisleCreator() {
|
|||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'solid-aisle',
|
||||
|
@ -113,7 +105,7 @@ function AisleCreator() {
|
|||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'dashed-aisle',
|
||||
|
@ -126,24 +118,6 @@ function AisleCreator() {
|
|||
addAisle(aisle);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'stripped-aisle') {
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'stripped-aisle',
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth
|
||||
}
|
||||
};
|
||||
addAisle(aisle);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'dotted-aisle') {
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
|
@ -151,7 +125,7 @@ function AisleCreator() {
|
|||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'dotted-aisle',
|
||||
|
@ -164,7 +138,23 @@ function AisleCreator() {
|
|||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'arrow-aisle') {
|
||||
console.log('Creating arrow-aisle');
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'arrow-aisle',
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth
|
||||
}
|
||||
};
|
||||
addAisle(aisle);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'arrows-aisle') {
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
|
@ -172,7 +162,7 @@ function AisleCreator() {
|
|||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'arrows-aisle',
|
||||
|
@ -186,11 +176,61 @@ function AisleCreator() {
|
|||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'arc-aisle') {
|
||||
console.log('Creating arc-aisle');
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'arc-aisle',
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
isFlipped: isFlipped
|
||||
}
|
||||
};
|
||||
addAisle(aisle);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'circle-aisle') {
|
||||
console.log('Creating circle-aisle');
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'circle-aisle',
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth
|
||||
}
|
||||
};
|
||||
addAisle(aisle);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'junction-aisle') {
|
||||
console.log('Creating junction-aisle');
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'junction-aisle',
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
isFlipped: isFlipped
|
||||
}
|
||||
};
|
||||
addAisle(aisle);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -209,9 +249,16 @@ function AisleCreator() {
|
|||
canvasElement.addEventListener("click", onMouseClick);
|
||||
canvasElement.addEventListener("contextmenu", onContext);
|
||||
} else {
|
||||
if (tempPoints.length > 0 || isCreating) {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
|
@ -220,7 +267,7 @@ function AisleCreator() {
|
|||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint]);
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, getAislePointById, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -228,7 +275,7 @@ function AisleCreator() {
|
|||
<>
|
||||
<group name='Aisle-Reference-Points-Group'>
|
||||
{tempPoints.map((point) => (
|
||||
<ReferencePoint key={point.uuid} point={point} />
|
||||
<ReferencePoint key={point.pointUuid} point={point} />
|
||||
))}
|
||||
</group>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ interface ReferenceAisleProps {
|
|||
}
|
||||
|
||||
function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setSnappedPosition, setSnappedPoint } = useBuilderStore();
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setSnappedPosition, setSnappedPoint } = useBuilderStore();
|
||||
const { pointer, raycaster, camera } = useThree();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
|
@ -24,11 +24,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
const [tempAisle, setTempAisle] = useState<Aisle | null>(null);
|
||||
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
|
||||
|
||||
// Calculate directional snap based on current and previous points
|
||||
const directionalSnap = useDirectionalSnapping(
|
||||
currentPosition,
|
||||
tempPoints[0]?.position || null
|
||||
);
|
||||
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null);
|
||||
const { checkSnapForAisle } = usePointSnapping({ uuid: 'temp-aisle', pointType: 'Aisle', position: directionalSnap.position || [0, 0, 0] });
|
||||
|
||||
useFrame(() => {
|
||||
|
@ -40,7 +36,6 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (intersectionPoint) {
|
||||
|
||||
const snapped = checkSnapForAisle([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
|
@ -59,13 +54,13 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
|
||||
if (!finalPosition.current) return;
|
||||
|
||||
if (aisleType === 'solid-aisle' || aisleType === 'stripped-aisle') {
|
||||
if (aisleType === 'solid-aisle') {
|
||||
setTempAisle({
|
||||
uuid: 'temp-aisle',
|
||||
aisleUuid: 'temp-aisle',
|
||||
points: [
|
||||
tempPoints[0],
|
||||
{
|
||||
uuid: 'temp-point',
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Aisle',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer
|
||||
|
@ -79,11 +74,11 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
});
|
||||
} else if (aisleType === 'dashed-aisle') {
|
||||
setTempAisle({
|
||||
uuid: 'temp-aisle',
|
||||
aisleUuid: 'temp-aisle',
|
||||
points: [
|
||||
tempPoints[0],
|
||||
{
|
||||
uuid: 'temp-point',
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Aisle',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer
|
||||
|
@ -99,11 +94,11 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
});
|
||||
} else if (aisleType === 'dotted-aisle') {
|
||||
setTempAisle({
|
||||
uuid: 'temp-aisle',
|
||||
aisleUuid: 'temp-aisle',
|
||||
points: [
|
||||
tempPoints[0],
|
||||
{
|
||||
uuid: 'temp-point',
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Aisle',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer
|
||||
|
@ -116,15 +111,31 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
gapLength: gapLength
|
||||
}
|
||||
});
|
||||
} else if (aisleType === 'arrow-aisle') {
|
||||
console.log();
|
||||
} else if (aisleType === 'arrows-aisle') {
|
||||
} else if (aisleType === 'arrow-aisle' || aisleType === 'circle-aisle') {
|
||||
setTempAisle({
|
||||
uuid: 'temp-aisle',
|
||||
aisleUuid: 'temp-aisle',
|
||||
points: [
|
||||
tempPoints[0],
|
||||
{
|
||||
uuid: 'temp-point',
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Aisle',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer
|
||||
}
|
||||
],
|
||||
type: {
|
||||
aisleType: aisleType,
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
}
|
||||
});
|
||||
} else if (aisleType === 'arrows-aisle') {
|
||||
setTempAisle({
|
||||
aisleUuid: 'temp-aisle',
|
||||
points: [
|
||||
tempPoints[0],
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Aisle',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer
|
||||
|
@ -138,12 +149,25 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
gapLength: gapLength
|
||||
}
|
||||
});
|
||||
} else if (aisleType === 'arc-aisle') {
|
||||
console.log();
|
||||
} else if (aisleType === 'circle-aisle') {
|
||||
console.log();
|
||||
} else if (aisleType === 'junction-aisle') {
|
||||
console.log();
|
||||
} else if (aisleType === 'junction-aisle' || aisleType === 'arc-aisle') {
|
||||
setTempAisle({
|
||||
aisleUuid: 'temp-aisle',
|
||||
points: [
|
||||
tempPoints[0],
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Aisle',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer
|
||||
}
|
||||
],
|
||||
type: {
|
||||
aisleType: aisleType,
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
isFlipped: isFlipped,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (tempAisle !== null) {
|
||||
|
@ -165,8 +189,16 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
return <DashedAisle aisle={tempAisle} />;
|
||||
case 'dotted-aisle':
|
||||
return <DottedAisle aisle={tempAisle} />;
|
||||
case 'arrow-aisle':
|
||||
return <ArrowAisle aisle={tempAisle} />;
|
||||
case 'arrows-aisle':
|
||||
return <ArrowsAisle aisle={tempAisle} />
|
||||
return <ArrowsAisle aisle={tempAisle} />;
|
||||
case 'circle-aisle':
|
||||
return <CircleAisle aisle={tempAisle} />;
|
||||
case 'junction-aisle':
|
||||
return <JunctionAisle aisle={tempAisle} />;
|
||||
case 'arc-aisle':
|
||||
return <ArcAisle aisle={tempAisle} />
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -175,27 +207,23 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
const textPosition = new THREE.Vector3().addVectors(new THREE.Vector3(...tempAisle.points[0].position), new THREE.Vector3(...tempAisle.points[1].position)).divideScalar(2);
|
||||
const distance = new THREE.Vector3(...tempAisle.points[0].position).distanceTo(new THREE.Vector3(...tempAisle.points[1].position));
|
||||
|
||||
|
||||
const rendertext = () => {
|
||||
return (
|
||||
<>
|
||||
{toggleView &&
|
||||
<Html
|
||||
// data
|
||||
key={tempAisle.uuid}
|
||||
key={tempAisle.aisleUuid}
|
||||
userData={tempAisle}
|
||||
position={[textPosition.x, 1, textPosition.z]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div
|
||||
key={tempAisle.uuid}
|
||||
className={`distance ${tempAisle.uuid}`}
|
||||
key={tempAisle.aisleUuid}
|
||||
className={`distance ${tempAisle.aisleUuid}`}
|
||||
>
|
||||
{distance.toFixed(2)} m
|
||||
</div>
|
||||
|
@ -216,6 +244,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
export default ReferenceAisle;
|
||||
|
||||
function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
|
||||
const shape = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null;
|
||||
|
||||
|
@ -253,10 +282,7 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} side={THREE.DoubleSide} />
|
||||
</Extrude>
|
||||
</group>
|
||||
);
|
||||
|
@ -446,3 +472,306 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const arrow = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrow-aisle') return null;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.1;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start);
|
||||
const length = direction.length();
|
||||
direction.normalize();
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
const arrowHeadLength = width * 2;
|
||||
const shaftLength = length - arrowHeadLength;
|
||||
|
||||
if (shaftLength > 0) {
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(width, -arrowHeadLength);
|
||||
shape.lineTo(width / 2, -arrowHeadLength);
|
||||
shape.lineTo(width / 2, -length);
|
||||
shape.lineTo(-width / 2, -length);
|
||||
shape.lineTo(-width / 2, -arrowHeadLength);
|
||||
shape.lineTo(-width, -arrowHeadLength);
|
||||
shape.lineTo(0, 0);
|
||||
} else {
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(width, -length);
|
||||
shape.lineTo(-width, -length);
|
||||
shape.lineTo(0, 0);
|
||||
}
|
||||
|
||||
shape.closePath();
|
||||
|
||||
const position = end;
|
||||
const angle = Math.atan2(direction.x, direction.z);
|
||||
|
||||
return { shape, position, rotationY: angle };
|
||||
}, [aisle]);
|
||||
|
||||
if (!arrow) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
<group position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
|
||||
<Extrude
|
||||
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const circle = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'circle-aisle') return null;
|
||||
|
||||
const center = new THREE.Vector3(...aisle.points[0].position);
|
||||
const widthCenter = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.1;
|
||||
|
||||
const middleRadius = center.distanceTo(widthCenter);
|
||||
|
||||
const innerRadius = Math.max(0, middleRadius - width / 2);
|
||||
const outerRadius = middleRadius + width / 2;
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);
|
||||
|
||||
if (innerRadius > 0) {
|
||||
const hole = new THREE.Path();
|
||||
hole.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);
|
||||
shape.holes.push(hole);
|
||||
}
|
||||
|
||||
return {
|
||||
shape,
|
||||
position: center,
|
||||
rotationY: 0
|
||||
};
|
||||
}, [aisle]);
|
||||
|
||||
if (!circle) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
<group position={[circle.position.x, circle.position.z, 0]}>
|
||||
<Extrude
|
||||
args={[circle.shape, {
|
||||
depth: 0.01,
|
||||
bevelEnabled: false,
|
||||
steps: 1,
|
||||
curveSegments: 64
|
||||
}]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const arrows = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'junction-aisle') return null;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.1;
|
||||
const isFlipped = aisle.type.isFlipped || false;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start);
|
||||
const length = direction.length();
|
||||
direction.normalize();
|
||||
|
||||
const mainShape = new THREE.Shape();
|
||||
const arrowHeadLength = width * 2;
|
||||
const shaftLength = length - arrowHeadLength;
|
||||
|
||||
if (shaftLength > 0) {
|
||||
mainShape.moveTo(0, 0);
|
||||
mainShape.lineTo(width, -arrowHeadLength);
|
||||
mainShape.lineTo(width / 2, -arrowHeadLength);
|
||||
mainShape.lineTo(width / 2, -length);
|
||||
mainShape.lineTo(-width / 2, -length);
|
||||
mainShape.lineTo(-width / 2, -arrowHeadLength);
|
||||
mainShape.lineTo(-width, -arrowHeadLength);
|
||||
mainShape.lineTo(0, 0);
|
||||
} else {
|
||||
mainShape.moveTo(0, 0);
|
||||
mainShape.lineTo(width, -length);
|
||||
mainShape.lineTo(-width, -length);
|
||||
mainShape.lineTo(0, 0);
|
||||
}
|
||||
mainShape.closePath();
|
||||
|
||||
const secondaryLength = length / 4;
|
||||
const secondaryShape = new THREE.Shape();
|
||||
const secondaryHeadLength = width * 2;
|
||||
const secondaryShaftLength = secondaryLength - secondaryHeadLength;
|
||||
|
||||
if (secondaryShaftLength > 0) {
|
||||
secondaryShape.moveTo(0, 0);
|
||||
secondaryShape.lineTo(width / 2, 0);
|
||||
secondaryShape.lineTo(width / 2, secondaryShaftLength);
|
||||
secondaryShape.lineTo(width, secondaryShaftLength);
|
||||
secondaryShape.lineTo(0, secondaryLength);
|
||||
secondaryShape.lineTo(-width, secondaryShaftLength);
|
||||
secondaryShape.lineTo(-width / 2, secondaryShaftLength);
|
||||
secondaryShape.lineTo(-width / 2, 0);
|
||||
secondaryShape.lineTo(0, 0);
|
||||
} else {
|
||||
secondaryShape.moveTo(0, 0);
|
||||
secondaryShape.lineTo(width, 0);
|
||||
secondaryShape.lineTo(0, secondaryLength);
|
||||
secondaryShape.lineTo(-width, 0);
|
||||
secondaryShape.lineTo(0, 0);
|
||||
}
|
||||
secondaryShape.closePath();
|
||||
|
||||
const mainPosition = end;
|
||||
const mainAngle = Math.atan2(direction.x, direction.z);
|
||||
|
||||
const perpendicularDirection = isFlipped
|
||||
? new THREE.Vector3(direction.z, 0, -direction.x).normalize()
|
||||
: new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||
|
||||
const secondaryAngle = Math.atan2(perpendicularDirection.x, perpendicularDirection.z);
|
||||
|
||||
const secondaryPosition = new THREE.Vector3().lerpVectors(start, end, 0.75);
|
||||
|
||||
return [
|
||||
{ shape: mainShape, position: mainPosition, rotationY: mainAngle },
|
||||
{
|
||||
shape: secondaryShape,
|
||||
position: secondaryPosition,
|
||||
rotationY: secondaryAngle + Math.PI
|
||||
}
|
||||
];
|
||||
}, [aisle]);
|
||||
|
||||
if (!arrows) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
{arrows.map((arrow, index) => (
|
||||
<group key={index} position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
|
||||
<Extrude
|
||||
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
))}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
function ArcAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const arc = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arc-aisle') return null;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.aisleWidth || 0.5;
|
||||
const isFlipped = aisle.type.isFlipped || false;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start);
|
||||
const length = direction.length();
|
||||
direction.normalize();
|
||||
|
||||
const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||
if (!isFlipped) perpendicular.negate();
|
||||
|
||||
const arcHeight = length * 0.25;
|
||||
const midPoint = new THREE.Vector3().lerpVectors(start, end, 0.5);
|
||||
const controlPoint = new THREE.Vector3().copy(midPoint).addScaledVector(perpendicular, arcHeight);
|
||||
|
||||
const widthOffset = perpendicular.clone().multiplyScalar(width / 2);
|
||||
|
||||
const p1 = new THREE.Vector3().copy(start).add(widthOffset);
|
||||
const p2 = new THREE.Vector3().copy(end).add(widthOffset);
|
||||
const p3 = new THREE.Vector3().copy(end).sub(widthOffset);
|
||||
const p4 = new THREE.Vector3().copy(start).sub(widthOffset);
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.moveTo(p1.x, p1.z);
|
||||
|
||||
shape.quadraticCurveTo(
|
||||
controlPoint.x + widthOffset.x,
|
||||
controlPoint.z + widthOffset.z,
|
||||
p2.x, p2.z
|
||||
);
|
||||
|
||||
shape.lineTo(p3.x, p3.z);
|
||||
|
||||
shape.quadraticCurveTo(
|
||||
controlPoint.x - widthOffset.x,
|
||||
controlPoint.z - widthOffset.z,
|
||||
p4.x, p4.z
|
||||
);
|
||||
|
||||
shape.lineTo(p1.x, p1.z);
|
||||
|
||||
return {
|
||||
shape,
|
||||
position: new THREE.Vector3(0, 0, 0),
|
||||
rotationY: 0
|
||||
};
|
||||
}, [aisle]);
|
||||
|
||||
|
||||
if (!arc) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
<Extrude
|
||||
args={[arc.shape, {
|
||||
depth: 0.01,
|
||||
bevelEnabled: false,
|
||||
curveSegments: 32
|
||||
}]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
);
|
||||
}
|
|
@ -12,6 +12,7 @@ import useModuleStore from "../../../store/useModuleStore";
|
|||
import { useThree } from "@react-three/fiber";
|
||||
import { CameraControls } from "@react-three/drei";
|
||||
import addAssetModel from "./functions/addAssetModel";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const gltfLoaderWorker = new Worker(
|
||||
new URL(
|
||||
|
@ -29,6 +30,10 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
|
|||
const { addEvent } = useEventsStore();
|
||||
const { setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
const { projectId } = useParams();
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
const userId = localStorage.getItem("userId")!;
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
@ -39,8 +44,6 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
|
|||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
let totalAssets = 0;
|
||||
let loadedAssets = 0;
|
||||
|
@ -58,7 +61,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
|
|||
}
|
||||
};
|
||||
|
||||
getFloorAssets(organization).then((data) => {
|
||||
getFloorAssets(organization, projectId).then((data) => {
|
||||
if (data.length > 0) {
|
||||
const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID));
|
||||
totalAssets = uniqueItems.length;
|
||||
|
@ -87,7 +90,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
|
|||
|
||||
if (loadedAssets === totalAssets) {
|
||||
const assets: Asset[] = [];
|
||||
getFloorAssets(organization).then((data: FloorItems) => {
|
||||
getFloorAssets(organization, projectId).then((data: FloorItems) => {
|
||||
data.forEach((item) => {
|
||||
if (item.eventData) {
|
||||
assets.push({
|
||||
|
@ -254,7 +257,6 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
|
|||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
|
||||
|
@ -263,7 +265,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
|
|||
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane);
|
||||
addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, projectId, userId);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||
import * as THREE from "three";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
|
||||
import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils";
|
||||
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
import PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator';
|
||||
import { Socket } from "socket.io-client";
|
||||
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||
import PointsCalculator from "../../../simulation/events/points/functions/pointsCalculator";
|
||||
|
||||
async function addAssetModel(
|
||||
raycaster: THREE.Raycaster,
|
||||
|
@ -19,8 +19,9 @@ async function addAssetModel(
|
|||
addEvent: (event: EventsSchema) => void,
|
||||
addAsset: (asset: Asset) => void,
|
||||
plane: Types.RefMesh,
|
||||
projectId?: string,
|
||||
userId?: string
|
||||
): Promise<void> {
|
||||
|
||||
////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage //////////
|
||||
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
@ -29,12 +30,19 @@ async function addAssetModel(
|
|||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
||||
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
||||
dracoLoader.setDecoderPath(
|
||||
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||
);
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const floorIntersections = raycaster.intersectObjects(floorGroup.current.children, true);
|
||||
const intersectedFloor = floorIntersections.find(intersect => intersect.object.name.includes("Floor"));
|
||||
const floorIntersections = raycaster.intersectObjects(
|
||||
floorGroup.current.children,
|
||||
true
|
||||
);
|
||||
const intersectedFloor = floorIntersections.find((intersect) =>
|
||||
intersect.object.name.includes("Floor")
|
||||
);
|
||||
|
||||
const planeIntersections = raycaster.intersectObject(plane.current!, true);
|
||||
const intersectedPlane = planeIntersections[0];
|
||||
|
@ -42,20 +50,52 @@ async function addAssetModel(
|
|||
let intersectPoint: THREE.Vector3 | null = null;
|
||||
|
||||
if (intersectedFloor && intersectedPlane) {
|
||||
intersectPoint = intersectedFloor.distance < intersectedPlane.distance ? (new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z)) : (new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z));
|
||||
intersectPoint =
|
||||
intersectedFloor.distance < intersectedPlane.distance
|
||||
? new THREE.Vector3(
|
||||
intersectedFloor.point.x,
|
||||
Math.round(intersectedFloor.point.y),
|
||||
intersectedFloor.point.z
|
||||
)
|
||||
: new THREE.Vector3(
|
||||
intersectedPlane.point.x,
|
||||
0,
|
||||
intersectedPlane.point.z
|
||||
);
|
||||
} else if (intersectedFloor) {
|
||||
intersectPoint = new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z);
|
||||
intersectPoint = new THREE.Vector3(
|
||||
intersectedFloor.point.x,
|
||||
Math.round(intersectedFloor.point.y),
|
||||
intersectedFloor.point.z
|
||||
);
|
||||
} else if (intersectedPlane) {
|
||||
intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
|
||||
intersectPoint = new THREE.Vector3(
|
||||
intersectedPlane.point.x,
|
||||
0,
|
||||
intersectedPlane.point.z
|
||||
);
|
||||
}
|
||||
|
||||
if (intersectPoint) {
|
||||
if (intersectPoint.y < 0) {
|
||||
intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z);
|
||||
intersectPoint = new THREE.Vector3(
|
||||
intersectPoint.x,
|
||||
0,
|
||||
intersectPoint.z
|
||||
);
|
||||
}
|
||||
const cachedModel = THREE.Cache.get(selectedItem.id);
|
||||
if (cachedModel) {
|
||||
handleModelLoad(cachedModel, intersectPoint!, selectedItem, addEvent, addAsset, socket);
|
||||
handleModelLoad(
|
||||
cachedModel,
|
||||
intersectPoint!,
|
||||
selectedItem,
|
||||
addEvent,
|
||||
addAsset,
|
||||
socket,
|
||||
projectId,
|
||||
userId
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
const cachedModelBlob = await retrieveGLTF(selectedItem.id);
|
||||
|
@ -65,15 +105,38 @@ async function addAssetModel(
|
|||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(selectedItem.id, gltf);
|
||||
handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket);
|
||||
handleModelLoad(
|
||||
gltf,
|
||||
intersectPoint!,
|
||||
selectedItem,
|
||||
addEvent,
|
||||
addAsset,
|
||||
socket,
|
||||
projectId,
|
||||
userId
|
||||
);
|
||||
});
|
||||
} else {
|
||||
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());
|
||||
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, addEvent, addAsset, socket);
|
||||
})
|
||||
await handleModelLoad(
|
||||
gltf,
|
||||
intersectPoint!,
|
||||
selectedItem,
|
||||
addEvent,
|
||||
addAsset,
|
||||
socket,
|
||||
projectId,
|
||||
userId
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,10 +153,16 @@ async function handleModelLoad(
|
|||
selectedItem: any,
|
||||
addEvent: (event: EventsSchema) => void,
|
||||
addAsset: (asset: Asset) => void,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?: string,
|
||||
userId?: string
|
||||
) {
|
||||
const model = gltf.scene.clone();
|
||||
model.userData = { name: selectedItem.name, modelId: selectedItem.id, modelUuid: model.uuid };
|
||||
model.userData = {
|
||||
name: selectedItem.name,
|
||||
modelId: selectedItem.id,
|
||||
modelUuid: model.uuid,
|
||||
};
|
||||
model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z);
|
||||
model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap);
|
||||
|
||||
|
@ -154,7 +223,7 @@ async function handleModelLoad(
|
|||
position: newFloorItem.position,
|
||||
rotation: newFloorItem.rotation,
|
||||
state: "idle",
|
||||
type: 'transfer',
|
||||
type: "transfer",
|
||||
speed: 1,
|
||||
points: data.points.map((point: THREE.Vector3, index: number) => {
|
||||
const triggers: TriggerSchema[] = [];
|
||||
|
@ -168,17 +237,17 @@ async function handleModelLoad(
|
|||
triggeredAsset: {
|
||||
triggeredModel: {
|
||||
modelName: newFloorItem.modelName,
|
||||
modelUuid: newFloorItem.modelUuid
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
},
|
||||
triggeredPoint: {
|
||||
pointName: `Point`,
|
||||
pointUuid: ""
|
||||
pointUuid: "",
|
||||
},
|
||||
triggeredAction: {
|
||||
actionName: `Action 1`,
|
||||
actionUuid: ""
|
||||
}
|
||||
}
|
||||
actionUuid: "",
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -189,15 +258,15 @@ async function handleModelLoad(
|
|||
action: {
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: `Action 1`,
|
||||
actionType: 'default',
|
||||
material: 'Default Material',
|
||||
actionType: "default",
|
||||
material: "Default Material",
|
||||
delay: 0,
|
||||
spawnInterval: 5,
|
||||
spawnCount: 1,
|
||||
triggers: triggers
|
||||
}
|
||||
triggers: triggers,
|
||||
},
|
||||
};
|
||||
})
|
||||
}),
|
||||
};
|
||||
|
||||
for (let i = 0; i < ConveyorEvent.points.length - 1; i++) {
|
||||
|
@ -205,17 +274,18 @@ async function handleModelLoad(
|
|||
const nextPoint = ConveyorEvent.points[i + 1];
|
||||
|
||||
if (currentPoint.action.triggers.length > 0) {
|
||||
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid;
|
||||
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid;
|
||||
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid =
|
||||
nextPoint.uuid;
|
||||
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid =
|
||||
nextPoint.action.actionUuid;
|
||||
}
|
||||
}
|
||||
addEvent(ConveyorEvent);
|
||||
eventData.points = ConveyorEvent.points.map(point => ({
|
||||
eventData.points = ConveyorEvent.points.map((point) => ({
|
||||
uuid: point.uuid,
|
||||
position: point.position,
|
||||
rotation: point.rotation
|
||||
rotation: point.rotation,
|
||||
}));
|
||||
|
||||
} else if (selectedItem.type === "Vehicle") {
|
||||
const vehicleEvent: VehicleEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
|
@ -238,17 +308,16 @@ async function handleModelLoad(
|
|||
steeringAngle: 0,
|
||||
pickUpPoint: null,
|
||||
unLoadPoint: null,
|
||||
triggers: []
|
||||
}
|
||||
}
|
||||
triggers: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
addEvent(vehicleEvent);
|
||||
eventData.point = {
|
||||
uuid: vehicleEvent.point.uuid,
|
||||
position: vehicleEvent.point.position,
|
||||
rotation: vehicleEvent.point.rotation
|
||||
rotation: vehicleEvent.point.rotation,
|
||||
};
|
||||
|
||||
} else if (selectedItem.type === "ArmBot") {
|
||||
const roboticArmEvent: RoboticArmEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
|
@ -269,20 +338,19 @@ async function handleModelLoad(
|
|||
actionType: "pickAndPlace",
|
||||
process: {
|
||||
startPoint: null,
|
||||
endPoint: null
|
||||
endPoint: null,
|
||||
},
|
||||
triggers: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
triggers: []
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
addEvent(roboticArmEvent);
|
||||
eventData.point = {
|
||||
uuid: roboticArmEvent.point.uuid,
|
||||
position: roboticArmEvent.point.position,
|
||||
rotation: roboticArmEvent.point.rotation
|
||||
rotation: roboticArmEvent.point.rotation,
|
||||
};
|
||||
|
||||
} else if (selectedItem.type === "StaticMachine") {
|
||||
const machineEvent: MachineEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
|
@ -301,15 +369,15 @@ async function handleModelLoad(
|
|||
actionType: "process",
|
||||
processTime: 10,
|
||||
swapMaterial: "Default Material",
|
||||
triggers: []
|
||||
}
|
||||
}
|
||||
triggers: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
addEvent(machineEvent);
|
||||
eventData.point = {
|
||||
uuid: machineEvent.point.uuid,
|
||||
position: machineEvent.point.position,
|
||||
rotation: machineEvent.point.rotation
|
||||
rotation: machineEvent.point.rotation,
|
||||
};
|
||||
} else if (selectedItem.type === "Storage") {
|
||||
const storageEvent: StorageEventSchema = {
|
||||
|
@ -328,15 +396,15 @@ async function handleModelLoad(
|
|||
actionName: "Action 1",
|
||||
actionType: "store",
|
||||
storageCapacity: 10,
|
||||
triggers: []
|
||||
}
|
||||
}
|
||||
}
|
||||
triggers: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
addEvent(storageEvent);
|
||||
eventData.point = {
|
||||
uuid: storageEvent.point.uuid,
|
||||
position: storageEvent.point.position,
|
||||
rotation: storageEvent.point.rotation
|
||||
rotation: storageEvent.point.rotation,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -346,60 +414,78 @@ async function handleModelLoad(
|
|||
modelName: newFloorItem.modelName,
|
||||
modelfileID: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
rotation: {
|
||||
x: model.rotation.x,
|
||||
y: model.rotation.y,
|
||||
z: model.rotation.z,
|
||||
},
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
eventData: eventData
|
||||
eventData: eventData,
|
||||
projectId: projectId,
|
||||
userId: userId,
|
||||
};
|
||||
|
||||
socket.emit("v2:model-asset:add", completeData);
|
||||
console.log('completeData: ', completeData);
|
||||
socket.emit("v1:model-asset:add", completeData);
|
||||
|
||||
const asset: Asset = {
|
||||
modelUuid: completeData.modelUuid,
|
||||
modelName: completeData.modelName,
|
||||
assetId: completeData.modelfileID,
|
||||
position: completeData.position,
|
||||
rotation: [completeData.rotation.x, completeData.rotation.y, completeData.rotation.z] as [number, number, number],
|
||||
rotation: [
|
||||
completeData.rotation.x,
|
||||
completeData.rotation.y,
|
||||
completeData.rotation.z,
|
||||
] as [number, number, number],
|
||||
isLocked: completeData.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: completeData.isVisible,
|
||||
opacity: 1,
|
||||
eventData: completeData.eventData
|
||||
}
|
||||
eventData: completeData.eventData,
|
||||
};
|
||||
|
||||
addAsset(asset);
|
||||
|
||||
} else {
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
modelfileID: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
rotation: {
|
||||
x: model.rotation.x,
|
||||
y: model.rotation.y,
|
||||
z: model.rotation.z,
|
||||
},
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId: projectId,
|
||||
userId: userId,
|
||||
};
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
const asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.modelfileID,
|
||||
position: data.position,
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [number, number, number],
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [
|
||||
number,
|
||||
number,
|
||||
number
|
||||
],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1
|
||||
}
|
||||
opacity: 1,
|
||||
};
|
||||
|
||||
addAsset(asset);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import useModuleStore, { useSubModuleStore } from '../../../../../store/useModul
|
|||
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
|
||||
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
|
||||
import { useProductContext } from '../../../../simulation/products/productContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
function Model({ asset }: { readonly asset: Asset }) {
|
||||
const { camera, controls, gl } = useThree();
|
||||
|
@ -38,6 +39,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||
const [gltfScene, setGltfScene] = useState<GLTF | null>(null);
|
||||
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
const loader = new GLTFLoader();
|
||||
|
@ -157,6 +159,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||
if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -168,10 +171,12 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||
organization: organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
modelName: asset.modelName,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
userId,
|
||||
projectId
|
||||
}
|
||||
|
||||
const response = socket.emit('v2:model-asset:delete', data)
|
||||
const response = socket.emit('v1:model-asset:delete', data)
|
||||
|
||||
useEventsStore.getState().removeEvent(asset.modelUuid);
|
||||
useProductStore.getState().deleteEvent(asset.modelUuid);
|
||||
|
|
|
@ -35,7 +35,6 @@ import * as Types from "../../types/world/worldTypes";
|
|||
import SocketResponses from "../collaboration/socket/socketResponses.dev";
|
||||
import FloorPlanGroup from "./groups/floorPlanGroup";
|
||||
import FloorGroup from "./groups/floorGroup";
|
||||
import FloorGroupAilse from "./groups/floorGroupAisle";
|
||||
import Draw from "./functions/draw";
|
||||
import WallsAndWallItems from "./groups/wallsAndWallItems";
|
||||
import Ground from "../scene/environment/ground";
|
||||
|
@ -48,7 +47,10 @@ import CalculateAreaGroup from "./groups/calculateAreaGroup";
|
|||
import LayoutImage from "./layout/layoutImage";
|
||||
import AssetsGroup from "./asset/assetsGroup";
|
||||
import { Bvh } from "@react-three/drei";
|
||||
import DxfFile from "./dfx/LoadBlueprint";
|
||||
import { useParams } from "react-router-dom";
|
||||
import AislesGroup from "./aisle/aislesGroup";
|
||||
import WallGroup from "./wall/wallGroup";
|
||||
|
||||
export default function Builder() {
|
||||
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
|
||||
|
@ -114,6 +116,7 @@ export default function Builder() {
|
|||
const { setUpdateScene } = useUpdateScene();
|
||||
const { setWalls } = useWalls();
|
||||
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
|
||||
const { projectId } = useParams();
|
||||
|
||||
// const loader = new GLTFLoader();
|
||||
// const dracoLoader = new DRACOLoader();
|
||||
|
@ -153,16 +156,13 @@ export default function Builder() {
|
|||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
async function fetchVisibility() {
|
||||
const visibility = await findEnvironment(
|
||||
organization,
|
||||
localStorage.getItem("userId")!
|
||||
);
|
||||
if (visibility) {
|
||||
setRoofVisibility(visibility.roofVisibility);
|
||||
setWallVisibility(visibility.wallVisibility);
|
||||
setShadows(visibility.shadowVisibility);
|
||||
setRenderDistance(visibility.renderDistance);
|
||||
setLimitDistance(visibility.limitDistance);
|
||||
const data = await findEnvironment(organization, localStorage.getItem("userId")!, projectId);
|
||||
if (data) {
|
||||
setRoofVisibility(data.roofVisibility);
|
||||
setWallVisibility(data.wallVisibility);
|
||||
setShadows(data.shadowVisibility);
|
||||
setRenderDistance(data.renderDistance);
|
||||
setLimitDistance(data.limitDistance);
|
||||
}
|
||||
}
|
||||
fetchVisibility();
|
||||
|
@ -275,39 +275,28 @@ export default function Builder() {
|
|||
|
||||
<ZoneGroup />
|
||||
|
||||
{/* <FloorGroupAilse
|
||||
floorGroupAisle={floorGroupAisle}
|
||||
plane={plane}
|
||||
floorPlanGroupLine={floorPlanGroupLine}
|
||||
floorPlanGroupPoint={floorPlanGroupPoint}
|
||||
line={line}
|
||||
lines={lines}
|
||||
currentLayerPoint={currentLayerPoint}
|
||||
dragPointControls={dragPointControls}
|
||||
floorPlanGroup={floorPlanGroup}
|
||||
ReferenceLineMesh={ReferenceLineMesh}
|
||||
LineCreated={LineCreated}
|
||||
isSnapped={isSnapped}
|
||||
ispreSnapped={ispreSnapped}
|
||||
snappedPoint={snappedPoint}
|
||||
isSnappedUUID={isSnappedUUID}
|
||||
isAngleSnapped={isAngleSnapped}
|
||||
anglesnappedPoint={anglesnappedPoint}
|
||||
/> */}
|
||||
|
||||
<AssetsGroup
|
||||
floorGroup={floorGroup}
|
||||
plane={plane}
|
||||
/>
|
||||
|
||||
{/* <WallGroup /> */}
|
||||
|
||||
<AislesGroup />
|
||||
|
||||
<MeasurementTool />
|
||||
|
||||
<CalculateAreaGroup />
|
||||
|
||||
<NavMesh lines={lines} />
|
||||
|
||||
<DxfFile
|
||||
lines={lines}
|
||||
dragPointControls={dragPointControls}
|
||||
currentLayerPoint={currentLayerPoint}
|
||||
floorPlanGroupLine={floorPlanGroupLine}
|
||||
floorPlanGroupPoint={floorPlanGroupPoint}
|
||||
/>
|
||||
|
||||
<LayoutImage />
|
||||
</Bvh>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
import { useEffect, useRef } from 'react';
|
||||
import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
|
||||
import { LineBasicMaterial, Line } from 'three';
|
||||
import loadInitialPoint from '../IntialLoad/loadInitialPoint';
|
||||
import loadInitialLine from '../IntialLoad/loadInitialLine';
|
||||
import { TransformControls } from '@react-three/drei';
|
||||
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
// Interface defining the props for the DxfFile component
|
||||
interface DxfFileProps {
|
||||
lines: Types.RefLines; // Reference to lines in the DXF file
|
||||
floorPlanGroupPoint: Types.RefGroup; // Reference to floor plan points group
|
||||
dragPointControls: Types.RefDragControl; // Reference to drag controls
|
||||
floorPlanGroupLine: Types.RefGroup; // Reference to floor plan lines group
|
||||
currentLayerPoint: Types.RefMeshArray; // Reference to current layer points
|
||||
}
|
||||
|
||||
/**
|
||||
* DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene.
|
||||
* It processes the DXF data to create points and lines representing walls and allows
|
||||
* transformation controls for interactive editing.
|
||||
*/
|
||||
const DxfFile = ({
|
||||
floorPlanGroupPoint,
|
||||
currentLayerPoint,
|
||||
dragPointControls,
|
||||
floorPlanGroupLine,
|
||||
lines,
|
||||
}: DxfFileProps) => {
|
||||
// State management hooks
|
||||
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload();
|
||||
const { setUpdateScene } = useUpdateScene();
|
||||
const { toggleView } = useToggleView();
|
||||
const { socket } = useSocketStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
// Refs for storing line objects
|
||||
const lineRefs = useRef<Line[]>([]);
|
||||
|
||||
/**
|
||||
* Effect hook that runs when DXF wall generation is triggered.
|
||||
* Loads initial points and lines from the DXF data and updates the scene.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (
|
||||
dfxWallGenerate &&
|
||||
dragPointControls &&
|
||||
floorPlanGroupPoint &&
|
||||
currentLayerPoint &&
|
||||
floorPlanGroupLine
|
||||
) {
|
||||
// Store generated lines in ref
|
||||
lines.current.push(...dfxWallGenerate);
|
||||
dfxWallGenerate.map((line: any) => {
|
||||
const lineData = arrayLineToObject(line as Types.Line);
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
// setLine(organization, lineData.layer!, lineData.line!, lineData.type!);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const input = {
|
||||
organization: organization,
|
||||
layer: lineData.layer,
|
||||
line: lineData.line,
|
||||
type: lineData.type,
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
||||
})
|
||||
|
||||
// Load initial points and lines from DXF data
|
||||
loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls);
|
||||
loadInitialLine(floorPlanGroupLine, lines);
|
||||
|
||||
// Trigger scene update
|
||||
setUpdateScene(true);
|
||||
}
|
||||
}, [
|
||||
lines,
|
||||
floorPlanGroupLine,
|
||||
floorPlanGroupPoint,
|
||||
currentLayerPoint,
|
||||
dragPointControls,
|
||||
dfxWallGenerate,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Handles transformation changes for individual lines.
|
||||
* Updates the object value state with new position and recalculates wall points.
|
||||
* @param index - Index of the line being transformed
|
||||
*/
|
||||
const handleTransformChange = (index: number) => {
|
||||
const line = lineRefs.current[index];
|
||||
if (!line) return;
|
||||
|
||||
// Get current position of the line
|
||||
const position = line.position;
|
||||
|
||||
// Update state with new position
|
||||
setObjValue({ x: position.x, y: position.y, z: position.z });
|
||||
|
||||
// Recalculate wall points based on new position
|
||||
getWallPointsFromBlueprint({
|
||||
objValue: { x: position.x, y: position.y, z: position.z },
|
||||
setDfxGenerate: () => { },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Render DXF lines with transform controls when DXF data is available and view is toggled */}
|
||||
{dfxuploaded &&
|
||||
dfxuploaded.length > 0 &&
|
||||
toggleView &&
|
||||
dfxuploaded?.map((geometry: any, index: number) => {
|
||||
// Create a new line object for each geometry in the DXF data
|
||||
const line = new Line(geometry, new LineBasicMaterial({ color: 'red' }));
|
||||
line.rotation.set(-Math.PI / 2, 0, 0);
|
||||
|
||||
// Store line reference
|
||||
lineRefs.current[index] = line;
|
||||
|
||||
return (
|
||||
<TransformControls
|
||||
key={index}
|
||||
object={line}
|
||||
showY={false}
|
||||
onMouseUp={() => handleTransformChange(index)}
|
||||
>
|
||||
{/* Render the line with current position from state */}
|
||||
<primitive object={line} position={[objValue.x, objValue.y, objValue.z]} />
|
||||
</TransformControls>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DxfFile;
|
|
@ -0,0 +1,65 @@
|
|||
import { BufferGeometry, Vector3 } from "three";
|
||||
type DXFData = any;
|
||||
export const convertDXFToThree = (dxfData: DXFData): BufferGeometry[] => {
|
||||
const geometries: BufferGeometry[] = [];
|
||||
const UNIT = 1000;
|
||||
if (dxfData.entities) {
|
||||
dxfData.entities.forEach((entity: any) => {
|
||||
// LINE
|
||||
if (entity.type === "LINE" && entity.vertices) {
|
||||
const points = [
|
||||
new Vector3(entity.vertices[0].x, entity.vertices[0].y, 0),
|
||||
new Vector3(entity.vertices[1].x, entity.vertices[1].y, 0),
|
||||
];
|
||||
const geometry = new BufferGeometry().setFromPoints(points);
|
||||
geometry.scale(1 / UNIT, 1 / UNIT, 1 / UNIT);
|
||||
geometries.push(geometry);
|
||||
}
|
||||
|
||||
// LWPOLYLINE
|
||||
else if (entity.type === "LWPOLYLINE" && entity.vertices) {
|
||||
const points: Vector3[] = entity.vertices.map(
|
||||
(v: any) => new Vector3(v.x, v.y, 0)
|
||||
);
|
||||
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
const segment = [points[i], points[i + 1]];
|
||||
const geometry = new BufferGeometry().setFromPoints(segment);
|
||||
geometry.scale(1 / UNIT, 1 / UNIT, 1 / UNIT);
|
||||
geometries.push(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
// ARC
|
||||
else if (
|
||||
entity.type === "ARC" &&
|
||||
entity.center &&
|
||||
entity.radius !== undefined
|
||||
) {
|
||||
const { center, radius, startAngle, endAngle } = entity;
|
||||
if (
|
||||
center === undefined ||
|
||||
radius === undefined ||
|
||||
startAngle === undefined ||
|
||||
endAngle === undefined
|
||||
)
|
||||
return;
|
||||
const numSegments = 32;
|
||||
const points: Vector3[] = [];
|
||||
|
||||
for (let i = 0; i <= numSegments; i++) {
|
||||
const t = i / numSegments;
|
||||
const angle = startAngle + t * (endAngle - startAngle); // linear interpolation
|
||||
const x = center.x + radius * Math.cos(angle);
|
||||
const y = center.y + radius * Math.sin(angle);
|
||||
points.push(new Vector3(x, y, 0));
|
||||
}
|
||||
|
||||
const geometry = new BufferGeometry().setFromPoints(points);
|
||||
geometry.scale(1 / UNIT, 1 / UNIT, 1 / UNIT);
|
||||
geometries.push(geometry);
|
||||
}
|
||||
});
|
||||
}
|
||||
return geometries;
|
||||
};
|
|
@ -0,0 +1,187 @@
|
|||
import { MathUtils, Vector3, BufferGeometry } from "three";
|
||||
type DXFData = any; // Replace with actual DXF data type
|
||||
type DXFEntity = any; // Replace with actual DXF entity type
|
||||
type WallLineVertex = [Vector3, string, number, string]; // Represents a wall segment with start point, ID, weight, and type
|
||||
interface Props {
|
||||
parsedData?: DXFData; // Parsed DXF file data
|
||||
setDfxGenerate?: (walls: WallLineVertex[][]) => void; // Callback to set generated walls
|
||||
objValue: any; // Object position values for offset calculation
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes DXF entities to generate wall points for a blueprint.
|
||||
* Handles LINE, LWPOLYLINE, and ARC entity types, converting them to wall segments.
|
||||
* Applies unit conversion and positional offsets to all points.
|
||||
*
|
||||
* @param {Props} params - Configuration parameters
|
||||
* @param {DXFData} params.parsedData - Parsed DXF file data
|
||||
* @param {Function} params.setDfxGenerate - Callback to store generated walls
|
||||
* @param {Object} params.objValue - Contains x,y,z offsets for position adjustment
|
||||
*/
|
||||
export function getWallPointsFromBlueprint({
|
||||
parsedData,
|
||||
setDfxGenerate,
|
||||
objValue,
|
||||
}: Props) {
|
||||
// Early return if no data is provided
|
||||
if (!parsedData) return;
|
||||
if (!parsedData.entities) return;
|
||||
|
||||
const unit = 1000; // Conversion factor from millimeters to meters
|
||||
const wallVertex: WallLineVertex[][] = []; // Stores all generated wall segments
|
||||
|
||||
// Process each entity in the DXF file
|
||||
parsedData.entities.forEach((entity: DXFEntity) => {
|
||||
// Handle LINE entities
|
||||
if (entity.type === "LINE" && entity.vertices) {
|
||||
// Create start and end vectors with unit conversion and position offset
|
||||
const startVec = new Vector3(
|
||||
entity.vertices[0].x / unit,
|
||||
0.01, // Slightly above ground to avoid z-fighting
|
||||
-entity.vertices[0].y / unit // Invert Y-axis to match Three.js coordinate system
|
||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||
|
||||
const endVec = new Vector3(
|
||||
entity.vertices[1].x / unit,
|
||||
0.01,
|
||||
-entity.vertices[1].y / unit
|
||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||
|
||||
// Check if points already exist to avoid duplicates
|
||||
const existingStart = wallVertex
|
||||
.flat()
|
||||
.find((v) => v[0].equals(startVec));
|
||||
const startPoint: WallLineVertex = existingStart || [
|
||||
startVec,
|
||||
MathUtils.generateUUID(), // Generate unique ID for new points
|
||||
1, // Default weight
|
||||
"WallLine", // Type identifier
|
||||
];
|
||||
|
||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
||||
const endPoint: WallLineVertex = existingEnd || [
|
||||
endVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
|
||||
// Add the line segment to our collection
|
||||
wallVertex.push([startPoint, endPoint]);
|
||||
}
|
||||
// Handle LWPOLYLINE entities (connected line segments)
|
||||
else if (entity.type === "LWPOLYLINE" && entity.vertices) {
|
||||
let firstPoint: WallLineVertex | undefined; // Store first point for closing the polyline
|
||||
|
||||
// Process each vertex pair in the polyline
|
||||
for (let i = 0; i < entity.vertices.length - 1; i++) {
|
||||
// Convert vertices to Three.js vectors with offset
|
||||
const startVec = new Vector3(
|
||||
entity.vertices[i].x / unit,
|
||||
0.01,
|
||||
-entity.vertices[i].y / unit
|
||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||
|
||||
const endVec = new Vector3(
|
||||
entity.vertices[i + 1].x / unit,
|
||||
0.01,
|
||||
-entity.vertices[i + 1].y / unit
|
||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||
|
||||
// Check for existing points
|
||||
const existingStart = wallVertex
|
||||
.flat()
|
||||
.find((v) => v[0].equals(startVec));
|
||||
const startPoint: WallLineVertex = existingStart || [
|
||||
startVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
|
||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
||||
const endPoint: WallLineVertex = existingEnd || [
|
||||
endVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
|
||||
wallVertex.push([startPoint, endPoint]);
|
||||
|
||||
// Store first point and create closing segment if this is the last vertex
|
||||
if (i === 0) firstPoint = startPoint;
|
||||
if (i === entity.vertices.length - 2 && firstPoint) {
|
||||
wallVertex.push([endPoint, firstPoint]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle ARC entities
|
||||
else if (entity.type === "ARC") {
|
||||
const { center, radius, startAngle, endAngle } = entity;
|
||||
// Validate required ARC properties
|
||||
if (
|
||||
!center ||
|
||||
radius === undefined ||
|
||||
startAngle === undefined ||
|
||||
endAngle === undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert ARC to series of line segments
|
||||
const numSegments = 16; // Number of segments to approximate the arc
|
||||
const angleStep = (endAngle - startAngle) / numSegments;
|
||||
const arcPoints: Vector3[] = []; // Stores points along the arc
|
||||
|
||||
// Generate points along the arc
|
||||
for (let i = 0; i <= numSegments; i++) {
|
||||
const angle = startAngle + i * angleStep;
|
||||
// Calculate arc point in DXF coordinate system
|
||||
const x = center.x + radius * Math.cos(angle);
|
||||
const y = -center.y + radius * Math.sin(angle); // Invert Y-axis
|
||||
|
||||
// Convert to Three.js vector with offset
|
||||
const vec = new Vector3(x / unit, 0.01, y / unit).add(
|
||||
new Vector3(objValue.x, 0, objValue.z)
|
||||
);
|
||||
|
||||
arcPoints.push(vec);
|
||||
}
|
||||
|
||||
// Create line segments between arc points
|
||||
for (let i = 0; i < arcPoints.length - 1; i++) {
|
||||
const startVec = arcPoints[i];
|
||||
const endVec = arcPoints[i + 1];
|
||||
|
||||
// Check for existing points
|
||||
const existingStart = wallVertex
|
||||
.flat()
|
||||
.find((v) => v[0].equals(startVec));
|
||||
const startPoint: WallLineVertex = existingStart || [
|
||||
startVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
|
||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
||||
const endPoint: WallLineVertex = existingEnd || [
|
||||
endVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
|
||||
wallVertex.push([startPoint, endPoint]);
|
||||
}
|
||||
}
|
||||
// Log unsupported entity types
|
||||
else {
|
||||
console.error("Unsupported entity type:", entity.type);
|
||||
}
|
||||
});
|
||||
|
||||
// Return the generated walls through callback if provided
|
||||
setDfxGenerate && setDfxGenerate(wallVertex);
|
||||
}
|
|
@ -15,7 +15,8 @@ export default async function addDragControl(
|
|||
floorPlanGroupLine: Types.RefGroup,
|
||||
lines: Types.RefLines,
|
||||
onlyFloorlines: Types.RefOnlyFloorLines,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?:string
|
||||
) {
|
||||
|
||||
////////// Dragging Point and also change the size to indicate during hover //////////
|
||||
|
@ -40,6 +41,7 @@ export default async function addDragControl(
|
|||
if (!dragPointControls.current) return;
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -55,7 +57,9 @@ export default async function addDragControl(
|
|||
organization: organization,
|
||||
position: { "x": event.object.position.x, "y": 0.01, "z": event.object.position.z },
|
||||
uuid: event.object.uuid,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:update', data);
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
import * as THREE from 'three';
|
||||
import * as Types from '../../../../types/world/worldTypes';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
|
||||
export default async function addAisleToScene(
|
||||
aisle: Types.Line,
|
||||
floorGroupAisle: Types.RefGroup,
|
||||
): Promise<void> {
|
||||
if (aisle.length >= 2 && aisle[0] && aisle[1]) {
|
||||
const start: Types.Vector3 = aisle[0][0];
|
||||
const end: Types.Vector3 = aisle[1][0];
|
||||
|
||||
const direction = new THREE.Vector3(
|
||||
end.x - start.x,
|
||||
end.y - start.y,
|
||||
end.z - start.z
|
||||
).normalize();
|
||||
|
||||
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||
const offsetDistance = CONSTANTS.aisleConfig.width;
|
||||
|
||||
const leftStart = new THREE.Vector3().copy(start).addScaledVector(perp, offsetDistance);
|
||||
const rightStart = new THREE.Vector3().copy(start).addScaledVector(perp, -offsetDistance);
|
||||
const leftEnd = new THREE.Vector3().copy(end).addScaledVector(perp, offsetDistance);
|
||||
const rightEnd = new THREE.Vector3().copy(end).addScaledVector(perp, -offsetDistance);
|
||||
|
||||
const stripShape = new THREE.Shape();
|
||||
stripShape.moveTo(leftStart.x, leftStart.z);
|
||||
stripShape.lineTo(leftEnd.x, leftEnd.z);
|
||||
stripShape.lineTo(rightEnd.x, rightEnd.z);
|
||||
stripShape.lineTo(rightStart.x, rightStart.z);
|
||||
stripShape.lineTo(leftStart.x, leftStart.z);
|
||||
|
||||
const extrudeSettings = {
|
||||
depth: CONSTANTS.aisleConfig.height,
|
||||
bevelEnabled: false,
|
||||
};
|
||||
|
||||
const stripGeometry = new THREE.ExtrudeGeometry(stripShape, extrudeSettings);
|
||||
const stripMaterial = new THREE.MeshStandardMaterial({
|
||||
color: CONSTANTS.aisleConfig.defaultColor,
|
||||
polygonOffset: true,
|
||||
polygonOffsetFactor: -1,
|
||||
polygonOffsetUnits: -1,
|
||||
});
|
||||
|
||||
const stripMesh = new THREE.Mesh(stripGeometry, stripMaterial);
|
||||
stripMesh.receiveShadow = true;
|
||||
stripMesh.castShadow = true;
|
||||
|
||||
stripMesh.position.y = (aisle[0][2] - 1) * CONSTANTS.wallConfig.height + 0.01;
|
||||
stripMesh.rotateX(Math.PI / 2);
|
||||
|
||||
floorGroupAisle.current.add(stripMesh);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import * as Types from '../../../../types/world/worldTypes';
|
||||
import addAisleToScene from './addAilseToScene';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
|
||||
export default async function loadAisles(
|
||||
lines: Types.RefLines,
|
||||
floorGroupAisle: Types.RefGroup
|
||||
) {
|
||||
// console.log('lines: ', lines.current[0][0][0]);
|
||||
if (!floorGroupAisle.current) return
|
||||
floorGroupAisle.current.children = [];
|
||||
const aisles = lines.current.filter((line) => line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName);
|
||||
|
||||
if (aisles.length > 0) {
|
||||
aisles.forEach((aisle: Types.Line) => {
|
||||
addAisleToScene(aisle, floorGroupAisle)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -37,7 +37,8 @@ async function drawOnlyFloor(
|
|||
setNewLines: any,
|
||||
setDeletedLines: any,
|
||||
activeLayer: Types.Number,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?:string
|
||||
): Promise<void> {
|
||||
|
||||
////////// Creating lines Based on the positions clicked //////////
|
||||
|
@ -72,7 +73,7 @@ async function drawOnlyFloor(
|
|||
|
||||
if (intersectionPoint) {
|
||||
|
||||
const newLines = splitLine(visibleIntersect, intersectionPoint, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, lines, setDeletedLines, floorPlanGroupLine, socket, pointColor, lineColor, intersectsLines[0].object.userData.linePoints[0][3]);
|
||||
const newLines = splitLine(visibleIntersect, intersectionPoint, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, lines, setDeletedLines, floorPlanGroupLine, socket, pointColor, lineColor, intersectsLines[0].object.userData.linePoints[0][3],projectId);
|
||||
setNewLines([newLines[0], newLines[1]]);
|
||||
|
||||
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), isSnappedUUID.current!, activeLayer, CONSTANTS.lineConfig.floorName]);
|
||||
|
@ -83,6 +84,7 @@ async function drawOnlyFloor(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -95,7 +97,9 @@ async function drawOnlyFloor(
|
|||
layer: data.layer,
|
||||
line: data.line,
|
||||
type: data.type,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
@ -146,6 +150,7 @@ async function drawOnlyFloor(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -158,7 +163,9 @@ async function drawOnlyFloor(
|
|||
layer: data.layer,
|
||||
line: data.line,
|
||||
type: data.type,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
|
|
@ -14,7 +14,8 @@ async function DeleteLayer(
|
|||
floorGroup: Types.RefGroup,
|
||||
setDeletedLines: any,
|
||||
setRemovedLayer: Types.setRemoveLayerSetState,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?:string
|
||||
): Promise<void> {
|
||||
|
||||
////////// Remove the Lines from the lines.current based on the removed layer and rearrange the layer number that are higher than the removed layer //////////
|
||||
|
@ -23,6 +24,7 @@ async function DeleteLayer(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -33,7 +35,9 @@ async function DeleteLayer(
|
|||
const data = {
|
||||
organization: organization,
|
||||
layer: removedLayer,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:delete:layer', data);
|
||||
|
|
|
@ -11,7 +11,8 @@ function deleteLine(
|
|||
floorPlanGroupLine: Types.RefGroup,
|
||||
floorPlanGroupPoint: Types.RefGroup,
|
||||
setDeletedLines: any,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?: string
|
||||
): void {
|
||||
|
||||
////////// Deleting a line and the points if they are not connected to any other line //////////
|
||||
|
@ -25,6 +26,7 @@ function deleteLine(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -44,7 +46,9 @@ function deleteLine(
|
|||
{ "uuid": linePoints[0][1] },
|
||||
{ "uuid": linePoints[1][1] }
|
||||
],
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:delete', data);
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Vector2 } from "three";
|
|||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import getRoomsFromLines from "../getRoomsFromLines";
|
||||
import * as turf from '@turf/turf';
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const DistanceText = () => {
|
||||
const [lines, setLines] = useState<
|
||||
|
@ -30,6 +31,7 @@ const DistanceText = () => {
|
|||
const { deletedLines, setDeletedLines } = useDeletedLines();
|
||||
const [linesState, setLinesState] = useState<Types.Lines>([]);
|
||||
const { roomsState, setRoomsState } = useRoomsState();
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
|
@ -95,7 +97,7 @@ const DistanceText = () => {
|
|||
if (!email) return;
|
||||
const organization = email.split("@")[1].split(".")[0];
|
||||
|
||||
getLines(organization).then((data) => {
|
||||
getLines(organization,projectId).then((data) => {
|
||||
data = objectLinesToArray(data);
|
||||
setLinesState(data);
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ async function drawWall(
|
|||
setNewLines: any,
|
||||
setDeletedLines: any,
|
||||
activeLayer: Types.Number,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?: string
|
||||
): Promise<void> {
|
||||
|
||||
////////// Creating lines Based on the positions clicked //////////
|
||||
|
@ -66,7 +67,7 @@ async function drawWall(
|
|||
|
||||
if (intersectionPoint) {
|
||||
|
||||
const newLines = splitLine(visibleIntersect, intersectionPoint, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, lines, setDeletedLines, floorPlanGroupLine, socket, CONSTANTS.pointConfig.wallOuterColor, CONSTANTS.lineConfig.wallColor, CONSTANTS.lineConfig.wallName);
|
||||
const newLines = splitLine(visibleIntersect, intersectionPoint, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, lines, setDeletedLines, floorPlanGroupLine, socket, CONSTANTS.pointConfig.wallOuterColor, CONSTANTS.lineConfig.wallColor, CONSTANTS.lineConfig.wallName,projectId);
|
||||
setNewLines([newLines[0], newLines[1]]);
|
||||
|
||||
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), isSnappedUUID.current!, activeLayer, CONSTANTS.lineConfig.wallName,]);
|
||||
|
@ -76,6 +77,7 @@ async function drawWall(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -88,9 +90,12 @@ async function drawWall(
|
|||
layer: data.layer,
|
||||
line: data.line,
|
||||
type: data.type,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
console.log('input: ', input);
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
||||
setNewLines([newLines[0], newLines[1], line.current]);
|
||||
|
@ -135,6 +140,7 @@ async function drawWall(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -147,9 +153,12 @@ async function drawWall(
|
|||
layer: data.layer,
|
||||
line: data.line,
|
||||
type: data.type,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
console.log('input: ', input);
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
||||
setNewLines([line.current])
|
||||
|
|
|
@ -23,6 +23,7 @@ function splitLine(
|
|||
pointColor: Types.String,
|
||||
lineColor: Types.String,
|
||||
lineType: Types.String,
|
||||
projectId?: string
|
||||
): [Types.Line, Types.Line] {
|
||||
|
||||
////////// Removing the clicked line and splitting it with the clicked position adding a new point and two new lines //////////
|
||||
|
@ -35,6 +36,7 @@ function splitLine(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -55,7 +57,9 @@ function splitLine(
|
|||
{ "uuid": visibleIntersect.object.userData.linePoints[0][1] },
|
||||
{ "uuid": visibleIntersect.object.userData.linePoints[1][1] }
|
||||
],
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:delete', data);
|
||||
|
@ -92,7 +96,9 @@ function splitLine(
|
|||
layer: line1.layer,
|
||||
line: line1.line,
|
||||
type: line1.type,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input1);
|
||||
|
@ -108,7 +114,9 @@ function splitLine(
|
|||
layer: line2.layer,
|
||||
line: line2.line,
|
||||
type: line2.type,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input2);
|
||||
|
|
|
@ -13,7 +13,8 @@ function deletePoint(
|
|||
floorPlanGroupLine: Types.RefGroup,
|
||||
lines: Types.RefLines,
|
||||
setDeletedLines: any,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?:string
|
||||
): void {
|
||||
////////// Deleting a Point and the lines that are connected to it //////////
|
||||
|
||||
|
@ -28,6 +29,7 @@ function deletePoint(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -38,9 +40,12 @@ function deletePoint(
|
|||
const data = {
|
||||
organization: organization,
|
||||
uuid: DeletedPointUUID,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
console.log('data: ', data);
|
||||
socket.emit('v1:Line:delete:point', data);
|
||||
|
||||
////////// Update onlyFloorlines.current to remove references to the deleted point //////////
|
||||
|
|
|
@ -12,7 +12,8 @@ async function AddWallItems(
|
|||
raycaster: THREE.Raycaster,
|
||||
CSGGroup: Types.RefMesh,
|
||||
setWallItems: Types.setWallItemSetState,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?: string
|
||||
): Promise<void> {
|
||||
let intersects = raycaster?.intersectObject(CSGGroup.current!, true);
|
||||
const wallRaycastIntersection = intersects?.find((child) => child.object.name.includes("WallRaycastReference"));
|
||||
|
@ -100,6 +101,7 @@ async function AddWallItems(
|
|||
|
||||
const email = localStorage.getItem('email');
|
||||
const organization = email ? (email.split("@")[1]).split(".")[0] : 'default';
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
|
@ -112,9 +114,12 @@ async function AddWallItems(
|
|||
position: newWallItem.position,
|
||||
quaternion: newWallItem.quaternion,
|
||||
scale: newWallItem.scale,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);
|
||||
socket.emit('v1:wallItems:set', data);
|
||||
|
||||
setWallItems((prevItems) => {
|
||||
|
|
|
@ -6,7 +6,8 @@ function DeleteWallItems(
|
|||
hoveredDeletableWallItem: Types.RefMesh,
|
||||
setWallItems: Types.setWallItemSetState,
|
||||
wallItems: Types.wallItems,
|
||||
socket: Socket<any>
|
||||
socket: Socket<any>,
|
||||
projectId?: string
|
||||
): void {
|
||||
|
||||
////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage //////////
|
||||
|
@ -23,6 +24,7 @@ function DeleteWallItems(
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -34,7 +36,9 @@ function DeleteWallItems(
|
|||
organization: organization,
|
||||
modelUuid: removedItem?.model?.uuid!,
|
||||
modelName: removedItem?.modelName!,
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
}
|
||||
|
||||
socket.emit('v1:wallItems:delete', data);
|
||||
|
|
|
@ -127,3 +127,14 @@ async function loadWalls(
|
|||
}
|
||||
|
||||
export default loadWalls;
|
||||
|
||||
|
||||
// A----- B----- C
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// F----- E----- D
|
||||
|
||||
// 1. A -> B, B -> C, C -> D, D -> E, E -> F, F -> A, B -> E
|
||||
|
||||
// 2. E -> F, F -> A, A -> B, B -> E, E -> D, D -> C, C -> B
|
|
@ -6,6 +6,7 @@ import {
|
|||
useToggleView,
|
||||
useWallVisibility,
|
||||
useUpdateScene,
|
||||
useRenameModeStore,
|
||||
} from "../../../store/builder/store";
|
||||
import hideRoof from "../geomentries/roofs/hideRoof";
|
||||
import hideWalls from "../geomentries/walls/hideWalls";
|
||||
|
@ -15,6 +16,7 @@ import addPillar from "../geomentries/pillars/addPillar";
|
|||
import DeletePillar from "../geomentries/pillars/deletePillar";
|
||||
import DeletableHoveredPillar from "../geomentries/pillars/deletableHoveredPillar";
|
||||
import loadFloor from "../geomentries/floors/loadFloor";
|
||||
import { useLeftData, useTopData } from "../../../store/visualization/useZone3DWidgetStore";
|
||||
|
||||
const FloorGroup = ({
|
||||
floorGroup,
|
||||
|
@ -30,6 +32,9 @@ const FloorGroup = ({
|
|||
const { addAction } = useAddAction();
|
||||
const { deleteTool } = useDeleteTool();
|
||||
const { updateScene, setUpdateScene } = useUpdateScene();
|
||||
const { setTop } = useTopData();
|
||||
const { setLeft } = useLeftData();
|
||||
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (updateScene) {
|
||||
|
@ -55,6 +60,7 @@ const FloorGroup = ({
|
|||
let isLeftMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
|
@ -62,6 +68,7 @@ const FloorGroup = ({
|
|||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
setIsRenameMode(false);
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (!drag) {
|
||||
|
@ -75,7 +82,15 @@ const FloorGroup = ({
|
|||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
const onMouseMove = (evt: any) => {
|
||||
if (!canvasElement) return;
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = evt.clientX - canvasRect.left;
|
||||
const relativeY = evt.clientY - canvasRect.top;
|
||||
if (!isRenameMode) {
|
||||
setTop(relativeY);
|
||||
setLeft(relativeX);
|
||||
}
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
|
@ -90,7 +105,7 @@ const FloorGroup = ({
|
|||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
};
|
||||
}, [deleteTool, addAction]);
|
||||
}, [deleteTool, addAction, isRenameMode]);
|
||||
|
||||
useFrame(() => {
|
||||
hideRoof(roofVisibility, floorGroup, camera);
|
||||
|
|
|
@ -1,242 +0,0 @@
|
|||
import * as THREE from 'three';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { useToggleView, useActiveLayer, useSocketStore, useDeletePointOrLine, useUpdateScene, useNewLines, useToolMode } from "../../../store/builder/store";
|
||||
import { useEffect } from "react";
|
||||
import removeSoloPoint from "../geomentries/points/removeSoloPoint";
|
||||
import removeReferenceLine from "../geomentries/lines/removeReferenceLine";
|
||||
import getClosestIntersection from "../geomentries/lines/getClosestIntersection";
|
||||
import addPointToScene from "../geomentries/points/addPointToScene";
|
||||
import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject';
|
||||
import addLineToScene from "../geomentries/lines/addLineToScene";
|
||||
import loadAisles from '../geomentries/aisles/loadAisles';
|
||||
|
||||
const FloorGroupAilse = ({ floorGroupAisle, plane, floorPlanGroupLine, floorPlanGroupPoint, line, lines, currentLayerPoint, dragPointControls, floorPlanGroup, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
|
||||
const { toggleView } = useToggleView();
|
||||
const { setDeletePointOrLine } = useDeletePointOrLine();
|
||||
const { toolMode } = useToolMode();
|
||||
const { socket } = useSocketStore();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { gl, raycaster } = useThree();
|
||||
const { updateScene, setUpdateScene } = useUpdateScene();
|
||||
const { setNewLines } = useNewLines();
|
||||
|
||||
useEffect(() => {
|
||||
if (updateScene) {
|
||||
loadAisles(lines, floorGroupAisle);
|
||||
setUpdateScene(false);
|
||||
}
|
||||
}, [updateScene])
|
||||
|
||||
useEffect(() => {
|
||||
if (toolMode === "Aisle") {
|
||||
setDeletePointOrLine(false);
|
||||
} else {
|
||||
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
|
||||
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
|
||||
}
|
||||
}, [toolMode]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onContextMenu = (e: any) => {
|
||||
e.preventDefault();
|
||||
if (toolMode === "Aisle") {
|
||||
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
|
||||
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseClick = (evt: any) => {
|
||||
if (!plane.current || drag) return;
|
||||
|
||||
const intersects = raycaster.intersectObject(plane.current, true);
|
||||
let intersectionPoint = intersects[0].point;
|
||||
const points = floorPlanGroupPoint.current?.children ?? [];
|
||||
const intersectsPoint = raycaster.intersectObjects(points, true).find(intersect => intersect.object.visible);
|
||||
let intersectsLines: any = raycaster.intersectObjects(floorPlanGroupLine.current.children, true);
|
||||
|
||||
|
||||
if (intersectsLines.length > 0 && intersects && intersects.length > 0 && !intersectsPoint) {
|
||||
const lineType = intersectsLines[0].object.userData.linePoints[0][3];
|
||||
if (lineType === CONSTANTS.lineConfig.aisleName) {
|
||||
// console.log("intersected a aisle line");
|
||||
const ThroughPoint = (intersectsLines[0].object.geometry.parameters.path).getPoints(CONSTANTS.lineConfig.lineIntersectionPoints);
|
||||
let intersection = getClosestIntersection(ThroughPoint, intersectionPoint);
|
||||
if (!intersection) return;
|
||||
const point = addPointToScene(intersection, CONSTANTS.pointConfig.aisleOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.aisleName);
|
||||
(line.current as Types.Line).push([new THREE.Vector3(intersection.x, 0.01, intersection.z), point.uuid, activeLayer, CONSTANTS.lineConfig.aisleName,]);
|
||||
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
|
||||
lines.current.push(line.current as Types.Line);
|
||||
|
||||
const data = arrayLineToObject(line.current as Types.Line);
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
||||
//REST
|
||||
|
||||
// setLine(organization, data.layer!, data.line!, data.type!);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const input = {
|
||||
organization: organization,
|
||||
layer: data.layer,
|
||||
line: data.line,
|
||||
type: data.type,
|
||||
socketId: socket.id
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
||||
setNewLines([line.current]);
|
||||
|
||||
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.pointConfig.aisleOuterColor, line.current, floorPlanGroupLine);
|
||||
let lastPoint = line.current[line.current.length - 1];
|
||||
line.current = [lastPoint];
|
||||
}
|
||||
}
|
||||
} else if (intersectsPoint && intersects && intersects.length > 0) {
|
||||
if (intersectsPoint.object.userData.type === CONSTANTS.lineConfig.aisleName) {
|
||||
// console.log("intersected a aisle point");
|
||||
intersectionPoint = intersectsPoint.object.position;
|
||||
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), intersectsPoint.object.uuid, activeLayer, CONSTANTS.lineConfig.aisleName,]);
|
||||
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
|
||||
lines.current.push(line.current as Types.Line);
|
||||
|
||||
const data = arrayLineToObject(line.current as Types.Line);
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
||||
//REST
|
||||
|
||||
// setLine(organization, data.layer!, data.line!, data.type!);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const input = {
|
||||
organization: organization,
|
||||
layer: data.layer,
|
||||
line: data.line,
|
||||
type: data.type,
|
||||
socketId: socket.id
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
||||
setNewLines([line.current]);
|
||||
|
||||
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.pointConfig.aisleOuterColor, line.current, floorPlanGroupLine);
|
||||
let lastPoint = line.current[line.current.length - 1];
|
||||
line.current = [lastPoint];
|
||||
ispreSnapped.current = false;
|
||||
isSnapped.current = false;
|
||||
}
|
||||
}
|
||||
} else if (intersects && intersects.length > 0) {
|
||||
// console.log("intersected a empty area");
|
||||
let uuid: string = "";
|
||||
if (isAngleSnapped.current && anglesnappedPoint.current && line.current.length > 0) {
|
||||
intersectionPoint = anglesnappedPoint.current;
|
||||
const point = addPointToScene(intersectionPoint, CONSTANTS.pointConfig.aisleOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.aisleName);
|
||||
uuid = point.uuid;
|
||||
} else if (isSnapped.current && snappedPoint.current && line.current.length > 0) {
|
||||
intersectionPoint = snappedPoint.current;
|
||||
uuid = isSnappedUUID.current!;
|
||||
} else if (ispreSnapped.current && snappedPoint.current) {
|
||||
intersectionPoint = snappedPoint.current;
|
||||
uuid = isSnappedUUID.current!;
|
||||
} else {
|
||||
const point = addPointToScene(intersectionPoint, CONSTANTS.pointConfig.aisleOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.aisleName);
|
||||
uuid = point.uuid;
|
||||
}
|
||||
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), uuid, activeLayer, CONSTANTS.lineConfig.aisleName,]);
|
||||
|
||||
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
|
||||
lines.current.push(line.current as Types.Line);
|
||||
|
||||
const data = arrayLineToObject(line.current as Types.Line);
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
||||
//REST
|
||||
|
||||
// setLine(organization, data.layer!, data.line!, data.type!);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const input = {
|
||||
organization: organization,
|
||||
layer: data.layer,
|
||||
line: data.line,
|
||||
type: data.type,
|
||||
socketId: socket.id
|
||||
}
|
||||
|
||||
socket.emit('v1:Line:create', input);
|
||||
|
||||
setNewLines([line.current]);
|
||||
|
||||
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.pointConfig.aisleOuterColor, line.current, floorPlanGroupLine);
|
||||
let lastPoint = line.current[line.current.length - 1];
|
||||
line.current = [lastPoint];
|
||||
ispreSnapped.current = false;
|
||||
isSnapped.current = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (toolMode === 'Aisle') {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("click", onMouseClick);
|
||||
canvasElement.addEventListener("contextmenu", onContextMenu);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContextMenu);
|
||||
};
|
||||
}, [toolMode])
|
||||
|
||||
|
||||
return (
|
||||
<group ref={floorGroupAisle} visible={!toggleView} name="floorGroupAisle">
|
||||
</group>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloorGroupAilse;
|
|
@ -16,6 +16,7 @@ import deleteLine from "../geomentries/lines/deleteLine";
|
|||
import drawWall from "../geomentries/lines/drawWall";
|
||||
import drawOnlyFloor from "../geomentries/floors/drawOnlyFloor";
|
||||
import addDragControl from "../eventDeclaration/dragControlDeclaration";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, floorGroup, currentLayerPoint, dragPointControls, hoveredDeletablePoint, hoveredDeletableLine, plane, line, lines, onlyFloorline, onlyFloorlines, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
|
||||
const state = useThree();
|
||||
|
@ -29,10 +30,12 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
|||
const { setNewLines } = useNewLines();
|
||||
const { setDeletedLines } = useDeletedLines();
|
||||
const { socket } = useSocketStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (toolMode === 'move') {
|
||||
addDragControl(dragPointControls, currentLayerPoint, state, floorPlanGroupPoint, floorPlanGroupLine, lines, onlyFloorlines, socket);
|
||||
addDragControl(dragPointControls, currentLayerPoint, state, floorPlanGroupPoint, floorPlanGroupLine, lines, onlyFloorlines, socket, projectId);
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
@ -47,7 +50,8 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
|||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
||||
// Load data from localStorage if available
|
||||
getLines(organization).then((data) => {
|
||||
getLines(organization, projectId).then((data) => {
|
||||
// console.log('data: ', data);
|
||||
|
||||
const Lines: Types.Lines = objectLinesToArray(data);
|
||||
|
||||
|
@ -103,7 +107,7 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
|||
|
||||
useEffect(() => {
|
||||
if (removedLayer !== null) {
|
||||
DeleteLayer(removedLayer, lines, floorPlanGroupLine, floorPlanGroupPoint, onlyFloorlines, floorGroup, setDeletedLines, setRemovedLayer, socket);
|
||||
DeleteLayer(removedLayer, lines, floorPlanGroupLine, floorPlanGroupPoint, onlyFloorlines, floorGroup, setDeletedLines, setRemovedLayer, socket, projectId);
|
||||
}
|
||||
}, [removedLayer]);
|
||||
|
||||
|
@ -149,19 +153,19 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
|||
|
||||
if (deletePointOrLine) {
|
||||
if (hoveredDeletablePoint.current !== null) {
|
||||
deletePoint(hoveredDeletablePoint, onlyFloorlines, floorPlanGroupPoint, floorPlanGroupLine, lines, setDeletedLines, socket);
|
||||
deletePoint(hoveredDeletablePoint, onlyFloorlines, floorPlanGroupPoint, floorPlanGroupLine, lines, setDeletedLines, socket, projectId);
|
||||
}
|
||||
if (hoveredDeletableLine.current !== null) {
|
||||
deleteLine(hoveredDeletableLine, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, socket);
|
||||
deleteLine(hoveredDeletableLine, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, socket, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
if (toolMode === "Wall") {
|
||||
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket);
|
||||
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId);
|
||||
}
|
||||
|
||||
if (toolMode === "Floor") {
|
||||
drawOnlyFloor(raycaster, state, camera, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, onlyFloorline, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket);
|
||||
drawOnlyFloor(raycaster, state, camera, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, onlyFloorline, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import DeleteWallItems from "../geomentries/walls/deleteWallItems";
|
|||
import loadInitialWallItems from "../IntialLoad/loadInitialWallItems";
|
||||
import AddWallItems from "../geomentries/walls/addWallItems";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const WallItemsGroup = ({
|
||||
currentWallItem,
|
||||
|
@ -38,10 +39,11 @@ const WallItemsGroup = ({
|
|||
const { setSelectedWallItem } = useSelectedWallItem();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedItem } = useSelectedItem();
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
// Load Wall Items from the backend
|
||||
loadInitialWallItems(setWallItems);
|
||||
loadInitialWallItems(setWallItems,projectId);
|
||||
}, []);
|
||||
|
||||
////////// Update the Position value changes in the selected item //////////
|
||||
|
@ -122,6 +124,7 @@ const WallItemsGroup = ({
|
|||
setTimeout(async () => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
//REST
|
||||
|
||||
|
@ -152,8 +155,11 @@ const WallItemsGroup = ({
|
|||
quaternion: currentItem.quaternion,
|
||||
scale: currentItem.scale!,
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);
|
||||
socket.emit("v1:wallItems:set", data);
|
||||
}, 0);
|
||||
(state.controls as any)!.enabled = true;
|
||||
|
@ -190,7 +196,7 @@ const WallItemsGroup = ({
|
|||
hoveredDeletableWallItem,
|
||||
setWallItems,
|
||||
wallItems,
|
||||
socket
|
||||
socket,projectId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +223,8 @@ const WallItemsGroup = ({
|
|||
raycaster,
|
||||
CSGGroup,
|
||||
setWallItems,
|
||||
socket
|
||||
socket,
|
||||
projectId
|
||||
);
|
||||
}
|
||||
event.preventDefault();
|
||||
|
|
|
@ -9,17 +9,19 @@ import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi";
|
|||
import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
|
||||
import loadWalls from "../geomentries/walls/loadWalls";
|
||||
import texturePath from "../../../assets/textures/floor/wall-tex.png";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const WallsMeshComponent = ({ lines }: any) => {
|
||||
const { walls, setWalls } = useWalls();
|
||||
const { updateScene, setUpdateScene } = useUpdateScene();
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (updateScene) {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
getLines(organization).then((data) => {
|
||||
getLines(organization,projectId).then((data) => {
|
||||
const Lines: Types.Lines = objectLinesToArray(data);
|
||||
localStorage.setItem("Lines", JSON.stringify(Lines));
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import * as CONSTANTS from "../../../types/world/worldConstants";
|
|||
import * as turf from "@turf/turf";
|
||||
import { computeArea } from "../functions/computeArea";
|
||||
import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const ZoneGroup: React.FC = () => {
|
||||
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
|
||||
|
@ -42,6 +43,7 @@ const ZoneGroup: React.FC = () => {
|
|||
const { setDeleteTool } = useDeleteTool();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const groupsRef = useRef<any>();
|
||||
|
||||
|
@ -79,11 +81,12 @@ const ZoneGroup: React.FC = () => {
|
|||
if (!email) return;
|
||||
|
||||
const organization = email.split("@")[1].split(".")[0];
|
||||
const data = await getZonesApi(organization);
|
||||
const data = await getZonesApi(organization, projectId);
|
||||
console.log('data: ', data);
|
||||
|
||||
if (data.data && data.data.length > 0) {
|
||||
const fetchedZones = data.data.map((zone: any) => ({
|
||||
zoneId: zone.zoneId,
|
||||
if (data.length > 0) {
|
||||
const fetchedZones = data.map((zone: any) => ({
|
||||
zoneUuid: zone.zoneUuid,
|
||||
zoneName: zone.zoneName,
|
||||
points: zone.points,
|
||||
viewPortCenter: zone.viewPortCenter,
|
||||
|
@ -93,7 +96,7 @@ const ZoneGroup: React.FC = () => {
|
|||
|
||||
setZones(fetchedZones);
|
||||
|
||||
const fetchedPoints = data.data.flatMap((zone: any) =>
|
||||
const fetchedPoints = data.flatMap((zone: any) =>
|
||||
zone.points
|
||||
.slice(0, 4)
|
||||
.map(
|
||||
|
@ -128,7 +131,7 @@ const ZoneGroup: React.FC = () => {
|
|||
zones
|
||||
.filter((zone: any) => zone.layer === removedLayer)
|
||||
.forEach((zone: any) => {
|
||||
deleteZoneFromBackend(zone.zoneId);
|
||||
deleteZoneFromBackend(zone.zoneUuid);
|
||||
});
|
||||
|
||||
setRemovedLayer(null);
|
||||
|
@ -151,11 +154,12 @@ const ZoneGroup: React.FC = () => {
|
|||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const addZoneToBackend = async (zone: {
|
||||
zoneId: string;
|
||||
zoneUuid: string;
|
||||
zoneName: string;
|
||||
points: [number, number, number][];
|
||||
layer: string;
|
||||
}) => {
|
||||
console.log('zoneUuid: ', zone);
|
||||
const email = localStorage.getItem("email");
|
||||
const userId = localStorage.getItem("userId");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
@ -189,10 +193,11 @@ const ZoneGroup: React.FC = () => {
|
|||
|
||||
const input = {
|
||||
userId: userId,
|
||||
projectId,
|
||||
organization: organization,
|
||||
zoneData: {
|
||||
zoneName: zone.zoneName,
|
||||
zoneId: zone.zoneId,
|
||||
zoneUuid: zone.zoneUuid,
|
||||
points: zone.points,
|
||||
viewPortCenter: target,
|
||||
viewPortposition: position,
|
||||
|
@ -200,12 +205,12 @@ const ZoneGroup: React.FC = () => {
|
|||
},
|
||||
};
|
||||
|
||||
socket.emit("v2:zone:set", input);
|
||||
socket.emit("v1:zone:set", input);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const updateZoneToBackend = async (zone: {
|
||||
zoneId: string;
|
||||
zoneUuid: string;
|
||||
zoneName: string;
|
||||
points: [number, number, number][];
|
||||
layer: string;
|
||||
|
@ -243,10 +248,11 @@ const ZoneGroup: React.FC = () => {
|
|||
|
||||
const input = {
|
||||
userId: userId,
|
||||
projectId,
|
||||
organization: organization,
|
||||
zoneData: {
|
||||
zoneName: zone.zoneName,
|
||||
zoneId: zone.zoneId,
|
||||
zoneUuid: zone.zoneUuid,
|
||||
points: zone.points,
|
||||
viewPortCenter: target,
|
||||
viewPortposition: position,
|
||||
|
@ -254,29 +260,30 @@ const ZoneGroup: React.FC = () => {
|
|||
},
|
||||
};
|
||||
|
||||
socket.emit("v2:zone:set", input);
|
||||
socket.emit("v1:zone:set", input);
|
||||
};
|
||||
|
||||
const deleteZoneFromBackend = async (zoneId: string) => {
|
||||
const deleteZoneFromBackend = async (zoneUuid: string) => {
|
||||
const email = localStorage.getItem("email");
|
||||
const userId = localStorage.getItem("userId");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
const input = {
|
||||
userId: userId,
|
||||
projectId,
|
||||
organization: organization,
|
||||
zoneId: zoneId,
|
||||
zoneUuid: zoneUuid,
|
||||
};
|
||||
|
||||
socket.emit("v2:zone:delete", input);
|
||||
socket.emit("v1:zone:delete", input);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const handleDeleteZone = (zoneId: string) => {
|
||||
const updatedZones = zones.filter((zone: any) => zone.zoneId !== zoneId);
|
||||
const handleDeleteZone = (zoneUuid: string) => {
|
||||
const updatedZones = zones.filter((zone: any) => zone.zoneUuid !== zoneUuid);
|
||||
setZones(updatedZones);
|
||||
|
||||
const zoneIndex = zones.findIndex((zone: any) => zone.zoneId === zoneId);
|
||||
const zoneIndex = zones.findIndex((zone: any) => zone.zoneUuid === zoneUuid);
|
||||
if (zoneIndex !== -1) {
|
||||
const zonePointsToRemove = zonePoints.slice(
|
||||
zoneIndex * 4,
|
||||
|
@ -291,7 +298,7 @@ const ZoneGroup: React.FC = () => {
|
|||
);
|
||||
setZonePoints(updatedzonePoints);
|
||||
}
|
||||
deleteZoneFromBackend(zoneId);
|
||||
deleteZoneFromBackend(zoneUuid);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -353,9 +360,9 @@ const ZoneGroup: React.FC = () => {
|
|||
] as [number, number, number][];
|
||||
|
||||
const zoneName = `Zone ${zones.length + 1}`;
|
||||
const zoneId = THREE.MathUtils.generateUUID();
|
||||
const zoneUuid = THREE.MathUtils.generateUUID();
|
||||
const newZone = {
|
||||
zoneId,
|
||||
zoneUuid,
|
||||
zoneName,
|
||||
points: points,
|
||||
layer: activeLayer,
|
||||
|
@ -399,8 +406,8 @@ const ZoneGroup: React.FC = () => {
|
|||
);
|
||||
if (sphereIndex !== -1) {
|
||||
const zoneIndex = Math.floor(sphereIndex / 4);
|
||||
const zoneId = zones[zoneIndex].zoneId;
|
||||
handleDeleteZone(zoneId);
|
||||
const zoneUuid = zones[zoneIndex].zoneUuid;
|
||||
handleDeleteZone(zoneUuid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -531,9 +538,9 @@ const ZoneGroup: React.FC = () => {
|
|||
<group name="zones" visible={!toggleView}>
|
||||
{zones.map((zone: any) => (
|
||||
<group
|
||||
key={zone.zoneId}
|
||||
key={zone.zoneUuid}
|
||||
name={zone.zoneName}
|
||||
visible={zone.zoneId === selectedZone.zoneId}
|
||||
visible={zone.zoneUuid === selectedZone.zoneUuid}
|
||||
>
|
||||
{zone.points
|
||||
.slice(0, -1)
|
||||
|
@ -610,7 +617,7 @@ const ZoneGroup: React.FC = () => {
|
|||
return (
|
||||
<Html
|
||||
// data
|
||||
key={zone.zoneId}
|
||||
key={zone.zoneUuid}
|
||||
position={htmlPosition}
|
||||
// class
|
||||
className="zone-name-wrapper"
|
||||
|
@ -629,14 +636,14 @@ const ZoneGroup: React.FC = () => {
|
|||
.filter((zone: any) => zone.layer === activeLayer)
|
||||
.map((zone: any) => (
|
||||
<Line
|
||||
key={zone.zoneId}
|
||||
key={zone.zoneUuid}
|
||||
points={zone.points}
|
||||
color="#007BFF"
|
||||
lineWidth={3}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (deletePointOrLine) {
|
||||
handleDeleteZone(zone.zoneId);
|
||||
handleDeleteZone(zone.zoneUuid);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -702,10 +709,10 @@ const ZoneGroup: React.FC = () => {
|
|||
.flatMap((zone: any) =>
|
||||
zone.points.slice(0, 4).map((point: any, pointIndex: number) => (
|
||||
<Sphere
|
||||
key={`${zone.zoneId}-point-${pointIndex}`}
|
||||
key={`${zone.zoneUuid}-point-${pointIndex}`}
|
||||
position={new THREE.Vector3(...point)}
|
||||
args={[0.3, 16, 16]}
|
||||
name={`point-${zone.zoneId}-${pointIndex}`}
|
||||
name={`point-${zone.zoneUuid}-${pointIndex}`}
|
||||
>
|
||||
<meshBasicMaterial color="red" />
|
||||
</Sphere>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo } from "react";
|
||||
import * as Constants from '../../../types/world/worldConstants';
|
||||
import { Tube } from '@react-three/drei';
|
||||
|
||||
interface LineProps {
|
||||
points: [Point, Point];
|
||||
}
|
||||
|
||||
function Line({ points }: Readonly<LineProps>) {
|
||||
const path = useMemo(() => {
|
||||
const [start, end] = points.map(p => new THREE.Vector3(...p.position));
|
||||
return new THREE.LineCurve3(start, end);
|
||||
}, [points]);
|
||||
|
||||
const colors = getColor(points[0]);
|
||||
|
||||
function getColor(point: Point) {
|
||||
if (point.pointType === 'Aisle') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.aisleColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Floor') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.floorColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.wallColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Zone') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.zoneColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.defaultColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Tube
|
||||
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
|
||||
>
|
||||
<meshStandardMaterial color={colors.defaultLineColor} />
|
||||
</Tube>
|
||||
);
|
||||
}
|
||||
|
||||
export default Line;
|
|
@ -0,0 +1,25 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo } from "react";
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import { Tube } from '@react-three/drei';
|
||||
|
||||
interface ReferenceLineProps {
|
||||
points: [Point, Point];
|
||||
}
|
||||
|
||||
function ReferenceLine({ points }: Readonly<ReferenceLineProps>) {
|
||||
const path = useMemo(() => {
|
||||
const [start, end] = points.map(p => new THREE.Vector3(...p.position));
|
||||
return new THREE.LineCurve3(start, end);
|
||||
}, [points]);
|
||||
|
||||
return (
|
||||
<Tube
|
||||
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
|
||||
>
|
||||
<meshStandardMaterial color={Constants.lineConfig.helperColor} />
|
||||
</Tube>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReferenceLine;
|
|
@ -16,7 +16,7 @@ export function useAislePointSnapping(point: Point) {
|
|||
} => {
|
||||
if (!CAN_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
|
||||
const connectedPoints = getConnectedPoints(point.uuid);
|
||||
const connectedPoints = getConnectedPoints(point.pointUuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return {
|
||||
position: newPosition,
|
||||
|
@ -68,7 +68,7 @@ export function useAislePointSnapping(point: Point) {
|
|||
isSnapped,
|
||||
snapSources
|
||||
};
|
||||
}, [point.uuid, getConnectedPoints]);
|
||||
}, [point.pointUuid, getConnectedPoints]);
|
||||
|
||||
return { snapPosition };
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useAisleStore } from '../../../../store/builder/useAisleStore';
|
||||
import * as THREE from 'three';
|
||||
import { useWallStore } from '../../../../store/builder/useWallStore';
|
||||
|
||||
const SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
||||
|
||||
|
@ -8,19 +9,27 @@ const CAN_SNAP = true; // Whether snapping is enabled or not
|
|||
|
||||
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
|
||||
const { aisles } = useAisleStore();
|
||||
const { walls } = useWallStore();
|
||||
|
||||
const getAllOtherPoints = useCallback(() => {
|
||||
const getAllOtherAislePoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
|
||||
return aisles.flatMap(aisle =>
|
||||
aisle.points.filter(point => point.uuid !== currentPoint.uuid)
|
||||
aisle.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
}, [aisles, currentPoint]);
|
||||
|
||||
const getAllOtherWallPoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
return walls.flatMap(wall =>
|
||||
wall.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
}, [walls, currentPoint]);
|
||||
|
||||
const checkSnapForAisle = useCallback((position: [number, number, number]) => {
|
||||
if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = getAllOtherPoints();
|
||||
const otherPoints = getAllOtherAislePoints();
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
|
||||
for (const point of otherPoints) {
|
||||
|
@ -33,9 +42,25 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
|
|||
}
|
||||
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherPoints]);
|
||||
}, [currentPoint, getAllOtherAislePoints]);
|
||||
|
||||
const checkSnapForWall = useCallback((position: [number, number, number]) => {
|
||||
if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = getAllOtherWallPoints();
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
if (distance <= SNAP_THRESHOLD && currentPoint.pointType === 'Wall') {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
}
|
||||
}
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherWallPoints]);
|
||||
|
||||
return {
|
||||
checkSnapForAisle,
|
||||
checkSnapForWall,
|
||||
};
|
||||
};
|
|
@ -8,6 +8,7 @@ import { useThree } from '@react-three/fiber';
|
|||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import { usePointSnapping } from './helpers/usePointSnapping';
|
||||
import { useAislePointSnapping } from './helpers/useAisleDragSnap';
|
||||
import { useWallStore } from '../../../store/builder/useWallStore';
|
||||
|
||||
function Point({ point }: { readonly point: Point }) {
|
||||
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
||||
|
@ -15,42 +16,75 @@ function Point({ point }: { readonly point: Point }) {
|
|||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setPosition, removePoint } = useAisleStore();
|
||||
const { setPosition: setAislePosition, removePoint: removeAislePoint } = useAisleStore();
|
||||
const { setPosition: setWallPosition, removePoint: removeWallPoint } = useWallStore();
|
||||
const { snapPosition } = useAislePointSnapping(point);
|
||||
const { checkSnapForAisle } = usePointSnapping({ uuid: point.uuid, pointType: point.pointType, position: point.position });
|
||||
const { checkSnapForAisle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
|
||||
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
|
||||
const { deletePointOrLine } = useDeletePointOrLine();
|
||||
|
||||
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
||||
const defaultInnerColor = Constants.pointConfig.defaultInnerColor;
|
||||
const defaultOuterColor = Constants.pointConfig.aisleOuterColor;
|
||||
const defaultDeleteColor = Constants.pointConfig.deleteColor;
|
||||
const colors = getColor(point);
|
||||
|
||||
function getColor(point: Point) {
|
||||
if (point.pointType === 'Aisle') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.aisleOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Floor') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.floorOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.wallOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Zone') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.zoneOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.defaultOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (materialRef.current && (toolMode === 'move' || deletePointOrLine)) {
|
||||
let innerColor;
|
||||
let outerColor;
|
||||
if (isHovered) {
|
||||
innerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor;
|
||||
outerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor;
|
||||
innerColor = deletePointOrLine ? colors.defaultDeleteColor : colors.defaultOuterColor;
|
||||
outerColor = deletePointOrLine ? colors.defaultDeleteColor : colors.defaultOuterColor;
|
||||
} else {
|
||||
innerColor = defaultInnerColor;
|
||||
outerColor = defaultOuterColor;
|
||||
innerColor = colors.defaultInnerColor;
|
||||
outerColor = colors.defaultOuterColor;
|
||||
}
|
||||
materialRef.current.uniforms.uInnerColor.value.set(innerColor);
|
||||
materialRef.current.uniforms.uOuterColor.value.set(outerColor);
|
||||
materialRef.current.uniformsNeedUpdate = true;
|
||||
} else if (materialRef.current && toolMode !== 'move') {
|
||||
materialRef.current.uniforms.uInnerColor.value.set(defaultInnerColor);
|
||||
materialRef.current.uniforms.uOuterColor.value.set(defaultOuterColor);
|
||||
materialRef.current.uniforms.uInnerColor.value.set(colors.defaultInnerColor);
|
||||
materialRef.current.uniforms.uOuterColor.value.set(colors.defaultOuterColor);
|
||||
materialRef.current.uniformsNeedUpdate = true;
|
||||
}
|
||||
}, [isHovered, defaultInnerColor, defaultOuterColor, toolMode, deletePointOrLine, defaultDeleteColor]);
|
||||
}, [isHovered, colors.defaultInnerColor, colors.defaultOuterColor, colors.defaultDeleteColor, toolMode, deletePointOrLine]);
|
||||
|
||||
const uniforms = useMemo(() => ({
|
||||
uOuterColor: { value: new THREE.Color(defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(defaultInnerColor) },
|
||||
}), [defaultInnerColor, defaultInnerColor]);
|
||||
uOuterColor: { value: new THREE.Color(colors.defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(colors.defaultInnerColor) },
|
||||
}), [colors.defaultInnerColor, colors.defaultOuterColor]);
|
||||
|
||||
const handleDrag = (point: Point) => {
|
||||
if (toolMode === 'move' && isHovered) {
|
||||
|
@ -63,7 +97,13 @@ function Point({ point }: { readonly point: Point }) {
|
|||
const aisleSnappedPosition = snapPosition(newPosition);
|
||||
const finalSnappedPosition = checkSnapForAisle(aisleSnappedPosition.position);
|
||||
|
||||
setPosition(point.uuid, finalSnappedPosition.position);
|
||||
setAislePosition(point.pointUuid, finalSnappedPosition.position);
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
if (position) {
|
||||
const newPosition: [number, number, number] = [position.x, position.y, position.z];
|
||||
|
||||
setWallPosition(point.pointUuid, newPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,16 +116,17 @@ function Point({ point }: { readonly point: Point }) {
|
|||
|
||||
const handlePointClick = (point: Point) => {
|
||||
if (deletePointOrLine) {
|
||||
const removedAisles = removePoint(point.uuid);
|
||||
if (point.pointType === 'Aisle') {
|
||||
const removedAisles = removeAislePoint(point.pointUuid);
|
||||
if (removedAisles.length > 0) {
|
||||
setHoveredPoint(null);
|
||||
console.log(removedAisles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (hoveredPoint && hoveredPoint.uuid !== point.uuid) {
|
||||
if (hoveredPoint && hoveredPoint.pointUuid !== point.pointUuid) {
|
||||
setIsHovered(false);
|
||||
}
|
||||
}, [hoveredPoint])
|
||||
|
@ -102,8 +143,8 @@ function Point({ point }: { readonly point: Point }) {
|
|||
onDragEnd={() => { handleDragEnd(point) }}
|
||||
>
|
||||
<mesh
|
||||
key={point.uuid}
|
||||
uuid={point.uuid}
|
||||
key={point.pointUuid}
|
||||
uuid={point.pointUuid}
|
||||
name='Aisle-Point'
|
||||
position={new THREE.Vector3(...point.position)}
|
||||
onClick={() => {
|
||||
|
@ -116,7 +157,7 @@ function Point({ point }: { readonly point: Point }) {
|
|||
}
|
||||
}}
|
||||
onPointerOut={() => {
|
||||
if (hoveredPoint && hoveredPoint.uuid === point.uuid) {
|
||||
if (hoveredPoint && hoveredPoint.pointUuid === point.pointUuid) {
|
||||
setHoveredPoint(null);
|
||||
}
|
||||
setIsHovered(false)
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import * as THREE from 'three';
|
||||
import * as Constants from '../../../types/world/worldConstants';
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import { useRef, useMemo } from 'react';
|
||||
|
||||
function ReferencePoint({ point }: { readonly point: Point }) {
|
||||
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
||||
|
||||
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
||||
const defaultInnerColor = Constants.pointConfig.defaultInnerColor;
|
||||
const defaultOuterColor = Constants.pointConfig.aisleOuterColor;
|
||||
const colors = {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.helperColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
};
|
||||
|
||||
const uniforms = useMemo(() => ({
|
||||
uOuterColor: { value: new THREE.Color(defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(defaultInnerColor) },
|
||||
}), [defaultOuterColor, defaultInnerColor]);
|
||||
uOuterColor: { value: new THREE.Color(colors.defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(colors.defaultInnerColor) },
|
||||
}), [colors.defaultInnerColor, colors.defaultOuterColor, colors.defaultDeleteColor]);
|
||||
|
||||
if (!point) {
|
||||
return null;
|
|
@ -0,0 +1,176 @@
|
|||
import { useMemo } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
export function useWallClassification(walls: Walls) {
|
||||
// Find all rooms from the given walls
|
||||
const findRooms = () => {
|
||||
if (walls.length < 3) return [];
|
||||
|
||||
const pointMap = new Map<string, Point>();
|
||||
const connections = new Map<string, string[]>();
|
||||
|
||||
// Build connection graph
|
||||
walls.forEach(wall => {
|
||||
const [p1, p2] = wall.points;
|
||||
if (!pointMap.has(p1.pointUuid)) pointMap.set(p1.pointUuid, p1);
|
||||
if (!pointMap.has(p2.pointUuid)) pointMap.set(p2.pointUuid, p2);
|
||||
|
||||
if (!connections.has(p1.pointUuid)) connections.set(p1.pointUuid, []);
|
||||
if (!connections.has(p2.pointUuid)) connections.set(p2.pointUuid, []);
|
||||
|
||||
connections.get(p1.pointUuid)?.push(p2.pointUuid);
|
||||
connections.get(p2.pointUuid)?.push(p1.pointUuid);
|
||||
});
|
||||
console.log('connections: ', connections);
|
||||
|
||||
const visited = new Set<string>();
|
||||
const rooms: Point[][] = [];
|
||||
|
||||
// Modified DFS to find all possible cycles
|
||||
const findAllCycles = (current: string, path: string[]) => {
|
||||
visited.add(current);
|
||||
path.push(current);
|
||||
|
||||
const neighbors = connections.get(current) || [];
|
||||
for (const neighbor of neighbors) {
|
||||
if (path.length > 2 && neighbor === path[0]) {
|
||||
// Found a cycle (potential room)
|
||||
const roomPoints = [...path, neighbor].map(uuid => pointMap.get(uuid)!);
|
||||
|
||||
// Check if this room is valid and not already found
|
||||
if (isValidRoom(roomPoints) && !roomAlreadyExists(rooms, roomPoints)) {
|
||||
rooms.push(roomPoints);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!path.includes(neighbor)) {
|
||||
findAllCycles(neighbor, [...path]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Start from each point to find all possible cycles
|
||||
for (const [pointUuid] of connections) {
|
||||
if (!visited.has(pointUuid)) {
|
||||
findAllCycles(pointUuid, []);
|
||||
}
|
||||
}
|
||||
|
||||
return rooms;
|
||||
};
|
||||
|
||||
// Helper function to check if room is valid
|
||||
const isValidRoom = (roomPoints: Point[]) => {
|
||||
if (roomPoints.length < 4) return false;
|
||||
const coordinates = roomPoints.map(p => [p.position[0], p.position[2]]);
|
||||
const polygon = turf.polygon([coordinates]);
|
||||
return turf.booleanValid(polygon);
|
||||
};
|
||||
|
||||
// Helper function to check for duplicate rooms
|
||||
const roomAlreadyExists = (existingRooms: Point[][], newRoom: Point[]) => {
|
||||
const newRoomIds = newRoom.map(p => p.pointUuid).sort().join('-');
|
||||
return existingRooms.some(room => {
|
||||
const roomIds = room.map(p => p.pointUuid).sort().join('-');
|
||||
return roomIds === newRoomIds;
|
||||
});
|
||||
};
|
||||
|
||||
const rooms = useMemo(() => findRooms(), [walls]);
|
||||
|
||||
// Modified to track all rooms a wall belongs to
|
||||
const getWallOrientation = (wall: Wall) => {
|
||||
const [p1, p2] = wall.points;
|
||||
const orientations: Array<{ isOutsideFacing: boolean }> = [];
|
||||
|
||||
for (const room of rooms) {
|
||||
for (let i = 0; i < room.length - 1; i++) {
|
||||
const roomP1 = room[i];
|
||||
const roomP2 = room[i + 1];
|
||||
|
||||
// Check both directions
|
||||
if ((p1.pointUuid === roomP1.pointUuid && p2.pointUuid === roomP2.pointUuid) ||
|
||||
(p1.pointUuid === roomP2.pointUuid && p2.pointUuid === roomP1.pointUuid)) {
|
||||
|
||||
const roomPoints = room.map(p => new THREE.Vector3(p.position[0], 0, p.position[2]));
|
||||
const centroid = new THREE.Vector3();
|
||||
roomPoints.forEach(p => centroid.add(p));
|
||||
centroid.divideScalar(roomPoints.length);
|
||||
|
||||
const wallVector = new THREE.Vector3(
|
||||
p2.position[0] - p1.position[0],
|
||||
0,
|
||||
p2.position[2] - p1.position[2]
|
||||
);
|
||||
|
||||
// Normal depends on wall direction
|
||||
let normal = new THREE.Vector3(-wallVector.z, 0, wallVector.x).normalize();
|
||||
if (p1.pointUuid === roomP2.pointUuid) {
|
||||
normal = new THREE.Vector3(wallVector.z, 0, -wallVector.x).normalize();
|
||||
}
|
||||
|
||||
const testPoint = new THREE.Vector3(
|
||||
(p1.position[0] + p2.position[0]) / 2 + normal.x,
|
||||
0,
|
||||
(p1.position[2] + p2.position[2]) / 2 + normal.z
|
||||
);
|
||||
|
||||
const pointInside = turf.booleanPointInPolygon(
|
||||
turf.point([testPoint.x, testPoint.z]),
|
||||
turf.polygon([room.map(p => [p.position[0], p.position[2]])])
|
||||
);
|
||||
|
||||
orientations.push({
|
||||
isOutsideFacing: !pointInside
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isRoomWall: orientations.length > 0,
|
||||
orientations // Now tracks all orientations for walls in multiple rooms
|
||||
};
|
||||
};
|
||||
|
||||
const getWallMaterialSide = (wall: Wall) => {
|
||||
const orientation = getWallOrientation(wall);
|
||||
|
||||
if (!orientation.isRoomWall) {
|
||||
return { front: 'inside', back: 'inside' }; // Both sides same for segment walls
|
||||
}
|
||||
|
||||
// For walls in multiple rooms, we need to determine which side faces which room
|
||||
if (orientation.orientations.length === 2) {
|
||||
// Wall is between two rooms - one side faces each room's interior
|
||||
return {
|
||||
front: 'inside',
|
||||
back: 'inside'
|
||||
};
|
||||
} else if (orientation.orientations.length === 1) {
|
||||
// Wall is part of only one room (exterior wall)
|
||||
return orientation.orientations[0].isOutsideFacing
|
||||
? { front: 'outside', back: 'inside' }
|
||||
: { front: 'inside', back: 'outside' };
|
||||
}
|
||||
|
||||
// Default case (shouldn't normally happen)
|
||||
return { front: 'inside', back: 'inside' };
|
||||
};
|
||||
|
||||
// Rest of the functions remain the same
|
||||
const getWallType = (wall: Wall) => getWallOrientation(wall).isRoomWall ? 'room' : 'segment';
|
||||
const isRoomWall = (wall: Wall) => getWallOrientation(wall).isRoomWall;
|
||||
const isSegmentWall = (wall: Wall) => !getWallOrientation(wall).isRoomWall;
|
||||
|
||||
return {
|
||||
rooms,
|
||||
getWallType,
|
||||
isRoomWall,
|
||||
isSegmentWall,
|
||||
getWallMaterialSide,
|
||||
findRooms,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
function useWallGeometry(wallLength: number, wallHeight: number, wallThickness: number) {
|
||||
return useMemo(() => {
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
|
||||
const halfLength = wallLength / 2;
|
||||
const halfThickness = wallThickness / 2;
|
||||
const height = wallHeight;
|
||||
|
||||
const vertices = [
|
||||
-halfLength, -height / 2, halfThickness,
|
||||
-halfLength, height / 2, halfThickness,
|
||||
halfLength, height / 2, halfThickness,
|
||||
halfLength, -height / 2, halfThickness,
|
||||
-halfLength, -height / 2, -halfThickness,
|
||||
-halfLength, height / 2, -halfThickness,
|
||||
halfLength, height / 2, -halfThickness,
|
||||
halfLength, -height / 2, -halfThickness,
|
||||
-halfLength, height / 2, halfThickness,
|
||||
-halfLength, height / 2, -halfThickness,
|
||||
halfLength, height / 2, -halfThickness,
|
||||
halfLength, height / 2, halfThickness,
|
||||
-halfLength, -height / 2, halfThickness,
|
||||
-halfLength, -height / 2, -halfThickness,
|
||||
halfLength, -height / 2, -halfThickness,
|
||||
halfLength, -height / 2, halfThickness,
|
||||
];
|
||||
|
||||
const indices = [
|
||||
0, 1, 2, 0, 2, 3,
|
||||
4, 6, 5, 4, 7, 6,
|
||||
0, 4, 5, 0, 5, 1,
|
||||
3, 2, 6, 3, 6, 7,
|
||||
8, 9, 10, 8, 10, 11,
|
||||
12, 14, 13, 12, 15, 14
|
||||
];
|
||||
|
||||
geometry.setIndex(indices);
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
|
||||
geometry.setAttribute('uv', new THREE.Float32BufferAttribute([
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0
|
||||
], 2));
|
||||
|
||||
geometry.computeVertexNormals();
|
||||
|
||||
geometry.addGroup(0, 6, 0); // Front
|
||||
geometry.addGroup(6, 6, 1); // Back
|
||||
geometry.addGroup(12, 6, 2); // Left
|
||||
geometry.addGroup(18, 6, 3); // Right
|
||||
geometry.addGroup(24, 6, 4); // Top
|
||||
geometry.addGroup(30, 6, 5); // Bottom
|
||||
|
||||
return geometry;
|
||||
}, [wallLength, wallHeight, wallThickness]);
|
||||
}
|
||||
|
||||
export default useWallGeometry;
|
|
@ -0,0 +1,92 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo } from 'react';
|
||||
import * as Constants from '../../../../../types/world/worldConstants';
|
||||
|
||||
import insideMaterial from '../../../../../assets/textures/floor/wall-tex.png';
|
||||
import outsideMaterial from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
||||
import useWallGeometry from './helpers/useWallGeometry';
|
||||
import { useWallStore } from '../../../../../store/builder/useWallStore';
|
||||
import { useWallClassification } from './helpers/useWallClassification';
|
||||
|
||||
function Wall({ wall }: { readonly wall: Wall }) {
|
||||
const { walls } = useWallStore();
|
||||
const { getWallMaterialSide, isRoomWall, rooms } = useWallClassification(walls);
|
||||
console.log('rooms: ', rooms);
|
||||
const materialSide = getWallMaterialSide(wall);
|
||||
const [startPoint, endPoint] = wall.points;
|
||||
|
||||
const startX = startPoint.position[0];
|
||||
const startZ = startPoint.position[2];
|
||||
const endX = endPoint.position[0];
|
||||
const endZ = endPoint.position[2];
|
||||
|
||||
const wallLength = Math.sqrt((endX - startX) ** 2 + (endZ - startZ) ** 2);
|
||||
const angle = Math.atan2(endZ - startZ, endX - startX);
|
||||
|
||||
const centerX = (startX + endX) / 2;
|
||||
const centerZ = (startZ + endZ) / 2;
|
||||
const centerY = wall.wallHeight / 2;
|
||||
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
|
||||
const [insideWallTexture, outsideWallTexture] = useMemo(() => {
|
||||
const inside = textureLoader.load(insideMaterial);
|
||||
inside.wrapS = inside.wrapT = THREE.RepeatWrapping;
|
||||
inside.repeat.set(wallLength / 10, wall.wallHeight / 10);
|
||||
inside.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
const outside = textureLoader.load(outsideMaterial);
|
||||
outside.wrapS = outside.wrapT = THREE.RepeatWrapping;
|
||||
outside.repeat.set(wallLength / 10, wall.wallHeight / 10);
|
||||
outside.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
return [inside, outside];
|
||||
}, [wallLength, wall.wallHeight, textureLoader]);
|
||||
|
||||
const materials = useMemo(() => {
|
||||
// For segment walls (not in a room), use inside material on both sides
|
||||
const frontMaterial = isRoomWall(wall)
|
||||
? (materialSide.front === 'inside' ? insideWallTexture : outsideWallTexture)
|
||||
: insideWallTexture;
|
||||
|
||||
const backMaterial = isRoomWall(wall)
|
||||
? (materialSide.back === 'inside' ? insideWallTexture : outsideWallTexture)
|
||||
: insideWallTexture;
|
||||
|
||||
return [
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: Constants.wallConfig.defaultColor,
|
||||
side: THREE.DoubleSide,
|
||||
map: frontMaterial
|
||||
}),
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: Constants.wallConfig.defaultColor,
|
||||
side: THREE.DoubleSide,
|
||||
map: backMaterial
|
||||
}),
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }), // Left
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }), // Right
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }), // Top
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }) // Bottom
|
||||
];
|
||||
}, [insideWallTexture, outsideWallTexture, materialSide, isRoomWall, wall]);
|
||||
|
||||
const geometry = useWallGeometry(wallLength, wall.wallHeight, wall.wallThickness);
|
||||
|
||||
return (
|
||||
<group
|
||||
name={`Wall-${wall.wallUuid}`}
|
||||
userData={wall}
|
||||
position={[centerX, centerY, centerZ]}
|
||||
rotation={[0, -angle, 0]}
|
||||
>
|
||||
<mesh geometry={geometry}>
|
||||
{materials.map((material, index) => (
|
||||
<primitive key={index} object={material} attach={`material-${index}`} />
|
||||
))}
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default Wall;
|
|
@ -0,0 +1,16 @@
|
|||
import { useToggleView } from "../../../../../store/builder/store";
|
||||
import Wall from "./wall"
|
||||
|
||||
function WallInstance({ wall }: { readonly wall: Wall }) {
|
||||
const { toggleView } = useToggleView();
|
||||
|
||||
return (
|
||||
<>
|
||||
{!toggleView && (
|
||||
<Wall wall={wall} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallInstance
|
|
@ -0,0 +1,42 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { useWallStore } from '../../../../store/builder/useWallStore'
|
||||
import WallInstance from './instance/wallInstance';
|
||||
import Line from '../../line/line';
|
||||
import Point from '../../point/point';
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import { Geometry } from '@react-three/csg';
|
||||
|
||||
function WallInstances() {
|
||||
const { walls } = useWallStore();
|
||||
const { toggleView } = useToggleView();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('walls: ', walls);
|
||||
}, [walls])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<Geometry computeVertexNormals useGroups>
|
||||
{walls.map((wall) => (
|
||||
<WallInstance key={wall.wallUuid} wall={wall} />
|
||||
))}
|
||||
</Geometry>
|
||||
|
||||
|
||||
{toggleView && (
|
||||
<>
|
||||
{walls.map((wall) => (
|
||||
<React.Fragment key={wall.wallUuid}>
|
||||
<Point point={wall.points[0]} />
|
||||
<Line points={wall.points} />
|
||||
<Point point={wall.points[1]} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallInstances
|
|
@ -0,0 +1,128 @@
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
|
||||
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
||||
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import ReferenceLine from '../../line/reference/referenceLine';
|
||||
|
||||
interface ReferenceWallProps {
|
||||
tempPoints: Point[];
|
||||
}
|
||||
|
||||
function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
const { wallHeight, wallThickness, setSnappedPosition, setSnappedPoint } = useBuilderStore();
|
||||
|
||||
const { pointer, raycaster, camera } = useThree();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
const finalPosition = useRef<[number, number, number] | null>(null);
|
||||
|
||||
const [tempWall, setTempWall] = useState<Wall | null>(null);
|
||||
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
|
||||
|
||||
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null);
|
||||
const { checkSnapForWall } = usePointSnapping({ uuid: 'temp-wall', pointType: 'Wall', position: directionalSnap.position || [0, 0, 0] });
|
||||
|
||||
useFrame(() => {
|
||||
if (toolMode === 'Wall' && toggleView && tempPoints.length === 1) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (intersectionPoint) {
|
||||
const snapped = checkSnapForWall([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
finalPosition.current = snapped.position;
|
||||
setSnappedPosition(snapped.position);
|
||||
setSnappedPoint(snapped.snappedPoint);
|
||||
} else if (directionalSnap.isSnapped) {
|
||||
finalPosition.current = directionalSnap.position;
|
||||
setSnappedPosition(directionalSnap.position);
|
||||
setSnappedPoint(null);
|
||||
} else {
|
||||
finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z];
|
||||
setSnappedPosition(null);
|
||||
setSnappedPoint(null);
|
||||
}
|
||||
|
||||
if (!finalPosition.current) return;
|
||||
|
||||
const wallPoints: [Point, Point] = [
|
||||
tempPoints[0],
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Wall',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer,
|
||||
}
|
||||
];
|
||||
|
||||
setTempWall({
|
||||
wallUuid: 'temp-wall',
|
||||
points: wallPoints,
|
||||
outSideMaterial: 'default',
|
||||
inSideMaterial: 'default',
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
})
|
||||
|
||||
}
|
||||
} else if (tempWall !== null) {
|
||||
setTempWall(null);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTempWall(null);
|
||||
}, [toolMode, toggleView, tempPoints.length, wallHeight, wallThickness]);
|
||||
|
||||
if (!tempWall) return null;
|
||||
|
||||
const renderWall = () => {
|
||||
return (
|
||||
<ReferenceLine points={tempWall.points} />
|
||||
)
|
||||
};
|
||||
|
||||
const textPosition = new THREE.Vector3().addVectors(new THREE.Vector3(...tempWall.points[0].position), new THREE.Vector3(...tempWall.points[1].position)).divideScalar(2);
|
||||
const distance = new THREE.Vector3(...tempWall.points[0].position).distanceTo(new THREE.Vector3(...tempWall.points[1].position));
|
||||
|
||||
const rendertext = () => {
|
||||
return (
|
||||
<>
|
||||
{toggleView && (
|
||||
<Html
|
||||
key={tempWall.wallUuid}
|
||||
userData={tempWall}
|
||||
position={[textPosition.x, 1, textPosition.z]}
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div className={`distance ${tempWall.wallUuid}`}>{distance.toFixed(2)} m</div>
|
||||
</Html>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<group name="Wall-Reference-Group">
|
||||
{renderWall()}
|
||||
{rendertext()}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReferenceWall;
|
|
@ -0,0 +1,155 @@
|
|||
import * as THREE from 'three'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useWallStore } from '../../../../store/builder/useWallStore';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
import ReferenceWall from './referenceWall';
|
||||
|
||||
function WallCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { addWall, getWallPointById } = useWallStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { wallThickness, wallHeight, snappedPosition, snappedPoint } = useBuilderStore();
|
||||
|
||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown.current = true;
|
||||
drag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseClick = () => {
|
||||
if (drag.current || !toggleView) return;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
if (!position) return;
|
||||
|
||||
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point');
|
||||
|
||||
const newPoint: Point = {
|
||||
pointUuid: THREE.MathUtils.generateUUID(),
|
||||
pointType: 'Wall',
|
||||
position: [position.x, position.y, position.z],
|
||||
layer: activeLayer
|
||||
};
|
||||
|
||||
if (snappedPosition && snappedPoint) {
|
||||
newPoint.pointUuid = snappedPoint.pointUuid;
|
||||
newPoint.position = snappedPosition;
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
}
|
||||
|
||||
if (intersects && !snappedPoint) {
|
||||
const point = getWallPointById(intersects.object.uuid);
|
||||
if (point) {
|
||||
newPoint.pointUuid = point.pointUuid;
|
||||
newPoint.position = point.position;
|
||||
newPoint.layer = point.layer;
|
||||
}
|
||||
}
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const wall: Wall = {
|
||||
wallUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
outSideMaterial: 'default',
|
||||
inSideMaterial: 'default',
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight
|
||||
};
|
||||
addWall(wall);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const onContext = (event: any) => {
|
||||
event.preventDefault();
|
||||
if (isCreating) {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (toolMode === "Wall" && toggleView) {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("click", onMouseClick);
|
||||
canvasElement.addEventListener("contextmenu", onContext);
|
||||
} else {
|
||||
if (tempPoints.length > 0 || isCreating) {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addWall, getWallPointById, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView &&
|
||||
<>
|
||||
<group name='Aisle-Reference-Points-Group'>
|
||||
{tempPoints.map((point) => (
|
||||
<ReferencePoint key={point.pointUuid} point={point} />
|
||||
))}
|
||||
</group>
|
||||
|
||||
{tempPoints.length > 0 &&
|
||||
<ReferenceWall tempPoints={tempPoints} />
|
||||
}
|
||||
</>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallCreator
|
|
@ -0,0 +1,16 @@
|
|||
import WallCreator from './wallCreator/wallCreator'
|
||||
import WallInstances from './Instances/wallInstances'
|
||||
|
||||
function WallGroup() {
|
||||
return (
|
||||
<>
|
||||
|
||||
<WallCreator />
|
||||
|
||||
<WallInstances />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallGroup
|
|
@ -143,7 +143,7 @@ const CamModelsGroup = () => {
|
|||
);
|
||||
});
|
||||
|
||||
socket.on("cameraUpdateResponse", (data: any) => {
|
||||
socket.on("v1:camera:Response:update", (data: any) => {
|
||||
if (
|
||||
!groupRef.current ||
|
||||
socket.id === data.socketId ||
|
||||
|
@ -188,7 +188,7 @@ const CamModelsGroup = () => {
|
|||
return () => {
|
||||
socket.off("userConnectResponse");
|
||||
socket.off("userDisConnectResponse");
|
||||
socket.off("cameraUpdateResponse");
|
||||
socket.off("v1:camera:Response:update");
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [email, loader, navigate, setActiveUsers, socket]);
|
||||
|
|
|
@ -31,6 +31,7 @@ import RemoveConnectedLines from "../../builder/geomentries/lines/removeConnecte
|
|||
import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility";
|
||||
import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils";
|
||||
import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useAssetsStore } from "../../../store/builder/useAssetStore";
|
||||
import { useEventsStore } from "../../../store/simulation/useEventsStore";
|
||||
import { useProductStore } from "../../../store/simulation/useProductStore";
|
||||
|
@ -61,6 +62,7 @@ export default function SocketResponses({
|
|||
const { setNewLines } = useNewLines();
|
||||
const { zones, setZones } = useZones();
|
||||
const { zonePoints, setZonePoints } = useZonePoints();
|
||||
const { projectId } = useParams();
|
||||
const { addAsset, updateAsset, removeAsset } = useAssetsStore();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -70,34 +72,32 @@ export default function SocketResponses({
|
|||
if (!socket) return;
|
||||
|
||||
socket.on("cameraCreateResponse", (data: any) => {
|
||||
// console.log('data: ', data);
|
||||
//
|
||||
});
|
||||
|
||||
socket.on("userConnectRespones", (data: any) => {
|
||||
// console.log('data: ', data);
|
||||
//
|
||||
});
|
||||
|
||||
socket.on("userDisConnectRespones", (data: any) => {
|
||||
// console.log('data: ', data);
|
||||
//
|
||||
});
|
||||
|
||||
socket.on("cameraUpdateResponse", (data: any) => {
|
||||
// console.log('data: ', data);
|
||||
socket.on("v1:camera:Response:update", (data: any) => {
|
||||
//
|
||||
});
|
||||
|
||||
socket.on("EnvironmentUpdateResponse", (data: any) => {
|
||||
// console.log('data: ', data);
|
||||
//
|
||||
});
|
||||
|
||||
socket.on("model-asset:response:updates", async (data: any) => {
|
||||
// console.log('data: ', data);
|
||||
socket.on("v1:model-asset:response:add", async (data: any) => {
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
if (organization !== data.organization) {
|
||||
return;
|
||||
}
|
||||
console.log('data.data: ', data);
|
||||
if (data.message === "Model created successfully") {
|
||||
try {
|
||||
|
||||
|
@ -148,7 +148,7 @@ export default function SocketResponses({
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("model-asset:response:updates", (data: any) => {
|
||||
socket.on("v1:model-asset:response:delete", (data: any) => {
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -171,7 +171,8 @@ export default function SocketResponses({
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("Line:response:update", (data: any) => {
|
||||
socket.on("v1:Line:response:update", (data: any) => {
|
||||
console.log('data: ', data);
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -212,7 +213,8 @@ export default function SocketResponses({
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("Line:response:delete", (data: any) => {
|
||||
socket.on("v1:Line:response:delete", (data: any) => {
|
||||
console.log('data: ', data);
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -293,7 +295,8 @@ export default function SocketResponses({
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("Line:response:delete:point", (data: any) => {
|
||||
socket.on("v1:Line:response:delete:point", (data: any) => {
|
||||
console.log('datapoint: ', data);
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -333,7 +336,8 @@ export default function SocketResponses({
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("Line:response:delete:layer", async (data: any) => {
|
||||
socket.on("v1:Line:response:delete:layer", async (data: any) => {
|
||||
console.log('data: ', data);
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -440,7 +444,7 @@ export default function SocketResponses({
|
|||
floorGroup.current?.remove(meshToRemove);
|
||||
}
|
||||
|
||||
const zonesData = await getZonesApi(organization);
|
||||
const zonesData = await getZonesApi(organization,projectId);
|
||||
const highestLayer = Math.max(
|
||||
1,
|
||||
lines.current.reduce(
|
||||
|
@ -448,7 +452,7 @@ export default function SocketResponses({
|
|||
Math.max(maxLayer, segment.layer || 0),
|
||||
0
|
||||
),
|
||||
zonesData.data.reduce(
|
||||
zonesData.reduce(
|
||||
(maxLayer: number, zone: any) =>
|
||||
Math.max(maxLayer, zone.layer || 0),
|
||||
0
|
||||
|
@ -471,7 +475,7 @@ export default function SocketResponses({
|
|||
const organization = email!.split("@")[1].split(".")[0];
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
||||
socket.on("wallItemsDeleteResponse", (data: any) => {
|
||||
socket.on("v1:wallItem:Response:Delete", (data: any) => {
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -501,7 +505,8 @@ export default function SocketResponses({
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("wallItemsUpdateResponse", (data: any) => {
|
||||
socket.on("v1:wallItems:Response:Update", (data: any) => {
|
||||
//
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -544,7 +549,7 @@ export default function SocketResponses({
|
|||
THREE.Cache.add(data.data.modelfileID, gltf);
|
||||
await handleModelLoad(gltf);
|
||||
} catch (error) {
|
||||
console.error('Failed to cache model:', error);
|
||||
|
||||
handleModelLoad(gltf);
|
||||
}
|
||||
});
|
||||
|
@ -627,8 +632,8 @@ export default function SocketResponses({
|
|||
});
|
||||
|
||||
return () => {
|
||||
socket.off("wallItemsDeleteResponse");
|
||||
socket.off("wallItemsUpdateResponse");
|
||||
socket.off("v1:wallItem:Response:Delete");
|
||||
socket.off("v1:wallItems:Response:Update");
|
||||
};
|
||||
}, [wallItems]);
|
||||
|
||||
|
@ -663,7 +668,9 @@ export default function SocketResponses({
|
|||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
socket.on("Line:response:create", async (data: any) => {
|
||||
socket.on("v1:Line:response:create", async (data: any) => {
|
||||
console.log('data: ', data);
|
||||
//
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -739,7 +746,7 @@ export default function SocketResponses({
|
|||
);
|
||||
lines.current.push(line);
|
||||
|
||||
const zonesData = await getZonesApi(organization);
|
||||
const zonesData = await getZonesApi(organization,projectId);
|
||||
const highestLayer = Math.max(
|
||||
1,
|
||||
lines.current.reduce(
|
||||
|
@ -747,7 +754,7 @@ export default function SocketResponses({
|
|||
Math.max(maxLayer, segment.layer || 0),
|
||||
0
|
||||
),
|
||||
zonesData.data.reduce(
|
||||
zonesData.reduce(
|
||||
(maxLayer: number, zone: any) =>
|
||||
Math.max(maxLayer, zone.layer || 0),
|
||||
0
|
||||
|
@ -771,7 +778,7 @@ export default function SocketResponses({
|
|||
});
|
||||
|
||||
return () => {
|
||||
socket.off("Line:response:create");
|
||||
socket.off("v1:Line:response:create");
|
||||
};
|
||||
}, [socket, activeLayer]);
|
||||
|
||||
|
@ -780,7 +787,8 @@ export default function SocketResponses({
|
|||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
socket.on("zone:response:updates", (data: any) => {
|
||||
socket.on("v1:zone:response:updates", (data: any) => {
|
||||
console.log('data: ', data);
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -818,14 +826,15 @@ export default function SocketResponses({
|
|||
|
||||
if (data.message === "zone updated") {
|
||||
const updatedZones = zones.map((zone: any) =>
|
||||
zone.zoneId === data.data.zoneId ? data.data : zone
|
||||
zone.zoneUuid === data.data.zoneUuid ? data.data : zone
|
||||
);
|
||||
setZones(updatedZones);
|
||||
setUpdateScene(true);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("zone:response:delete", (data: any) => {
|
||||
socket.on("v1:zone:response:delete", (data: any) => {
|
||||
console.log('data: ', data);
|
||||
if (socket.id === data.socketId) {
|
||||
return;
|
||||
}
|
||||
|
@ -834,12 +843,12 @@ export default function SocketResponses({
|
|||
}
|
||||
if (data.message === "zone deleted") {
|
||||
const updatedZones = zones.filter(
|
||||
(zone: any) => zone.zoneId !== data.data.zoneId
|
||||
(zone: any) => zone.zoneUuid !== data.data.zoneUuid
|
||||
);
|
||||
setZones(updatedZones);
|
||||
|
||||
const zoneIndex = zones.findIndex(
|
||||
(zone: any) => zone.zoneId === data.data.zoneId
|
||||
(zone: any) => zone.zoneUuid === data.data.zoneUuid
|
||||
);
|
||||
if (zoneIndex !== -1) {
|
||||
const updatedzonePoints = zonePoints.filter(
|
||||
|
@ -869,8 +878,8 @@ export default function SocketResponses({
|
|||
});
|
||||
|
||||
return () => {
|
||||
socket.off("zone:response:updates");
|
||||
socket.off("zone:response:delete");
|
||||
socket.off("v1:zone:response:updates");
|
||||
socket.off("v1:zone:response:delete");
|
||||
};
|
||||
}, [socket, zones, zonePoints]);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useToggleView } from "../../../store/builder/store";
|
|||
import { useThree } from "@react-three/fiber";
|
||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
export default function SwitchView() {
|
||||
const { toggleView } = useToggleView();
|
||||
|
@ -13,6 +14,7 @@ export default function SwitchView() {
|
|||
const orthoCamera = useRef<THREE.OrthographicCamera | null>(null);
|
||||
orthoCamera.current = new THREE.OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 0.01, 1000);
|
||||
perspectiveCamera.current = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000);
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (!perspectiveCamera.current || !orthoCamera.current) return;
|
||||
|
@ -38,7 +40,7 @@ export default function SwitchView() {
|
|||
try {
|
||||
const email = localStorage.getItem('email');
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
getCamera(organization, localStorage.getItem('userId')!).then((data) => {
|
||||
getCamera(organization, localStorage.getItem('userId')!,projectId).then((data) => {
|
||||
if (data && data.position && data.target) {
|
||||
state.controls?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||
state.controls?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||
|
|
|
@ -5,7 +5,8 @@ export default function updateCamPosition(
|
|||
controls: any,
|
||||
socket: Socket,
|
||||
position: THREE.Vector3,
|
||||
rotation: THREE.Euler
|
||||
rotation: THREE.Euler,
|
||||
projectId?:string
|
||||
) {
|
||||
if (!controls.current) return;
|
||||
const target = controls.current.getTarget(new THREE.Vector3());
|
||||
|
@ -19,6 +20,7 @@ export default function updateCamPosition(
|
|||
target: new THREE.Vector3(target.x, 0, target.z),
|
||||
rotation: new THREE.Vector3(rotation.x, rotation.y, rotation.z),
|
||||
socketId: socket.id,
|
||||
projectId
|
||||
};
|
||||
socket.emit("v1:Camera:set", camData);
|
||||
localStorage.setItem("cameraPosition", JSON.stringify(position));
|
||||
|
|
|
@ -11,6 +11,7 @@ import CamMode from "../camera/camMode";
|
|||
import SwitchView from "../camera/switchView";
|
||||
import SelectionControls from "./selectionControls/selectionControls";
|
||||
import TransformControl from "./transformControls/transformControls";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
export default function Controls() {
|
||||
const controlsRef = useRef<CameraControls>(null);
|
||||
|
@ -19,6 +20,8 @@ export default function Controls() {
|
|||
const { resetCamera, setResetCamera } = useResetCamera();
|
||||
const { socket } = useSocketStore();
|
||||
const state = useThree();
|
||||
const { projectId } = useParams();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (controlsRef.current) {
|
||||
|
@ -27,7 +30,10 @@ export default function Controls() {
|
|||
}
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
getCamera(organization, localStorage.getItem("userId")!).then((data) => {
|
||||
const userId = localStorage.getItem("userId")!;
|
||||
|
||||
getCamera(organization, userId, projectId).then((data) => {
|
||||
// console.log('data: ', data);
|
||||
if (data && data.position && data.target) {
|
||||
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||
|
@ -50,14 +56,16 @@ export default function Controls() {
|
|||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
const camData = {
|
||||
organization: organization,
|
||||
userId: localStorage.getItem('userId')!,
|
||||
userId: userId,
|
||||
position: new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition),
|
||||
target: new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget),
|
||||
rotation: new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation),
|
||||
socketId: socket.id
|
||||
socketId: socket.id,
|
||||
projectId
|
||||
};
|
||||
socket.emit('v1:Camera:set', camData)
|
||||
|
||||
|
@ -75,7 +83,7 @@ export default function Controls() {
|
|||
if (hasInteracted && controlsRef.current && state.camera.position && !toggleView) {
|
||||
const position = state.camera.position;
|
||||
if (position.x === 0 && position.y === 0 && position.z === 0) return;
|
||||
updateCamPosition(controlsRef, socket, position, state.camera.rotation);
|
||||
updateCamPosition(controlsRef, socket, position, state.camera.rotation, projectId);
|
||||
stopInterval();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -33,8 +33,13 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) =>
|
|||
if (selectedAssets.length === 0) return [];
|
||||
|
||||
if (isPerAsset) {
|
||||
return selectedAssets.map((obj: any) => {
|
||||
const box = new THREE.Box3().setFromObject(obj.clone());
|
||||
return selectedAssets.map((obj: THREE.Object3D) => {
|
||||
const position = obj.position;
|
||||
const rotation = obj.getWorldQuaternion(new THREE.Quaternion());
|
||||
const clone = obj.clone();
|
||||
clone.position.set(0, 0, 0);
|
||||
clone.rotation.set(0, 0, 0);
|
||||
const box = new THREE.Box3().setFromObject(clone);
|
||||
const size = new THREE.Vector3();
|
||||
const center = new THREE.Vector3();
|
||||
box.getSize(size);
|
||||
|
@ -46,7 +51,8 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) =>
|
|||
|
||||
return {
|
||||
points: getBoxLines(min, max),
|
||||
position: center.toArray(),
|
||||
position: [position.x, center.y, position.z],
|
||||
rotation: rotation.toArray(),
|
||||
size: size.toArray(),
|
||||
};
|
||||
});
|
||||
|
@ -66,6 +72,7 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) =>
|
|||
{
|
||||
points: getBoxLines(min, max),
|
||||
position: center.toArray(),
|
||||
rotation: [0, 0, 0, 1],
|
||||
size: size.toArray(),
|
||||
},
|
||||
];
|
||||
|
@ -75,7 +82,10 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) =>
|
|||
return (
|
||||
<>
|
||||
{boxes.map((box: any, index: number) => (
|
||||
<group key={index} name="SelectionGroupBoundingBoxLine">
|
||||
<group
|
||||
key={index}
|
||||
name="SelectionGroupBoundingBoxLine"
|
||||
>
|
||||
<Line
|
||||
name="SelectionGroupBoundingBox"
|
||||
depthWrite={false}
|
||||
|
@ -83,12 +93,15 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) =>
|
|||
color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"}
|
||||
lineWidth={2.7}
|
||||
segments
|
||||
position={[box.position[0], 0, box.position[2]]}
|
||||
quaternion={new THREE.Quaternion(...box.rotation)}
|
||||
/>
|
||||
<mesh
|
||||
name="SelectionGroupBoundingLine"
|
||||
ref={index === 0 ? boundingBoxRef : null}
|
||||
visible={false}
|
||||
position={box.position}
|
||||
quaternion={new THREE.Quaternion(...box.rotation)}
|
||||
>
|
||||
<boxGeometry args={box.size} />
|
||||
<meshBasicMaterial />
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../st
|
|||
import * as Types from "../../../../types/world/worldTypes";
|
||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useAssetsStore } from "../../../../store/builder/useAssetStore";
|
||||
|
||||
const CopyPasteControls = ({
|
||||
|
@ -27,6 +28,7 @@ const CopyPasteControls = ({
|
|||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const { socket } = useSocketStore();
|
||||
const { addEvent } = useEventsStore();
|
||||
const { projectId } = useParams();
|
||||
const { assets, addAsset } = useAssetsStore();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -331,7 +333,7 @@ const CopyPasteControls = ({
|
|||
|
||||
newFloorItem.eventData = eventData;
|
||||
|
||||
//REST
|
||||
const userId = localStorage.getItem("userId"); //REST
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
|
@ -357,9 +359,11 @@ const CopyPasteControls = ({
|
|||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
eventData: eventData,
|
||||
userId,
|
||||
projectId
|
||||
};
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
obj.userData = {
|
||||
name: newFloorItem.modelName,
|
||||
|
@ -399,7 +403,7 @@ const CopyPasteControls = ({
|
|||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const userId = localStorage.getItem("userId");
|
||||
const data = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
|
@ -410,9 +414,11 @@ const CopyPasteControls = ({
|
|||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
projectId,
|
||||
userId
|
||||
};
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue