1. Integerated DashBoard, #97

Merged
Marikannan merged 31 commits from v3-wall into main 2025-06-04 12:09:09 +00:00
190 changed files with 6650 additions and 2152 deletions
app
package-lock.jsonpackage.json
src
app.tsx
assets/image
components
modules

83
app/package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 (

View File

@ -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;

View File

@ -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,
};
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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
);
//

View File

@ -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
);
};

View File

@ -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);
}
}
}
};

View File

@ -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(

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 />

View File

@ -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>
);

View File

@ -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"

View File

@ -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
})
}
/>

View File

@ -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`);
}

View File

@ -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 (

View File

@ -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,
};

View File

@ -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

View File

@ -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}`;

View File

@ -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>

View File

@ -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} />
)}
</>
);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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>
);
}

View File

@ -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);
}
};

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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>
</>

View File

@ -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;

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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)
})
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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])

View File

@ -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);

View File

@ -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 //////////

View File

@ -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) => {

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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));

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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 };
}

View File

@ -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,
};
};

View File

@ -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)

View File

@ -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;

View File

@ -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,
};
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,16 @@
import WallCreator from './wallCreator/wallCreator'
import WallInstances from './Instances/wallInstances'
function WallGroup() {
return (
<>
<WallCreator />
<WallInstances />
</>
)
}
export default WallGroup

View File

@ -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]);

View File

@ -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]);

View File

@ -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);

View File

@ -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));

View File

@ -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();
}
};

View File

@ -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 />

View File

@ -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