Compare commits
5 Commits
dev-r3f-wa
...
merge/wall
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ebd16db83 | |||
| 898179c2c1 | |||
| 84101905ff | |||
| 3a70a13c94 | |||
| e589cb22b5 |
@@ -216,139 +216,3 @@ const DashboardProjects: React.FC = () => {
|
|||||||
export default DashboardProjects;
|
export default DashboardProjects;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// const MyProjects = () => {
|
|
||||||
// const { projectSocket } = useSocketStore();
|
|
||||||
// const { userId, organization } = getUserData();
|
|
||||||
|
|
||||||
// const fetchAllProjects = async () => {
|
|
||||||
// try {
|
|
||||||
// const projects = await getAllProjects(userId, organization);
|
|
||||||
// if (!projects || !projects.Projects) return;
|
|
||||||
|
|
||||||
// if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) {
|
|
||||||
// setWorkspaceProjects(projects);
|
|
||||||
// }
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Error fetching projects:", error);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// const handleDeleteProject = async (projectId: any) => {
|
|
||||||
// try {
|
|
||||||
// // const deletedProject = await deleteProject(
|
|
||||||
// // projectId,
|
|
||||||
// // userId,
|
|
||||||
// // organization
|
|
||||||
// // );
|
|
||||||
// // console.log('deletedProject: ', deletedProject);
|
|
||||||
// const deleteProjects = {
|
|
||||||
// projectId,
|
|
||||||
// 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}
|
|
||||||
// />
|
|
||||||
// ));
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const renderSharedProjects = () => {
|
|
||||||
// if (activeFolder !== "shared") 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 null
|
|
||||||
// }
|
|
||||||
@@ -5,8 +5,6 @@ import { RemoveIcon } from "../../../icons/ExportCommonIcons";
|
|||||||
import PositionInput from "../customInput/PositionInputs";
|
import PositionInput from "../customInput/PositionInputs";
|
||||||
import RotationInput from "../customInput/RotationInput";
|
import RotationInput from "../customInput/RotationInput";
|
||||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
|
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
|
||||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
|
||||||
import { center } from "@turf/turf";
|
|
||||||
|
|
||||||
interface UserData {
|
interface UserData {
|
||||||
id: number; // Unique identifier for the user data
|
id: number; // Unique identifier for the user data
|
||||||
@@ -20,10 +18,6 @@ const AssetProperties: React.FC = () => {
|
|||||||
const { selectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem } = useSelectedFloorItem();
|
||||||
const { objectPosition } = useObjectPosition();
|
const { objectPosition } = useObjectPosition();
|
||||||
const { objectRotation } = useObjectRotation();
|
const { objectRotation } = useObjectRotation();
|
||||||
const { assetStore } = useSceneContext();
|
|
||||||
const { assets, setCurrentAnimation } = assetStore()
|
|
||||||
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
|
|
||||||
const [isPlaying, setIsplaying] = useState(false);
|
|
||||||
// Function to handle adding new user data
|
// Function to handle adding new user data
|
||||||
const handleAddUserData = () => {
|
const handleAddUserData = () => {
|
||||||
const newUserData: UserData = {
|
const newUserData: UserData = {
|
||||||
@@ -51,12 +45,6 @@ const AssetProperties: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAnimationClick = (animation: string) => {
|
|
||||||
if (selectedFloorItem) {
|
|
||||||
const isPlaying = selectedFloorItem.animationState?.playing || false;
|
|
||||||
setCurrentAnimation(selectedFloorItem.uuid, animation, !isPlaying);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className="asset-properties-container">
|
<div className="asset-properties-container">
|
||||||
{/* Name */}
|
{/* Name */}
|
||||||
@@ -108,40 +96,6 @@ const AssetProperties: React.FC = () => {
|
|||||||
+ Add
|
+ Add
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
|
|
||||||
{selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
|
|
||||||
{assets.map((asset) => (
|
|
||||||
<div key={asset.modelUuid} className="asset-item">
|
|
||||||
{asset.modelUuid === selectedFloorItem.uuid &&
|
|
||||||
asset.animations &&
|
|
||||||
asset.animations.length > 0 &&
|
|
||||||
asset.animations.map((animation, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
onClick={() => handleAnimationClick(animation)}
|
|
||||||
onMouseEnter={() => setHoveredIndex(index)}
|
|
||||||
onMouseLeave={() => setHoveredIndex(null)}
|
|
||||||
style={{
|
|
||||||
height: "20px",
|
|
||||||
width: "100%",
|
|
||||||
borderRadius: "5px",
|
|
||||||
background:
|
|
||||||
hoveredIndex === index
|
|
||||||
? "#7b4cd3"
|
|
||||||
: "transparent",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{animation.charAt(0).toUpperCase() +
|
|
||||||
animation.slice(1).toLowerCase()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -133,17 +133,11 @@ const SelectedWallProperties = () => {
|
|||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
label="Height"
|
label="Height"
|
||||||
value={height}
|
value={height}
|
||||||
min={1}
|
|
||||||
max={25}
|
|
||||||
step={1}
|
|
||||||
onChange={handleHeightChange}
|
onChange={handleHeightChange}
|
||||||
/>
|
/>
|
||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
label="Thickness"
|
label="Thickness"
|
||||||
value={thickness}
|
value={thickness}
|
||||||
min={0.1}
|
|
||||||
max={2}
|
|
||||||
step={0.1}
|
|
||||||
onChange={handleThicknessChange}
|
onChange={handleThicknessChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,17 +63,11 @@ const WallProperties = () => {
|
|||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
label="Height"
|
label="Height"
|
||||||
value={`${wallHeight}`}
|
value={`${wallHeight}`}
|
||||||
min={1}
|
|
||||||
max={25}
|
|
||||||
step={1}
|
|
||||||
onChange={(val) => handleHeightChange(val)}
|
onChange={(val) => handleHeightChange(val)}
|
||||||
/>
|
/>
|
||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
label="Thickness"
|
label="Thickness"
|
||||||
value={`${wallThickness}`}
|
value={`${wallThickness}`}
|
||||||
min={0.1}
|
|
||||||
max={2}
|
|
||||||
step={0.1}
|
|
||||||
onChange={(val) => handleThicknessChange(val)}
|
onChange={(val) => handleThicknessChange(val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { useActiveUsers } from "../../store/builder/store";
|
|||||||
import { getUserData } from "../../functions/getUserData";
|
import { getUserData } from "../../functions/getUserData";
|
||||||
import { getProjectSharedList } from "../../services/factoryBuilder/collab/getProjectSharedList";
|
import { getProjectSharedList } from "../../services/factoryBuilder/collab/getProjectSharedList";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { projection } from "@turf/turf";
|
|
||||||
import { shareAccess } from "../../services/factoryBuilder/collab/shareAccess";
|
import { shareAccess } from "../../services/factoryBuilder/collab/shareAccess";
|
||||||
import { getAvatarColor } from "../../modules/collaboration/functions/getAvatarColor";
|
import { getAvatarColor } from "../../modules/collaboration/functions/getAvatarColor";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { useProjectName } from "../../store/builder/store";
|
|||||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||||
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
|
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
|
||||||
import { getUserData } from "../../functions/getUserData";
|
import { getUserData } from "../../functions/getUserData";
|
||||||
import { recentlyViewed } from "../../services/dashboard/recentlyViewed";
|
|
||||||
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
|
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
|
||||||
|
|
||||||
interface LoadingPageProps {
|
interface LoadingPageProps {
|
||||||
@@ -20,23 +19,6 @@ const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
|
|||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
|
||||||
const validatedProgress = Math.min(100, Math.max(0, progress));
|
const validatedProgress = Math.min(100, Math.max(0, progress));
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (!userId) return;
|
|
||||||
|
|
||||||
|
|
||||||
// // getAllProjects(userId, organization).then((projects) => {
|
|
||||||
// // sharedWithMeProjects().then((shared) => {
|
|
||||||
// // console.log('filterProject: ', shared);
|
|
||||||
// // const filterProject = (projects?.Projects || shared)?.find((val: any) => val.projectUuid === projectId || val._id === projectId)
|
|
||||||
// // console.log('filterProject: ', filterProject);
|
|
||||||
// // // setProjectName(filterProject?.projectName)
|
|
||||||
|
|
||||||
// // })
|
|
||||||
// // }).catch(() => {
|
|
||||||
// // console.error("Error fetching projects")
|
|
||||||
// // })
|
|
||||||
// }, []);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
console.error("User data not found in localStorage");
|
console.error("User data not found in localStorage");
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import useLayoutStore from "../../store/builder/uselayoutStore";
|
import useLayoutStore from "../../store/builder/uselayoutStore";
|
||||||
import { useDfxUpload } from "../../store/builder/store";
|
import { useActiveLayer, useDfxUpload } from "../../store/builder/store";
|
||||||
import DxfParser from "dxf-parser";
|
import DxfParser from "dxf-parser";
|
||||||
import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint";
|
import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint";
|
||||||
import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree";
|
import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree";
|
||||||
import { AIIcon } from "../icons/ExportCommonIcons";
|
import { AIIcon } from "../icons/ExportCommonIcons";
|
||||||
|
import { useBuilderStore } from "../../store/builder/useBuilderStore";
|
||||||
|
import { useSceneContext } from "../../modules/scene/sceneContext";
|
||||||
type DXFData = any;
|
type DXFData = any;
|
||||||
const SelectFloorPlan: React.FC = () => {
|
const SelectFloorPlan: React.FC = () => {
|
||||||
// Access layout state and state setters
|
// Access layout state and state setters
|
||||||
const { currentLayout, setLayout } = useLayoutStore();
|
const { currentLayout, setLayout } = useLayoutStore();
|
||||||
// Access DXF-related states and setters
|
// Access DXF-related states and setters
|
||||||
const { setDfxUploaded, setDfxGenerate, setObjValue, objValue } =
|
const { setDfxUploaded, setDxfWallGenerate, setObjValue, objValue } =
|
||||||
useDfxUpload();
|
useDfxUpload();
|
||||||
|
const { activeLayer } = useActiveLayer();
|
||||||
|
const { wallThickness, wallHeight, insideMaterial, outsideMaterial } = useBuilderStore();
|
||||||
|
const { wallStore } = useSceneContext();
|
||||||
|
const { addWall } = wallStore();
|
||||||
// Local state to store the parsed DXF file
|
// Local state to store the parsed DXF file
|
||||||
const [parsedFile, setParsedFile] = useState<DXFData | undefined>(undefined);
|
const [parsedFile, setParsedFile] = useState<DXFData | undefined>(undefined);
|
||||||
|
const { walls } = wallStore();
|
||||||
// Flag to trigger generation after file upload
|
// Flag to trigger generation after file upload
|
||||||
const [generate, setGenerate] = useState<boolean>(false);
|
const [generate, setGenerate] = useState<boolean>(false);
|
||||||
|
|
||||||
@@ -58,8 +63,9 @@ const SelectFloorPlan: React.FC = () => {
|
|||||||
if (parsedFile !== undefined) {
|
if (parsedFile !== undefined) {
|
||||||
getWallPointsFromBlueprint({
|
getWallPointsFromBlueprint({
|
||||||
parsedData: parsedFile,
|
parsedData: parsedFile,
|
||||||
setDfxGenerate,
|
setDxfWallGenerate,
|
||||||
objValue,
|
objValue,
|
||||||
|
wallThickness, wallHeight, outsideMaterial, insideMaterial, activeLayer, addWall, walls
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [generate]);
|
}, [generate]);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers";
|
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers";
|
||||||
import { shareProject } from "../../../services/factoryBuilder/collab/shareProject";
|
import { shareProject } from "../../../services/factoryBuilder/collab/shareProject";
|
||||||
import { getUserData } from "../../../functions/getUserData";
|
import { getUserData } from "../../../functions/getUserData";
|
||||||
|
|
||||||
@@ -11,121 +12,120 @@ interface UserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface MultiEmailProps {
|
interface MultiEmailProps {
|
||||||
users: any,
|
users: Array<any>;
|
||||||
getData: any,
|
getData: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MultiEmailInvite: React.FC<MultiEmailProps> = ({ users, getData }) => {
|
const MultiEmailInvite: React.FC<MultiEmailProps> = ({ users, getData }) => {
|
||||||
const [emails, setEmails] = useState<any>([]);
|
const [selectedUsers, setSelectedUsers] = useState<UserData[]>([]);
|
||||||
const [searchedEmail, setSearchedEmail] = useState<UserData[]>([]);
|
const [searchResults, setSearchResults] = useState<UserData[]>([]);
|
||||||
const [inputFocus, setInputFocus] = useState(false);
|
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { userId } = getUserData();
|
const { userId } = getUserData();
|
||||||
|
|
||||||
const handleAddEmail = async (selectedUser: UserData) => {
|
|
||||||
if (!projectId || !selectedUser) return
|
|
||||||
const trimmedEmail = inputValue.trim();
|
|
||||||
setEmails((prev: any[]) => {
|
|
||||||
if (!selectedUser) return prev;
|
|
||||||
const isNotCurrentUser = selectedUser._id !== userId;
|
|
||||||
const alreadyExistsInEmails = prev.some(email => email._id === selectedUser._id);
|
|
||||||
const alreadyExistsInUsers = users.some((val: any) => val.userId === selectedUser._id);
|
|
||||||
if (isNotCurrentUser && !alreadyExistsInEmails && !alreadyExistsInUsers) {
|
|
||||||
return [...prev, selectedUser];
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev;
|
const handleSearchInput = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
});
|
const value = e.target.value.trim();
|
||||||
setInputValue(""); // Clear the input field after adding
|
setInputValue(value);
|
||||||
};
|
|
||||||
const handleSearchMail = async (e: any) => {
|
if (value.length < 3) return;
|
||||||
setInputValue(e.target.value);
|
|
||||||
const trimmedEmail = e.target.value.trim();
|
|
||||||
|
|
||||||
if (trimmedEmail.length < 3) return;
|
|
||||||
try {
|
try {
|
||||||
const searchedMail = await getSearchUsers(trimmedEmail);
|
const result = await getSearchUsers(value);
|
||||||
const filteredEmail = searchedMail.sharchMail?.filtered;
|
const filtered = result?.sharchMail?.filtered || [];
|
||||||
if (filteredEmail) {
|
setSearchResults(filtered);
|
||||||
setSearchedEmail(filteredEmail)
|
} catch (err) {
|
||||||
}
|
console.error("Search failed:", err);
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to search mail:", error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddUser = (user: UserData) => {
|
||||||
|
if (!user || user._id === userId) return;
|
||||||
|
|
||||||
const handleKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const isAlreadySelected = selectedUsers.some(u => u._id === user._id);
|
||||||
if (e.key === "Enter" || e.key === "," && searchedEmail.length > 0) {
|
const isAlreadyShared = users.some(u => u.userId === user._id);
|
||||||
|
|
||||||
|
if (!isAlreadySelected && !isAlreadyShared) {
|
||||||
|
setSelectedUsers(prev => [...prev, user]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInputValue("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if ((e.key === "Enter" || e.key === ",") && searchResults.length > 0) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleAddEmail(searchedEmail[0]);
|
handleAddUser(searchResults[0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveEmail = (idToRemove: string) => {
|
const handleRemoveUser = (userIdToRemove: string) => {
|
||||||
setEmails((prev: any) => prev.filter((email: any) => email._id !== idToRemove));
|
setSelectedUsers(prev => prev.filter(user => user._id !== userIdToRemove));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const validateEmail = (email: string) => {
|
const validateEmail = (email: string) => {
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
return emailRegex.test(email);
|
return emailRegex.test(email);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInvite = () => {
|
const handleAddEmail = async () => {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
try {
|
|
||||||
emails.forEach((user: any) => {
|
|
||||||
shareProject(user._id, projectId)
|
|
||||||
.then((res) => {
|
|
||||||
console.log("sharedProject:", res);
|
|
||||||
|
|
||||||
})
|
try {
|
||||||
.catch((err) => {
|
await Promise.all(
|
||||||
console.error("Error sharing project:", err);
|
selectedUsers.map(user =>
|
||||||
});
|
shareProject(user._id, projectId)
|
||||||
setEmails([])
|
.then(res => console.log("Shared:", res))
|
||||||
setInputValue("")
|
.catch(err => console.error("Share error:", err))
|
||||||
});
|
)
|
||||||
setTimeout(() => {
|
);
|
||||||
getData()
|
|
||||||
}, 1000);
|
setSelectedUsers([]);
|
||||||
|
setInputValue("");
|
||||||
|
setTimeout(getData, 1000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("General error:", error);
|
console.error("Invite error:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="multi-email-invite-input-container">
|
<div className="multi-email-invite-input-container">
|
||||||
<div className={`multi-email-invite-input${inputFocus ? " active" : ""}`}>
|
<div className={`multi-email-invite-input${isFocused ? " active" : ""}`}>
|
||||||
{emails.map((email: any, index: number) => (
|
{selectedUsers.map(user => (
|
||||||
<div key={index} className="entered-emails">
|
<div key={user._id} className="entered-emails">
|
||||||
{email.Email}
|
{user.Email}
|
||||||
<span onClick={() => handleRemoveEmail(email._id)}>×</span>
|
<span onClick={() => handleRemoveUser(user._id)}>×</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(e) => handleSearchMail(e)}
|
onChange={handleSearchInput}
|
||||||
onFocus={() => setInputFocus(true)}
|
onFocus={() => setIsFocused(true)}
|
||||||
// onBlur={() => setInputFocus(false)}
|
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
placeholder="Enter email and press Enter or comma to seperate"
|
placeholder="Enter email and press Enter or comma"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={handleInvite} className="invite-button">
|
|
||||||
|
<div onClick={handleAddEmail} className="invite-button">
|
||||||
Add
|
Add
|
||||||
</div>
|
</div>
|
||||||
{inputFocus && inputValue.length > 2 && searchedEmail && searchedEmail.length > 0 && (
|
|
||||||
|
{isFocused && inputValue.length > 2 && searchResults.length > 0 && (
|
||||||
<div className="users-list-container">
|
<div className="users-list-container">
|
||||||
{/* list available users here */}
|
{searchResults.map(user => (
|
||||||
{searchedEmail.map((val: any, i: any) => (
|
<div
|
||||||
<div onClick={(e) => {
|
key={user._id}
|
||||||
handleAddEmail(val)
|
onClick={() => {
|
||||||
setInputFocus(false)
|
handleAddUser(user);
|
||||||
}} key={i} >
|
setIsFocused(false);
|
||||||
{val?.Email}
|
}}
|
||||||
|
>
|
||||||
|
{user.Email}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
|||||||
import defaultMaterial from '../../../assets/textures/floor/wall-tex.png';
|
import defaultMaterial from '../../../assets/textures/floor/wall-tex.png';
|
||||||
import useModuleStore from '../../../store/useModuleStore';
|
import useModuleStore from '../../../store/useModuleStore';
|
||||||
|
|
||||||
function DecalInstance({ visible = true, decal, zPosition = decal.decalPosition[2] }: { visible?: boolean, decal: Decal, zPosition?: number }) {
|
function DecalInstance({ visible = true, decal }: { visible?: boolean, decal: Decal }) {
|
||||||
const { setSelectedWall, setSelectedFloor, selectedDecal, setSelectedDecal } = useBuilderStore();
|
const { setSelectedWall, setSelectedFloor, selectedDecal, setSelectedDecal } = useBuilderStore();
|
||||||
const { togglView } = useToggleView();
|
const { togglView } = useToggleView();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
@@ -17,7 +17,7 @@ function DecalInstance({ visible = true, decal, zPosition = decal.decalPosition[
|
|||||||
<Decal
|
<Decal
|
||||||
// debug
|
// debug
|
||||||
visible={visible}
|
visible={visible}
|
||||||
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
position={decal.decalPosition}
|
||||||
rotation={[0, 0, decal.decalRotation]}
|
rotation={[0, 0, decal.decalRotation]}
|
||||||
scale={[decal.decalScale, decal.decalScale, 0.01]}
|
scale={[decal.decalScale, decal.decalScale, 0.01]}
|
||||||
userData={decal}
|
userData={decal}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as THREE from "three"
|
import * as THREE from "three"
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
|
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
|
||||||
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
||||||
@@ -269,7 +269,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvasElement = gl.domElement;
|
const canvasElement = gl.domElement;
|
||||||
|
|
||||||
const onDrop = (event: DragEvent) => {
|
const onDrop = (event: any) => {
|
||||||
if (!event.dataTransfer?.files[0]) return;
|
if (!event.dataTransfer?.files[0]) return;
|
||||||
|
|
||||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') {
|
if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
|||||||
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
||||||
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
||||||
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
|
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
|
||||||
import { CameraControls, Html } from '@react-three/drei';
|
import { CameraControls } from '@react-three/drei';
|
||||||
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
|
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
|
||||||
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
|
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
|
||||||
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
|
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
|
||||||
@@ -23,7 +23,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const { subModule } = useSubModuleStore();
|
const { subModule } = useSubModuleStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { assetStore, eventStore, productStore } = useSceneContext();
|
const { assetStore, eventStore, productStore } = useSceneContext();
|
||||||
const { assets, removeAsset, setAnimations } = assetStore();
|
const { removeAsset } = assetStore();
|
||||||
const { setTop } = useTopData();
|
const { setTop } = useTopData();
|
||||||
const { setLeft } = useLeftData();
|
const { setLeft } = useLeftData();
|
||||||
const { getIsEventInProduct } = productStore();
|
const { getIsEventInProduct } = productStore();
|
||||||
@@ -46,9 +46,6 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
const [animationNames, setAnimationNames] = useState<string[]>([]);
|
|
||||||
const mixerRef = useRef<THREE.AnimationMixer>();
|
|
||||||
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDeletableFloorItem(null);
|
setDeletableFloorItem(null);
|
||||||
@@ -63,45 +60,11 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const loadModel = async () => {
|
const loadModel = async () => {
|
||||||
try {
|
try {
|
||||||
// Check Cache
|
// Check Cache
|
||||||
// const assetId = asset.assetId;
|
|
||||||
// const cachedModel = THREE.Cache.get(assetId);
|
|
||||||
// if (cachedModel) {
|
|
||||||
// setGltfScene(cachedModel.scene.clone());
|
|
||||||
// calculateBoundingBox(cachedModel.scene);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Check Cache
|
|
||||||
// const assetId = asset.assetId;
|
|
||||||
// console.log('assetId: ', assetId);
|
|
||||||
// const cachedModel = THREE.Cache.get(assetId);
|
|
||||||
// console.log('cachedModel: ', cachedModel);
|
|
||||||
// if (cachedModel) {
|
|
||||||
// setGltfScene(cachedModel.scene.clone());
|
|
||||||
// calculateBoundingBox(cachedModel.scene);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const assetId = asset.assetId;
|
const assetId = asset.assetId;
|
||||||
const cachedModel = THREE.Cache.get(assetId);
|
const cachedModel = THREE.Cache.get(assetId);
|
||||||
if (cachedModel) {
|
if (cachedModel) {
|
||||||
const clonedScene = cachedModel.scene.clone();
|
setGltfScene(cachedModel.scene.clone());
|
||||||
clonedScene.animations = cachedModel.animations || [];
|
calculateBoundingBox(cachedModel.scene);
|
||||||
setGltfScene(clonedScene);
|
|
||||||
calculateBoundingBox(clonedScene);
|
|
||||||
if (cachedModel.animations && clonedScene.animations.length > 0) {
|
|
||||||
const animationName = clonedScene.animations.map((clip: any) => clip.name);
|
|
||||||
setAnimationNames(animationName)
|
|
||||||
setAnimations(asset.modelUuid, animationName)
|
|
||||||
mixerRef.current = new THREE.AnimationMixer(clonedScene);
|
|
||||||
|
|
||||||
clonedScene.animations.forEach((animation: any) => {
|
|
||||||
const action = mixerRef.current!.clipAction(animation);
|
|
||||||
actions.current[animation.name] = action;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log('No animations');
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,32 +259,6 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
clearSelectedAsset()
|
clearSelectedAsset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
useFrame((_, delta) => {
|
|
||||||
if (mixerRef.current) {
|
|
||||||
mixerRef.current.update(delta);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handlePlay = (clipName: string) => {
|
|
||||||
console.log('clipName: ', clipName, asset.animationState);
|
|
||||||
if (!mixerRef.current) return;
|
|
||||||
|
|
||||||
Object.values(actions.current).forEach((action) => action.stop());
|
|
||||||
|
|
||||||
const action = actions.current[clipName];
|
|
||||||
if (action && asset.animationState?.playing) {
|
|
||||||
action.reset().setLoop(THREE.LoopOnce, 1).play();
|
|
||||||
console.log(`Playing: ${clipName}`);
|
|
||||||
} else {
|
|
||||||
console.warn(`No action found for: ${clipName}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handlePlay(asset.animationState?.current || '');
|
|
||||||
|
|
||||||
}, [asset])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
@@ -369,18 +306,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
<AssetBoundingBox boundingBox={boundingBox} />
|
<AssetBoundingBox boundingBox={boundingBox} />
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
{/* <group >
|
</group>
|
||||||
<Html>
|
|
||||||
<div style={{ position: 'absolute', }}>
|
|
||||||
{animationNames.map((name) => (
|
|
||||||
<button key={name} onClick={() => handlePlay(name)} style={{ margin: 4 }}>
|
|
||||||
{name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Html>
|
|
||||||
</group> */}
|
|
||||||
</group >
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { Geometry } from "@react-three/csg";
|
import { Bvh } from "@react-three/drei";
|
||||||
|
|
||||||
////////// Zustand State Imports //////////
|
////////// Zustand State Imports //////////
|
||||||
|
|
||||||
@@ -39,14 +39,16 @@ import ZoneGroup from "./zone/zoneGroup";
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useBuilderStore } from "../../store/builder/useBuilderStore";
|
import { useBuilderStore } from "../../store/builder/useBuilderStore";
|
||||||
import { getUserData } from "../../functions/getUserData";
|
import { getUserData } from "../../functions/getUserData";
|
||||||
import WallAssetGroup from "./wallAsset/wallAssetGroup";
|
|
||||||
|
|
||||||
export default function Builder() {
|
export default function Builder() {
|
||||||
const state = useThree<Types.ThreeState>();
|
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
|
||||||
const plane = useRef<THREE.Mesh>(null);
|
|
||||||
const csgRef = useRef<any>(null);
|
|
||||||
|
|
||||||
const { toggleView } = useToggleView();
|
// Assigning the scene and camera from the Three.js state to the references.
|
||||||
|
|
||||||
|
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
|
||||||
|
const grid = useRef() as any; // Reference for a grid object for raycaster reference.
|
||||||
|
|
||||||
|
const { toggleView } = useToggleView(); // State for toggling between 2D and 3D.
|
||||||
const { setToolMode } = useToolMode();
|
const { setToolMode } = useToolMode();
|
||||||
const { setRoofVisibility } = useRoofVisibility();
|
const { setRoofVisibility } = useRoofVisibility();
|
||||||
const { setWallVisibility } = useWallVisibility();
|
const { setWallVisibility } = useWallVisibility();
|
||||||
@@ -57,6 +59,14 @@ export default function Builder() {
|
|||||||
const { setHoveredPoint, setHoveredLine } = useBuilderStore();
|
const { setHoveredPoint, setHoveredLine } = useBuilderStore();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
|
||||||
|
// const loader = new GLTFLoader();
|
||||||
|
// const dracoLoader = new DRACOLoader();
|
||||||
|
|
||||||
|
// dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
||||||
|
// loader.setDRACOLoader(dracoLoader);
|
||||||
|
|
||||||
|
////////// All Toggle's //////////
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!toggleView) {
|
if (!toggleView) {
|
||||||
setHoveredLine(null);
|
setHoveredLine(null);
|
||||||
@@ -81,32 +91,30 @@ export default function Builder() {
|
|||||||
fetchVisibility();
|
fetchVisibility();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useFrame(() => {
|
////////// Return //////////
|
||||||
if (csgRef.current) {
|
|
||||||
csgRef.current.update();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Ground plane={plane} />
|
<Ground grid={grid} plane={plane} />
|
||||||
|
|
||||||
<SocketResponses />
|
<SocketResponses />
|
||||||
|
|
||||||
|
{/* <WallsAndWallItems
|
||||||
|
CSGGroup={CSGGroup}
|
||||||
|
setSelectedItemsIndex={setSelectedItemsIndex}
|
||||||
|
selectedItemsIndex={selectedItemsIndex}
|
||||||
|
currentWallItem={currentWallItem}
|
||||||
|
csg={csg}
|
||||||
|
lines={lines}
|
||||||
|
hoveredDeletableWallItem={hoveredDeletableWallItem}
|
||||||
|
/> */}
|
||||||
|
|
||||||
<AssetsGroup plane={plane} />
|
<AssetsGroup plane={plane} />
|
||||||
|
|
||||||
<mesh name='Walls-And-WallAssets-Group'>
|
|
||||||
<Geometry ref={csgRef} useGroups>
|
|
||||||
|
|
||||||
<WallGroup />
|
|
||||||
|
|
||||||
<WallAssetGroup />
|
|
||||||
|
|
||||||
</Geometry>
|
|
||||||
</mesh>
|
|
||||||
|
|
||||||
<AislesGroup />
|
<AislesGroup />
|
||||||
|
|
||||||
|
<WallGroup />
|
||||||
|
|
||||||
<FloorGroup />
|
<FloorGroup />
|
||||||
|
|
||||||
<ZoneGroup />
|
<ZoneGroup />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
|
import { useActiveLayer, useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
|
||||||
import { LineBasicMaterial, Line } from 'three';
|
import { LineBasicMaterial, Line } from 'three';
|
||||||
import { TransformControls } from '@react-three/drei';
|
import { TransformControls } from '@react-three/drei';
|
||||||
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
|
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
|
||||||
@@ -7,6 +7,8 @@ import * as Types from '../../../types/world/worldTypes';
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { getUserData } from '../../../functions/getUserData';
|
import { getUserData } from '../../../functions/getUserData';
|
||||||
import { useVersionContext } from '../version/versionContext';
|
import { useVersionContext } from '../version/versionContext';
|
||||||
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||||
|
import { useSceneContext } from '../../scene/sceneContext';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene.
|
* DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene.
|
||||||
@@ -15,48 +17,44 @@ import { useVersionContext } from '../version/versionContext';
|
|||||||
*/
|
*/
|
||||||
const DxfFile = () => {
|
const DxfFile = () => {
|
||||||
// State management hooks
|
// State management hooks
|
||||||
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload();
|
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue, setDfxUploaded } = useDfxUpload();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { selectedVersionStore } = useVersionContext();
|
const { selectedVersionStore } = useVersionContext();
|
||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
const { activeLayer } = useActiveLayer();
|
||||||
|
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, } = useBuilderStore();
|
||||||
// Refs for storing line objects
|
// Refs for storing line objects
|
||||||
const lineRefs = useRef<Line[]>([]);
|
const lineRefs = useRef<Line[]>([]);
|
||||||
|
const { wallStore } = useSceneContext();
|
||||||
|
const { addWall, } = wallStore();
|
||||||
|
const { walls } = wallStore();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Effect hook that runs when DXF wall generation is triggered.
|
* Effect hook that runs when DXF wall generation is triggered.
|
||||||
* Loads initial points and lines from the DXF data and updates the scene.
|
* Loads initial points and lines from the DXF data and updates the scene.
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (dfxWallGenerate) {
|
if (dfxWallGenerate) {
|
||||||
// Store generated lines in ref
|
dfxWallGenerate.map((wall: Wall) => {
|
||||||
// lines.current.push(...dfxWallGenerate);
|
const data = {
|
||||||
// dfxWallGenerate.map((line: any) => {
|
wallData: wall,
|
||||||
// const lineData = arrayLineToObject(line as Types.Line);
|
projectId: projectId,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
userId: userId,
|
||||||
|
organization: organization
|
||||||
|
}
|
||||||
|
addWall(wall);
|
||||||
|
socket.emit('v1:model-Wall:add', data);
|
||||||
|
// API
|
||||||
|
|
||||||
// //REST
|
// if (projectId) {
|
||||||
|
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall);
|
||||||
// // setLine(organization, lineData.layer!, lineData.line!, lineData.type!);
|
// }
|
||||||
|
})
|
||||||
// //SOCKET
|
|
||||||
|
|
||||||
// const input = {
|
|
||||||
// organization,
|
|
||||||
// layer: lineData.layer,
|
|
||||||
// line: lineData.line,
|
|
||||||
// type: lineData.type,
|
|
||||||
// socketId: socket.id,
|
|
||||||
// versionId: selectedVersion?.versionId || '',
|
|
||||||
// projectId,
|
|
||||||
// userId
|
|
||||||
// }
|
|
||||||
|
|
||||||
// socket.emit('v1:Line:create', input);
|
|
||||||
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
}, [dfxWallGenerate]);
|
}, [dfxWallGenerate]);
|
||||||
|
|
||||||
@@ -78,10 +76,15 @@ const DxfFile = () => {
|
|||||||
// Recalculate wall points based on new position
|
// Recalculate wall points based on new position
|
||||||
getWallPointsFromBlueprint({
|
getWallPointsFromBlueprint({
|
||||||
objValue: { x: position.x, y: position.y, z: position.z },
|
objValue: { x: position.x, y: position.y, z: position.z },
|
||||||
setDfxGenerate: () => { },
|
setDxfWallGenerate: () => { },
|
||||||
|
wallThickness, wallHeight, outsideMaterial, insideMaterial, activeLayer, addWall, walls
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (!toggleView) {
|
||||||
|
setDfxUploaded([])
|
||||||
|
}
|
||||||
|
}, [toggleView])
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Render DXF lines with transform controls when DXF data is available and view is toggled */}
|
{/* Render DXF lines with transform controls when DXF data is available and view is toggled */}
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
import { MathUtils, Vector3, BufferGeometry } from "three";
|
import { MathUtils, Vector3, BufferGeometry } from "three";
|
||||||
|
import { useActiveLayer } from "../../../../store/builder/store";
|
||||||
|
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||||
|
import { wait } from "@testing-library/user-event/dist/utils";
|
||||||
type DXFData = any; // Replace with actual DXF data type
|
type DXFData = any; // Replace with actual DXF data type
|
||||||
type DXFEntity = any; // Replace with actual DXF entity 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
|
type WallLineVertex = [Vector3, string, number, string]; // Represents a wall segment with start point, ID, weight, and type
|
||||||
interface Props {
|
interface Props {
|
||||||
parsedData?: DXFData; // Parsed DXF file data
|
parsedData?: DXFData; // Parsed DXF file data
|
||||||
setDfxGenerate?: (walls: WallLineVertex[][]) => void; // Callback to set generated walls
|
setDxfWallGenerate?: any; // Callback to set generated walls
|
||||||
objValue: any; // Object position values for offset calculation
|
objValue: any; // Object position values for offset calculation
|
||||||
|
wallThickness: number;
|
||||||
|
wallHeight: number;
|
||||||
|
outsideMaterial: string;
|
||||||
|
insideMaterial: string;
|
||||||
|
activeLayer: number; // Active layer for wall points
|
||||||
|
addWall: (wall: Wall) => void; // Function to add a wall to the scene
|
||||||
|
walls: Wall[]; // Array of walls to be processed
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,20 +25,36 @@ interface Props {
|
|||||||
*
|
*
|
||||||
* @param {Props} params - Configuration parameters
|
* @param {Props} params - Configuration parameters
|
||||||
* @param {DXFData} params.parsedData - Parsed DXF file data
|
* @param {DXFData} params.parsedData - Parsed DXF file data
|
||||||
* @param {Function} params.setDfxGenerate - Callback to store generated walls
|
* @param {Function} params.setDxfWallGenerate - Callback to store generated walls
|
||||||
* @param {Object} params.objValue - Contains x,y,z offsets for position adjustment
|
* @param {Object} params.objValue - Contains x,y,z offsets for position adjustment
|
||||||
*/
|
*/
|
||||||
export function getWallPointsFromBlueprint({
|
export function getWallPointsFromBlueprint({
|
||||||
parsedData,
|
parsedData,
|
||||||
setDfxGenerate,
|
setDxfWallGenerate,
|
||||||
objValue,
|
objValue,
|
||||||
|
wallThickness,
|
||||||
|
wallHeight,
|
||||||
|
outsideMaterial,
|
||||||
|
insideMaterial,
|
||||||
|
activeLayer,
|
||||||
|
addWall,
|
||||||
|
walls,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
// Early return if no data is provided
|
// Early return if no data is provided
|
||||||
if (!parsedData) return;
|
if (!parsedData) return;
|
||||||
if (!parsedData.entities) return;
|
if (!parsedData.entities) return;
|
||||||
|
|
||||||
const unit = 1000; // Conversion factor from millimeters to meters
|
const unit = 1000; // Conversion factor from millimeters to meters
|
||||||
const wallVertex: WallLineVertex[][] = []; // Stores all generated wall segments
|
const wallVertex: any[] = []; // Array to store wall vertices
|
||||||
|
const findExistingPoint = (vec: Vector3): Point | undefined => {
|
||||||
|
for (const wall of wallVertex) {
|
||||||
|
for (const pt of wall.points) {
|
||||||
|
const pos = new Vector3(...pt.position);
|
||||||
|
if (pos.equals(vec)) return pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
// Process each entity in the DXF file
|
// Process each entity in the DXF file
|
||||||
parsedData.entities.forEach((entity: DXFEntity) => {
|
parsedData.entities.forEach((entity: DXFEntity) => {
|
||||||
@@ -47,31 +73,41 @@ export function getWallPointsFromBlueprint({
|
|||||||
-entity.vertices[1].y / unit
|
-entity.vertices[1].y / unit
|
||||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||||
|
|
||||||
// Check if points already exist to avoid duplicates
|
// Create start and end points
|
||||||
const existingStart = wallVertex
|
const existingStart = findExistingPoint(startVec);
|
||||||
.flat()
|
const startPoint: Point = existingStart || {
|
||||||
.find((v) => v[0].equals(startVec));
|
pointUuid: MathUtils.generateUUID(),
|
||||||
const startPoint: WallLineVertex = existingStart || [
|
pointType: "Wall",
|
||||||
startVec,
|
position: [startVec.x, startVec.y, startVec.z],
|
||||||
MathUtils.generateUUID(), // Generate unique ID for new points
|
layer: activeLayer,
|
||||||
1, // Default weight
|
};
|
||||||
"WallLine", // Type identifier
|
|
||||||
];
|
|
||||||
|
|
||||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
const existingEnd = findExistingPoint(endVec);
|
||||||
const endPoint: WallLineVertex = existingEnd || [
|
const endPoint: Point = existingEnd || {
|
||||||
endVec,
|
pointUuid: MathUtils.generateUUID(),
|
||||||
MathUtils.generateUUID(),
|
pointType: "Wall",
|
||||||
1,
|
position: [endVec.x, endVec.y, endVec.z],
|
||||||
"WallLine",
|
layer: activeLayer,
|
||||||
];
|
};
|
||||||
|
|
||||||
// Add the line segment to our collection
|
// Create the wall
|
||||||
wallVertex.push([startPoint, endPoint]);
|
const wallSet: Wall = {
|
||||||
|
wallUuid: MathUtils.generateUUID(),
|
||||||
|
points: [startPoint, endPoint], // Store start and end points
|
||||||
|
outsideMaterial: insideMaterial,
|
||||||
|
insideMaterial: outsideMaterial,
|
||||||
|
wallThickness: wallThickness,
|
||||||
|
wallHeight: wallHeight,
|
||||||
|
decals: [],
|
||||||
|
};
|
||||||
|
wallVertex.push(wallSet); // Store wall segment in array
|
||||||
|
// Add the wall to the scene
|
||||||
|
// addWall(wallSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle LWPOLYLINE entities (connected line segments)
|
// Handle LWPOLYLINE entities (connected line segments)
|
||||||
else if (entity.type === "LWPOLYLINE" && entity.vertices) {
|
else if (entity.type === "LWPOLYLINE" && entity.vertices) {
|
||||||
let firstPoint: WallLineVertex | undefined; // Store first point for closing the polyline
|
let firstPoint: Point | undefined; // Store first point for closing the polyline
|
||||||
|
|
||||||
// Process each vertex pair in the polyline
|
// Process each vertex pair in the polyline
|
||||||
for (let i = 0; i < entity.vertices.length - 1; i++) {
|
for (let i = 0; i < entity.vertices.length - 1; i++) {
|
||||||
@@ -88,37 +124,60 @@ export function getWallPointsFromBlueprint({
|
|||||||
-entity.vertices[i + 1].y / unit
|
-entity.vertices[i + 1].y / unit
|
||||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||||
|
|
||||||
// Check for existing points
|
// Create start and end points
|
||||||
const existingStart = wallVertex
|
const existingStart = findExistingPoint(startVec);
|
||||||
.flat()
|
const startPoint: Point = existingStart || {
|
||||||
.find((v) => v[0].equals(startVec));
|
pointUuid: MathUtils.generateUUID(), // Generate unique ID for new points
|
||||||
const startPoint: WallLineVertex = existingStart || [
|
pointType: "Wall", // Type identifier
|
||||||
startVec,
|
position: [startVec.x, startVec.y, startVec.z], // Position in 3D space
|
||||||
MathUtils.generateUUID(),
|
layer: activeLayer,
|
||||||
1,
|
};
|
||||||
"WallLine",
|
|
||||||
];
|
|
||||||
|
|
||||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
const existingEnd = findExistingPoint(endVec);
|
||||||
const endPoint: WallLineVertex = existingEnd || [
|
const endPoint: Point = existingEnd || {
|
||||||
endVec,
|
pointUuid: MathUtils.generateUUID(), // Generate unique ID for new points
|
||||||
MathUtils.generateUUID(),
|
pointType: "Wall", // Type identifier
|
||||||
1,
|
position: [endVec.x, endVec.y, endVec.z], // Position in 3D space
|
||||||
"WallLine",
|
layer: activeLayer,
|
||||||
];
|
};
|
||||||
|
|
||||||
wallVertex.push([startPoint, endPoint]);
|
// Create the wall segment
|
||||||
|
const wallSet: Wall = {
|
||||||
|
wallUuid: MathUtils.generateUUID(),
|
||||||
|
points: [startPoint, endPoint], // Store start and end points
|
||||||
|
outsideMaterial: insideMaterial,
|
||||||
|
insideMaterial: outsideMaterial,
|
||||||
|
wallThickness: wallThickness,
|
||||||
|
wallHeight: wallHeight,
|
||||||
|
decals: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the wall segment
|
||||||
|
// addWall(wallSet);
|
||||||
|
wallVertex.push(wallSet);
|
||||||
// Store first point and create closing segment if this is the last vertex
|
// Store first point and create closing segment if this is the last vertex
|
||||||
if (i === 0) firstPoint = startPoint;
|
if (i === 0) firstPoint = startPoint;
|
||||||
if (i === entity.vertices.length - 2 && firstPoint) {
|
if (i === entity.vertices.length - 2 && firstPoint) {
|
||||||
wallVertex.push([endPoint, firstPoint]);
|
const closingWallSet: Wall = {
|
||||||
|
wallUuid: MathUtils.generateUUID(),
|
||||||
|
points: [endPoint, firstPoint], // Create closing segment
|
||||||
|
outsideMaterial: insideMaterial,
|
||||||
|
insideMaterial: outsideMaterial,
|
||||||
|
wallThickness: wallThickness,
|
||||||
|
wallHeight: wallHeight,
|
||||||
|
decals: [],
|
||||||
|
};
|
||||||
|
// Add the closing wall
|
||||||
|
wallVertex.push(closingWallSet);
|
||||||
|
// addWall(closingWallSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle ARC entities
|
// Handle ARC entities
|
||||||
else if (entity.type === "ARC") {
|
else if (entity.type === "ARC") {
|
||||||
const { center, radius, startAngle, endAngle } = entity;
|
const { center, radius, startAngle, endAngle } = entity;
|
||||||
|
|
||||||
// Validate required ARC properties
|
// Validate required ARC properties
|
||||||
if (
|
if (
|
||||||
!center ||
|
!center ||
|
||||||
@@ -154,34 +213,46 @@ export function getWallPointsFromBlueprint({
|
|||||||
const startVec = arcPoints[i];
|
const startVec = arcPoints[i];
|
||||||
const endVec = arcPoints[i + 1];
|
const endVec = arcPoints[i + 1];
|
||||||
|
|
||||||
// Check for existing points
|
// Create start and end points
|
||||||
const existingStart = wallVertex
|
const existingStart = findExistingPoint(startVec);
|
||||||
.flat()
|
const startPoint: Point = existingStart || {
|
||||||
.find((v) => v[0].equals(startVec));
|
pointUuid: MathUtils.generateUUID(),
|
||||||
const startPoint: WallLineVertex = existingStart || [
|
pointType: "Wall",
|
||||||
startVec,
|
position: [startVec.x, startVec.y, startVec.z],
|
||||||
MathUtils.generateUUID(),
|
layer: activeLayer,
|
||||||
1,
|
};
|
||||||
"WallLine",
|
|
||||||
];
|
|
||||||
|
|
||||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
const existingEnd = findExistingPoint(endVec);
|
||||||
const endPoint: WallLineVertex = existingEnd || [
|
const endPoint: Point = existingEnd || {
|
||||||
endVec,
|
pointUuid: MathUtils.generateUUID(),
|
||||||
MathUtils.generateUUID(),
|
pointType: "Wall",
|
||||||
1,
|
position: [endVec.x, endVec.y, endVec.z],
|
||||||
"WallLine",
|
layer: activeLayer,
|
||||||
];
|
};
|
||||||
|
|
||||||
wallVertex.push([startPoint, endPoint]);
|
// Create the wall segment
|
||||||
|
const wallSet: Wall = {
|
||||||
|
wallUuid: MathUtils.generateUUID(),
|
||||||
|
points: [startPoint, endPoint],
|
||||||
|
outsideMaterial: insideMaterial,
|
||||||
|
insideMaterial: outsideMaterial,
|
||||||
|
wallThickness: wallThickness,
|
||||||
|
wallHeight: wallHeight,
|
||||||
|
decals: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the wall segment
|
||||||
|
// addWall(wallSet);
|
||||||
|
wallVertex.push(wallSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log unsupported entity types
|
// Log unsupported entity types
|
||||||
else {
|
else {
|
||||||
console.error("Unsupported entity type:", entity.type);
|
console.error("Unsupported entity type:", entity.type);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log("wallVertex: ", wallVertex);
|
||||||
// Return the generated walls through callback if provided
|
// Return the generated walls through callback if provided
|
||||||
setDfxGenerate && setDfxGenerate(wallVertex);
|
setDxfWallGenerate && setDxfWallGenerate(wallVertex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useActiveTool, useToggleView } from '../../../store/builder/store'
|
import { useToggleView } from '../../../store/builder/store'
|
||||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||||
import { useVersionContext } from '../version/versionContext';
|
import { useVersionContext } from '../version/versionContext';
|
||||||
import { useSceneContext } from '../../scene/sceneContext';
|
import { useSceneContext } from '../../scene/sceneContext';
|
||||||
@@ -13,7 +13,6 @@ function FloorGroup() {
|
|||||||
const { togglView } = useToggleView();
|
const { togglView } = useToggleView();
|
||||||
const { setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
const { setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { activeTool } = useActiveTool();
|
|
||||||
const { selectedVersionStore } = useVersionContext();
|
const { selectedVersionStore } = useVersionContext();
|
||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { floorStore } = useSceneContext();
|
const { floorStore } = useSceneContext();
|
||||||
@@ -25,7 +24,7 @@ function FloorGroup() {
|
|||||||
setSelectedFloor(null);
|
setSelectedFloor(null);
|
||||||
setSelectedDecal(null);
|
setSelectedDecal(null);
|
||||||
}
|
}
|
||||||
}, [togglView, activeModule, activeTool])
|
}, [togglView, activeModule])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (projectId && selectedVersion) {
|
if (projectId && selectedVersion) {
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Vector3 } from "three";
|
|
||||||
|
|
||||||
export default function closestPointOnLineSegment(p: Vector3, a: Vector3, b: Vector3) {
|
|
||||||
const ab = new Vector3().subVectors(b, a);
|
|
||||||
const ap = new Vector3().subVectors(p, a);
|
|
||||||
|
|
||||||
const abLengthSq = ab.lengthSq();
|
|
||||||
const dot = ap.dot(ab);
|
|
||||||
const t = Math.max(0, Math.min(1, dot / abLengthSq));
|
|
||||||
|
|
||||||
return new Vector3().copy(a).add(ab.multiplyScalar(t));
|
|
||||||
}
|
|
||||||
@@ -154,7 +154,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
<MeshDiscardMaterial />
|
<MeshDiscardMaterial />
|
||||||
|
|
||||||
{wall.decals.map((decal) => (
|
{wall.decals.map((decal) => (
|
||||||
<DecalInstance zPosition={wall.wallThickness / 2 + 0.001} visible={visible} key={decal.decalUuid} decal={decal} />
|
<DecalInstance visible={visible} key={decal.decalUuid} decal={decal} />
|
||||||
))}
|
))}
|
||||||
</mesh>
|
</mesh>
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three';
|
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three';
|
||||||
|
import { Geometry } from '@react-three/csg';
|
||||||
import { Html, Extrude } from '@react-three/drei';
|
import { Html, Extrude } from '@react-three/drei';
|
||||||
import { useLoader } from '@react-three/fiber';
|
import { useLoader } from '@react-three/fiber';
|
||||||
import { useSceneContext } from '../../../scene/sceneContext';
|
import { useSceneContext } from '../../../scene/sceneContext';
|
||||||
@@ -43,9 +44,13 @@ function WallInstances() {
|
|||||||
<>
|
<>
|
||||||
{!toggleView && walls.length > 1 && (
|
{!toggleView && walls.length > 1 && (
|
||||||
<>
|
<>
|
||||||
{walls.map((wall) => (
|
<mesh name='Walls-Group'>
|
||||||
<WallInstance key={wall.wallUuid} wall={wall} />
|
<Geometry useGroups>
|
||||||
))}
|
{walls.map((wall) => (
|
||||||
|
<WallInstance key={wall.wallUuid} wall={wall} />
|
||||||
|
))}
|
||||||
|
</Geometry>
|
||||||
|
</mesh>
|
||||||
|
|
||||||
<group name='Wall-Floors-Group'>
|
<group name='Wall-Floors-Group'>
|
||||||
{rooms.map((room, index) => (
|
{rooms.map((room, index) => (
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ function WallCreator() {
|
|||||||
|
|
||||||
if (wallIntersect && !pointIntersects) {
|
if (wallIntersect && !pointIntersects) {
|
||||||
const wall = getWallByPoints(wallIntersect.object.userData.points);
|
const wall = getWallByPoints(wallIntersect.object.userData.points);
|
||||||
|
console.log('wall: ', wall);
|
||||||
if (wall) {
|
if (wall) {
|
||||||
const ThroughPoint = wallIntersect.object.userData.path.getPoints(Constants.lineConfig.lineIntersectionPoints);
|
const ThroughPoint = wallIntersect.object.userData.path.getPoints(Constants.lineConfig.lineIntersectionPoints);
|
||||||
let intersectionPoint = getClosestIntersection(ThroughPoint, wallIntersect.point);
|
let intersectionPoint = getClosestIntersection(ThroughPoint, wallIntersect.point);
|
||||||
@@ -294,7 +295,9 @@ function WallCreator() {
|
|||||||
position: [position.x, position.y, position.z],
|
position: [position.x, position.y, position.z],
|
||||||
layer: activeLayer
|
layer: activeLayer
|
||||||
};
|
};
|
||||||
|
console.log('newPoint: ', newPoint);
|
||||||
|
|
||||||
|
console.log('snappedPoint: ', snappedPoint);
|
||||||
if (snappedPosition && snappedPoint) {
|
if (snappedPosition && snappedPoint) {
|
||||||
newPoint.pointUuid = snappedPoint.pointUuid;
|
newPoint.pointUuid = snappedPoint.pointUuid;
|
||||||
newPoint.position = snappedPosition;
|
newPoint.position = snappedPosition;
|
||||||
@@ -316,6 +319,7 @@ function WallCreator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('tempPoints: ', tempPoints);
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -330,6 +334,7 @@ function WallCreator() {
|
|||||||
decals: []
|
decals: []
|
||||||
};
|
};
|
||||||
addWall(wall);
|
addWall(wall);
|
||||||
|
console.log('wall: ', wall);
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useActiveTool, useToggleView } from '../../../store/builder/store';
|
import { useToggleView } from '../../../store/builder/store';
|
||||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||||
import { useVersionContext } from '../version/versionContext';
|
import { useVersionContext } from '../version/versionContext';
|
||||||
import { useSceneContext } from '../../scene/sceneContext';
|
import { useSceneContext } from '../../scene/sceneContext';
|
||||||
@@ -14,7 +14,6 @@ function WallGroup() {
|
|||||||
const { togglView } = useToggleView();
|
const { togglView } = useToggleView();
|
||||||
const { setSelectedWall, setSelectedDecal } = useBuilderStore();
|
const { setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { activeTool } = useActiveTool();
|
|
||||||
const { selectedVersionStore } = useVersionContext();
|
const { selectedVersionStore } = useVersionContext();
|
||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { wallStore } = useSceneContext();
|
const { wallStore } = useSceneContext();
|
||||||
@@ -26,7 +25,7 @@ function WallGroup() {
|
|||||||
setSelectedWall(null);
|
setSelectedWall(null);
|
||||||
setSelectedDecal(null);
|
setSelectedDecal(null);
|
||||||
}
|
}
|
||||||
}, [togglView, activeModule, activeTool])
|
}, [togglView, activeModule])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (projectId && selectedVersion) {
|
if (projectId && selectedVersion) {
|
||||||
|
|||||||
@@ -1,313 +0,0 @@
|
|||||||
import * as THREE from 'three';
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
||||||
import { useThree } from '@react-three/fiber';
|
|
||||||
import { Base, Geometry, Subtraction } from '@react-three/csg';
|
|
||||||
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
|
|
||||||
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
||||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
|
||||||
import useModuleStore from '../../../../../store/useModuleStore';
|
|
||||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
|
||||||
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
|
||||||
import { useActiveTool, useSocketStore, useToggleView } from '../../../../../store/builder/store';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { useVersionContext } from '../../../version/versionContext';
|
|
||||||
import { getUserData } from '../../../../../functions/getUserData';
|
|
||||||
import closestPointOnLineSegment from '../../../line/helpers/getClosestPointOnLineSegment';
|
|
||||||
|
|
||||||
import { upsertWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
|
||||||
import { deleteWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
|
|
||||||
|
|
||||||
function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|
||||||
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
|
||||||
const { socket } = useSocketStore();
|
|
||||||
const { raycaster, pointer, camera, scene, controls, gl } = useThree();
|
|
||||||
const { wallStore, wallAssetStore } = useSceneContext();
|
|
||||||
const { walls, getWallById } = wallStore();
|
|
||||||
const { updateWallAsset, removeWallAsset } = wallAssetStore();
|
|
||||||
const { toggleView } = useToggleView();
|
|
||||||
const { activeTool } = useActiveTool();
|
|
||||||
const { activeModule } = useModuleStore();
|
|
||||||
const { selectedWallAsset, setSelectedWallAsset, setDeletableWallAsset, deletableWallAsset } = useBuilderStore();
|
|
||||||
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
|
|
||||||
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
|
|
||||||
const groupRef = useRef<THREE.Group>(null);
|
|
||||||
const wall = useMemo(() => getWallById(wallAsset.wallUuid), [getWallById, wallAsset.wallUuid, walls]);
|
|
||||||
const draggingRef = useRef(false);
|
|
||||||
const { selectedVersionStore } = useVersionContext();
|
|
||||||
const { selectedVersion } = selectedVersionStore();
|
|
||||||
const { userId, organization } = getUserData();
|
|
||||||
const { projectId } = useParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const loader = new GLTFLoader();
|
|
||||||
const dracoLoader = new DRACOLoader();
|
|
||||||
|
|
||||||
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
|
||||||
loader.setDRACOLoader(dracoLoader);
|
|
||||||
const loadModel = async () => {
|
|
||||||
try {
|
|
||||||
// Check Cache
|
|
||||||
const assetId = wallAsset.assetId;
|
|
||||||
const cachedModel = THREE.Cache.get(assetId);
|
|
||||||
if (cachedModel) {
|
|
||||||
setGltfScene(cachedModel.scene.clone());
|
|
||||||
calculateBoundingBox(cachedModel.scene);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check IndexedDB
|
|
||||||
const indexedDBModel = await retrieveGLTF(assetId);
|
|
||||||
if (indexedDBModel) {
|
|
||||||
const blobUrl = URL.createObjectURL(indexedDBModel);
|
|
||||||
loader.load(blobUrl, (gltf) => {
|
|
||||||
URL.revokeObjectURL(blobUrl);
|
|
||||||
THREE.Cache.remove(blobUrl);
|
|
||||||
THREE.Cache.add(assetId, gltf);
|
|
||||||
setGltfScene(gltf.scene.clone());
|
|
||||||
calculateBoundingBox(gltf.scene);
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
(error) => {
|
|
||||||
echo.error(`[IndexedDB] Error loading ${wallAsset.modelName}:`);
|
|
||||||
URL.revokeObjectURL(blobUrl);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch from Backend
|
|
||||||
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`;
|
|
||||||
const handleBackendLoad = async (gltf: GLTF) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(modelUrl);
|
|
||||||
const modelBlob = await response.blob();
|
|
||||||
await storeGLTF(assetId, modelBlob);
|
|
||||||
THREE.Cache.add(assetId, gltf);
|
|
||||||
setGltfScene(gltf.scene.clone());
|
|
||||||
calculateBoundingBox(gltf.scene);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`[Backend] Error storing/loading ${wallAsset.modelName}:`, error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
loader.load(
|
|
||||||
modelUrl,
|
|
||||||
handleBackendLoad,
|
|
||||||
undefined,
|
|
||||||
(error) => {
|
|
||||||
echo.error(`[Backend] Error loading ${wallAsset.modelName}:`);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Failed to load model:", wallAsset.assetId, err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const calculateBoundingBox = (scene: THREE.Object3D) => {
|
|
||||||
const box = new THREE.Box3().setFromObject(scene);
|
|
||||||
setBoundingBox(box);
|
|
||||||
};
|
|
||||||
|
|
||||||
loadModel();
|
|
||||||
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const canvasElement = gl.domElement;
|
|
||||||
|
|
||||||
const onPointerUp = () => {
|
|
||||||
draggingRef.current = false;
|
|
||||||
if (controls) {
|
|
||||||
(controls as any).enabled = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPointerMove = (e: any) => {
|
|
||||||
if (!draggingRef.current || !wall || !selectedWallAsset) return;
|
|
||||||
if (controls) {
|
|
||||||
(controls as any).enabled = false;
|
|
||||||
}
|
|
||||||
pointer.x = (e.clientX / window.innerWidth) * 2 - 1;
|
|
||||||
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
|
||||||
|
|
||||||
raycaster.setFromCamera(pointer, camera);
|
|
||||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
|
||||||
const intersect = intersects.find((i: any) => i.object.name.includes('WallReference'));
|
|
||||||
|
|
||||||
if (intersect && intersect.object.userData.wallUuid) {
|
|
||||||
const newPoint = closestPointOnLineSegment(
|
|
||||||
new THREE.Vector3(intersect.point.x, 0, intersect.point.z),
|
|
||||||
new THREE.Vector3(...intersect.object.userData.points[0].position),
|
|
||||||
new THREE.Vector3(...intersect.object.userData.points[1].position)
|
|
||||||
);
|
|
||||||
|
|
||||||
const wallRotation = intersect.object.rotation.clone();
|
|
||||||
|
|
||||||
const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, {
|
|
||||||
wallUuid: intersect.object.userData.wallUuid,
|
|
||||||
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z],
|
|
||||||
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (projectId && updatedWallAsset) {
|
|
||||||
|
|
||||||
// API
|
|
||||||
|
|
||||||
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
|
|
||||||
|
|
||||||
// SOCKET
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
wallAssetData: updatedWallAsset,
|
|
||||||
projectId: projectId,
|
|
||||||
versionId: selectedVersion?.versionId || '',
|
|
||||||
userId: userId,
|
|
||||||
organization: organization
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit('v1:wall-asset:add', data);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (selectedWallAsset && !toggleView && activeModule === 'builder') {
|
|
||||||
canvasElement.addEventListener('mousemove', onPointerMove);
|
|
||||||
canvasElement.addEventListener('pointerup', onPointerUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
canvasElement.removeEventListener('mousemove', onPointerMove);
|
|
||||||
canvasElement.removeEventListener('pointerup', onPointerUp);
|
|
||||||
};
|
|
||||||
|
|
||||||
}, [gl, camera, toggleView, activeModule, selectedWallAsset, socket])
|
|
||||||
|
|
||||||
const handlePointerClick = useCallback((wallAsset: WallAsset) => {
|
|
||||||
if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
|
||||||
const removedWallAsset = removeWallAsset(wallAsset.modelUuid);
|
|
||||||
|
|
||||||
if (projectId && removedWallAsset) {
|
|
||||||
|
|
||||||
// API
|
|
||||||
|
|
||||||
// deleteWallAssetApi(projectId, selectedVersion?.versionId || '', removedWallAsset.modelUuid, removedWallAsset.wallUuid);
|
|
||||||
|
|
||||||
// SOCKET
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
projectId: projectId,
|
|
||||||
versionId: selectedVersion?.versionId || '',
|
|
||||||
userId: userId,
|
|
||||||
organization: organization,
|
|
||||||
modelUuid: removedWallAsset.modelUuid,
|
|
||||||
wallUuid: removedWallAsset.wallUuid
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit('v1:wall-asset:delete', data);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [activeTool, activeModule, deletableWallAsset, socket])
|
|
||||||
|
|
||||||
const handlePointerOver = useCallback((wallAsset: WallAsset, currentObject: THREE.Object3D) => {
|
|
||||||
if (activeTool === "delete" && activeModule === 'builder') {
|
|
||||||
if (deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
setDeletableWallAsset(currentObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [activeTool, activeModule, deletableWallAsset]);
|
|
||||||
|
|
||||||
const handlePointerOut = useCallback((wallAsset: WallAsset) => {
|
|
||||||
if (activeTool === "delete" && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
|
||||||
setDeletableWallAsset(null);
|
|
||||||
}
|
|
||||||
}, [activeTool, deletableWallAsset]);
|
|
||||||
|
|
||||||
if (!gltfScene || !boundingBox || !wall) { return null }
|
|
||||||
const size = new THREE.Vector3();
|
|
||||||
boundingBox.getSize(size);
|
|
||||||
const center = new THREE.Vector3();
|
|
||||||
boundingBox.getCenter(center);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<group
|
|
||||||
key={wallAsset.modelUuid}
|
|
||||||
name={wallAsset.modelName}
|
|
||||||
ref={groupRef}
|
|
||||||
position={wallAsset.position}
|
|
||||||
rotation={wallAsset.rotation}
|
|
||||||
visible={wallAsset.isVisible}
|
|
||||||
userData={wallAsset}
|
|
||||||
>
|
|
||||||
<Subtraction position={[center.x, center.y, center.z]} scale={[size.x, size.y, wall.wallThickness + 0.05]}>
|
|
||||||
<Geometry>
|
|
||||||
<Base geometry={new THREE.BoxGeometry()} />
|
|
||||||
</Geometry>
|
|
||||||
</Subtraction>
|
|
||||||
{gltfScene && (
|
|
||||||
<mesh
|
|
||||||
onPointerDown={(e) => {
|
|
||||||
if (!toggleView && activeModule === 'builder' && selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
|
||||||
draggingRef.current = true;
|
|
||||||
e.stopPropagation();
|
|
||||||
setSelectedWallAsset(groupRef.current);
|
|
||||||
if (controls) {
|
|
||||||
(controls as any).enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
|
||||||
if (!toggleView && activeModule === 'builder' && activeTool !== 'delete') {
|
|
||||||
if (e.object) {
|
|
||||||
e.stopPropagation();
|
|
||||||
let currentObject = e.object as THREE.Object3D;
|
|
||||||
while (currentObject) {
|
|
||||||
if (currentObject.name === "Scene") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
currentObject = currentObject.parent as THREE.Object3D;
|
|
||||||
}
|
|
||||||
setSelectedWallAsset(currentObject);
|
|
||||||
}
|
|
||||||
} else if (!toggleView && activeModule === 'builder' && activeTool === 'delete') {
|
|
||||||
handlePointerClick(wallAsset);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onPointerMissed={() => {
|
|
||||||
if (selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
|
||||||
setSelectedWallAsset(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onPointerEnter={(e) => {
|
|
||||||
if (!toggleView) {
|
|
||||||
e.stopPropagation();
|
|
||||||
let currentObject = e.object as THREE.Object3D;
|
|
||||||
while (currentObject) {
|
|
||||||
if (currentObject.name === "Scene") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
currentObject = currentObject.parent as THREE.Object3D;
|
|
||||||
}
|
|
||||||
handlePointerOver(wallAsset, currentObject);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onPointerOut={(e) => {
|
|
||||||
if (!toggleView) {
|
|
||||||
e.stopPropagation();
|
|
||||||
handlePointerOut(wallAsset);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
userData={wallAsset}
|
|
||||||
>
|
|
||||||
<primitive userData={wallAsset} object={gltfScene} />
|
|
||||||
</mesh>
|
|
||||||
)}
|
|
||||||
</group>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WallAssetInstance
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { useSceneContext } from '../../../scene/sceneContext'
|
|
||||||
import { useToggleView } from '../../../../store/builder/store';
|
|
||||||
import WallAssetInstance from './Instances/wallAssetInstance';
|
|
||||||
|
|
||||||
function WallAssetInstances() {
|
|
||||||
const { wallAssetStore } = useSceneContext();
|
|
||||||
const { wallAssets } = wallAssetStore();
|
|
||||||
const { toggleView } = useToggleView();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// console.log('wallAssets: ', wallAssets);
|
|
||||||
}, [wallAssets])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
|
|
||||||
{!toggleView && wallAssets.length > 0 && (
|
|
||||||
<>
|
|
||||||
{wallAssets.map((wallAsset) => (
|
|
||||||
<WallAssetInstance key={wallAsset.modelUuid} wallAsset={wallAsset} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WallAssetInstances
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import { useThree } from '@react-three/fiber';
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
import useModuleStore from '../../../store/useModuleStore';
|
|
||||||
import { useSelectedItem, useSocketStore, useToggleView } from '../../../store/builder/store';
|
|
||||||
import { MathUtils, Vector3 } from 'three';
|
|
||||||
import { useSceneContext } from '../../scene/sceneContext';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { useVersionContext } from '../version/versionContext';
|
|
||||||
import { getUserData } from '../../../functions/getUserData';
|
|
||||||
import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegment';
|
|
||||||
|
|
||||||
import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
|
||||||
|
|
||||||
function WallAssetCreator() {
|
|
||||||
const { socket } = useSocketStore();
|
|
||||||
const { pointer, camera, raycaster, scene, gl } = useThree();
|
|
||||||
const { togglView } = useToggleView();
|
|
||||||
const { activeModule } = useModuleStore();
|
|
||||||
const { wallAssetStore } = useSceneContext();
|
|
||||||
const { addWallAsset } = wallAssetStore();
|
|
||||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
|
||||||
const { selectedVersionStore } = useVersionContext();
|
|
||||||
const { selectedVersion } = selectedVersionStore();
|
|
||||||
const { userId, organization } = getUserData();
|
|
||||||
const { projectId } = useParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const canvasElement = gl.domElement;
|
|
||||||
|
|
||||||
const onDrop = (event: DragEvent) => {
|
|
||||||
if (!event.dataTransfer?.files[0]) return;
|
|
||||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category === 'Fenestration') {
|
|
||||||
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
|
||||||
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
|
||||||
|
|
||||||
raycaster.setFromCamera(pointer, camera);
|
|
||||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
|
||||||
const intersect = intersects.find((intersect) => intersect.object.name.includes('WallReference'));
|
|
||||||
|
|
||||||
if (intersect) {
|
|
||||||
const wall = intersect.object.userData as Wall;
|
|
||||||
const closestPoint = closestPointOnLineSegment(
|
|
||||||
new Vector3(intersect.point.x, 0, intersect.point.z),
|
|
||||||
new Vector3(...wall.points[0].position),
|
|
||||||
new Vector3(...wall.points[1].position)
|
|
||||||
)
|
|
||||||
|
|
||||||
const wallRotation = intersect.object.rotation.clone();
|
|
||||||
|
|
||||||
const newWallAsset: WallAsset = {
|
|
||||||
modelName: selectedItem.name,
|
|
||||||
modelUuid: MathUtils.generateUUID(),
|
|
||||||
wallUuid: wall.wallUuid,
|
|
||||||
wallAssetType: selectedItem.subCategory,
|
|
||||||
assetId: selectedItem.id,
|
|
||||||
position: [closestPoint.x, selectedItem.subCategory === "fixed-move" ? 0 : intersect.point.y, closestPoint.z],
|
|
||||||
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
|
||||||
isLocked: false,
|
|
||||||
isVisible: true,
|
|
||||||
opacity: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
addWallAsset(newWallAsset);
|
|
||||||
if (projectId) {
|
|
||||||
|
|
||||||
// API
|
|
||||||
|
|
||||||
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', newWallAsset);
|
|
||||||
|
|
||||||
// SOCKET
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
wallAssetData: newWallAsset,
|
|
||||||
projectId: projectId,
|
|
||||||
versionId: selectedVersion?.versionId || '',
|
|
||||||
userId: userId,
|
|
||||||
organization: organization
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit('v1:wall-asset:add', data);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!togglView && activeModule === 'builder') {
|
|
||||||
canvasElement.addEventListener('drop', onDrop);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
canvasElement.removeEventListener('drop', onDrop);
|
|
||||||
};
|
|
||||||
|
|
||||||
}, [gl, camera, togglView, activeModule, socket, selectedItem, setSelectedItem]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WallAssetCreator
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { useActiveTool, useToggleView } from '../../../store/builder/store';
|
|
||||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
|
||||||
import { useVersionContext } from '../version/versionContext';
|
|
||||||
import { useSceneContext } from '../../scene/sceneContext';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import useModuleStore from '../../../store/useModuleStore';
|
|
||||||
import WallAssetCreator from './wallAssetCreator'
|
|
||||||
import WallAssetInstances from './Instances/wallAssetInstances'
|
|
||||||
import { getWallAssetsApi } from '../../../services/factoryBuilder/asset/wallAsset/getWallAssetsApi';
|
|
||||||
|
|
||||||
function WallAssetGroup() {
|
|
||||||
const { togglView } = useToggleView();
|
|
||||||
const { setSelectedFloorAsset, setDeletableWallAsset } = useBuilderStore();
|
|
||||||
const { activeModule } = useModuleStore();
|
|
||||||
const { activeTool } = useActiveTool();
|
|
||||||
const { selectedVersionStore } = useVersionContext();
|
|
||||||
const { selectedVersion } = selectedVersionStore();
|
|
||||||
const { wallAssetStore } = useSceneContext();
|
|
||||||
const { setWallAssets } = wallAssetStore();
|
|
||||||
const { projectId } = useParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (togglView || activeModule !== 'builder') {
|
|
||||||
setSelectedFloorAsset(null);
|
|
||||||
}
|
|
||||||
setDeletableWallAsset(null);
|
|
||||||
}, [togglView, activeModule, activeTool])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (projectId && selectedVersion) {
|
|
||||||
getWallAssetsApi(projectId, selectedVersion?.versionId || '').then((wallAssets) => {
|
|
||||||
console.log('wallAssets: ', wallAssets);
|
|
||||||
if (wallAssets && wallAssets.length > 0) {
|
|
||||||
setWallAssets(wallAssets);
|
|
||||||
} else {
|
|
||||||
setWallAssets([]);
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.log(err);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [projectId, selectedVersion?.versionId])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
|
|
||||||
<WallAssetCreator />
|
|
||||||
|
|
||||||
<WallAssetInstances />
|
|
||||||
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WallAssetGroup
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useActiveTool, useToggleView } from '../../../store/builder/store';
|
import { useToggleView } from '../../../store/builder/store';
|
||||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||||
import { useVersionContext } from '../version/versionContext';
|
import { useVersionContext } from '../version/versionContext';
|
||||||
import { useSceneContext } from '../../scene/sceneContext';
|
import { useSceneContext } from '../../scene/sceneContext';
|
||||||
@@ -14,7 +14,6 @@ function ZoneGroup() {
|
|||||||
const { togglView } = useToggleView();
|
const { togglView } = useToggleView();
|
||||||
const { setSelectedZone } = useBuilderStore();
|
const { setSelectedZone } = useBuilderStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { activeTool } = useActiveTool();
|
|
||||||
const { selectedVersionStore } = useVersionContext();
|
const { selectedVersionStore } = useVersionContext();
|
||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { zoneStore } = useSceneContext();
|
const { zoneStore } = useSceneContext();
|
||||||
@@ -25,11 +24,12 @@ function ZoneGroup() {
|
|||||||
if (togglView || activeModule !== 'builder') {
|
if (togglView || activeModule !== 'builder') {
|
||||||
setSelectedZone(null);
|
setSelectedZone(null);
|
||||||
}
|
}
|
||||||
}, [togglView, activeModule, activeTool])
|
}, [togglView, activeModule])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (projectId && selectedVersion) {
|
if (projectId && selectedVersion) {
|
||||||
getZonesApi(projectId, selectedVersion?.versionId || '').then((zones) => {
|
getZonesApi(projectId, selectedVersion?.versionId || '').then((zones) => {
|
||||||
|
console.log('zones: ', zones);
|
||||||
if (zones && zones.length > 0) {
|
if (zones && zones.length > 0) {
|
||||||
setZones(zones);
|
setZones(zones);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { useSocketStore } from '../../../store/builder/store';
|
|
||||||
|
|
||||||
export default function SocketResponses() {
|
export default function SocketResponses() {
|
||||||
const { socket } = useSocketStore();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
socket.on("v1:wall-asset:response:delete", (data: any) => {
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
socket.off("v1:wall-asset:response:delete");
|
|
||||||
}
|
|
||||||
}, [socket])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ const SelectionControls: React.FC = () => {
|
|||||||
selectedObjects.forEach((object) => {
|
selectedObjects.forEach((object) => {
|
||||||
let currentObject: THREE.Object3D | null = object;
|
let currentObject: THREE.Object3D | null = object;
|
||||||
while (currentObject) {
|
while (currentObject) {
|
||||||
if (currentObject.userData.modelUuid && !currentObject.userData.wallAssetType) {
|
if (currentObject.userData.modelUuid) {
|
||||||
Objects.add(currentObject);
|
Objects.add(currentObject);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { useTileDistance, useToggleView } from "../../../store/builder/store";
|
import { useTileDistance, useToggleView } from "../../../store/builder/store";
|
||||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
|
|
||||||
const Ground = ({ plane }: any) => {
|
const Ground = ({ grid, plane }: any) => {
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { planeValue, gridValue } = useTileDistance();
|
const { planeValue, gridValue } = useTileDistance();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh name="Ground">
|
<mesh name="Ground">
|
||||||
<mesh
|
<mesh
|
||||||
|
ref={grid}
|
||||||
name="Grid"
|
name="Grid"
|
||||||
position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}
|
position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function PostProcessing() {
|
|||||||
const { selectedWallItem } = useSelectedWallItem();
|
const { selectedWallItem } = useSelectedWallItem();
|
||||||
const { selectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem } = useSelectedFloorItem();
|
||||||
const { selectedEventSphere } = useSelectedEventSphere();
|
const { selectedEventSphere } = useSelectedEventSphere();
|
||||||
const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset } = useBuilderStore();
|
const { selectedAisle, selectedWall, selectedDecal, selectedFloor } = useBuilderStore();
|
||||||
|
|
||||||
function flattenChildren(children: any[]) {
|
function flattenChildren(children: any[]) {
|
||||||
const allChildren: any[] = [];
|
const allChildren: any[] = [];
|
||||||
@@ -48,14 +48,6 @@ export default function PostProcessing() {
|
|||||||
// console.log('selectedFloor: ', selectedFloor);
|
// console.log('selectedFloor: ', selectedFloor);
|
||||||
}, [selectedFloor])
|
}, [selectedFloor])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// console.log('selectedWallAsset: ', selectedWallAsset);
|
|
||||||
}, [selectedWallAsset])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// console.log('deletableWallAsset: ', deletableWallAsset);
|
|
||||||
}, [deletableWallAsset])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EffectComposer autoClear={false}>
|
<EffectComposer autoClear={false}>
|
||||||
<N8AO
|
<N8AO
|
||||||
@@ -67,36 +59,6 @@ export default function PostProcessing() {
|
|||||||
denoiseRadius={6}
|
denoiseRadius={6}
|
||||||
denoiseSamples={16}
|
denoiseSamples={16}
|
||||||
/>
|
/>
|
||||||
{selectedWallAsset && (
|
|
||||||
<Outline
|
|
||||||
selection={flattenChildren(selectedWallAsset.children)}
|
|
||||||
selectionLayer={10}
|
|
||||||
width={2000}
|
|
||||||
blendFunction={BlendFunction.ALPHA}
|
|
||||||
edgeStrength={5}
|
|
||||||
resolutionScale={2}
|
|
||||||
pulseSpeed={0}
|
|
||||||
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
|
|
||||||
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
|
|
||||||
blur={true}
|
|
||||||
xRay={true}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{deletableWallAsset && (
|
|
||||||
<Outline
|
|
||||||
selection={flattenChildren(deletableWallAsset.children)}
|
|
||||||
selectionLayer={10}
|
|
||||||
width={2000}
|
|
||||||
blendFunction={BlendFunction.ALPHA}
|
|
||||||
edgeStrength={5}
|
|
||||||
resolutionScale={2}
|
|
||||||
pulseSpeed={0}
|
|
||||||
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
|
|
||||||
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
|
|
||||||
blur={true}
|
|
||||||
xRay={true}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{selectedAisle && (
|
{selectedAisle && (
|
||||||
<Outline
|
<Outline
|
||||||
selection={flattenChildren(selectedAisle.children)}
|
selection={flattenChildren(selectedAisle.children)}
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
|
|||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
||||||
const updateProjects = {
|
const updateProjects = {
|
||||||
projectId: project?.projectUuid,
|
projectId: project.projectUuid,
|
||||||
organization,
|
organization,
|
||||||
userId,
|
userId,
|
||||||
projectName: project?.projectName,
|
projectName: project.projectName,
|
||||||
thumbnail: screenshotDataUrl,
|
thumbnail: screenshotDataUrl,
|
||||||
};
|
};
|
||||||
if (projectSocket) {
|
if (projectSocket) {
|
||||||
|
|||||||
@@ -12,13 +12,12 @@ interface PolygonGeneratorProps {
|
|||||||
export default function PolygonGenerator({
|
export default function PolygonGenerator({
|
||||||
groupRef,
|
groupRef,
|
||||||
}: PolygonGeneratorProps) {
|
}: PolygonGeneratorProps) {
|
||||||
const { aisleStore } = useSceneContext();
|
const { aisleStore, wallStore } = useSceneContext();
|
||||||
const { aisles } = aisleStore();
|
const { aisles } = aisleStore();
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
|
const { walls } = wallStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// let allLines = arrayLinesToObject(lines.current);
|
|
||||||
// const wallLines = allLines?.filter((line) => line?.type === "WallLine");
|
|
||||||
const result = aisles
|
const result = aisles
|
||||||
.filter(
|
.filter(
|
||||||
(aisle) =>
|
(aisle) =>
|
||||||
@@ -61,9 +60,12 @@ export default function PolygonGenerator({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// const wallPoints = wallLines
|
|
||||||
// .map((pair) => pair?.line.map((vals) => vals.position))
|
const wallPoints: THREE.Vector3[][] = walls
|
||||||
// .filter((wall): wall is THREE.Vector3[] => !!wall);
|
.map((wall) =>
|
||||||
|
wall.points.map((pt) => new THREE.Vector3(pt.position[0], pt.position[1], pt.position[2]))
|
||||||
|
)
|
||||||
|
.filter(points => points.length === 2);
|
||||||
|
|
||||||
|
|
||||||
if (!result || result.some((line) => !line)) {
|
if (!result || result.some((line) => !line)) {
|
||||||
@@ -81,7 +83,7 @@ export default function PolygonGenerator({
|
|||||||
|
|
||||||
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
|
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
|
||||||
|
|
||||||
// renderWallGeometry(wallPoints);
|
renderWallGeometry(wallPoints);
|
||||||
|
|
||||||
if (polygons.features.length > 0) {
|
if (polygons.features.length > 0) {
|
||||||
polygons.features.forEach((feature) => {
|
polygons.features.forEach((feature) => {
|
||||||
@@ -116,7 +118,7 @@ export default function PolygonGenerator({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [ aisles, scene]);
|
}, [aisles, scene, walls]);
|
||||||
|
|
||||||
const renderWallGeometry = (walls: THREE.Vector3[][]) => {
|
const renderWallGeometry = (walls: THREE.Vector3[][]) => {
|
||||||
walls.forEach((wall) => {
|
walls.forEach((wall) => {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import { SceneProvider } from "../modules/scene/sceneContext";
|
|||||||
import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/getVersionHistoryApi";
|
import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/getVersionHistoryApi";
|
||||||
import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore";
|
import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore";
|
||||||
import { VersionProvider } from "../modules/builder/version/versionContext";
|
import { VersionProvider } from "../modules/builder/version/versionContext";
|
||||||
import { recentlyViewed } from "../services/dashboard/recentlyViewed";
|
|
||||||
import { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject";
|
import { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject";
|
||||||
|
|
||||||
const Project: React.FC = () => {
|
const Project: React.FC = () => {
|
||||||
@@ -81,7 +80,6 @@ const Project: React.FC = () => {
|
|||||||
fetchProjects();
|
fetchProjects();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
getVersionHistoryApi(projectId).then((data) => {
|
getVersionHistoryApi(projectId).then((data) => {
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
|
||||||
|
|
||||||
export const deleteWallAssetApi = async (
|
|
||||||
projectId: string,
|
|
||||||
versionId: string,
|
|
||||||
modelUuid: string,
|
|
||||||
wallUuid: string,
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/delete/wallasset`, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer <access_token>",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
token: localStorage.getItem("token") || "",
|
|
||||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ projectId, versionId, modelUuid, wallUuid }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const newAccessToken = response.headers.get("x-access-token");
|
|
||||||
if (newAccessToken) {
|
|
||||||
localStorage.setItem("token", newAccessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Failed to delete wall asset:", response.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
echo.error("Failed to delete wall asset");
|
|
||||||
if (error instanceof Error) {
|
|
||||||
console.log(error.message);
|
|
||||||
} else {
|
|
||||||
console.log("An unknown error occurred");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
|
||||||
|
|
||||||
export const getWallAssetsApi = async (
|
|
||||||
projectId: string,
|
|
||||||
versionId: string,
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/wallassets/${projectId}/${versionId}`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer <access_token>",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
token: localStorage.getItem("token") || "",
|
|
||||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const newAccessToken = response.headers.get("x-access-token");
|
|
||||||
if (newAccessToken) {
|
|
||||||
localStorage.setItem("token", newAccessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Failed to get wall assets:", response.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
echo.error("Failed to get wall assets");
|
|
||||||
if (error instanceof Error) {
|
|
||||||
console.log(error.message);
|
|
||||||
} else {
|
|
||||||
console.log("An unknown error occurred");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
|
||||||
|
|
||||||
export const upsertWallAssetApi = async (
|
|
||||||
projectId: string,
|
|
||||||
versionId: string,
|
|
||||||
wallAssetData: WallAsset
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/wall-asset`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer <access_token>",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
token: localStorage.getItem("token") || "",
|
|
||||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ projectId, versionId, wallAssetData }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const newAccessToken = response.headers.get("x-access-token");
|
|
||||||
if (newAccessToken) {
|
|
||||||
localStorage.setItem("token", newAccessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Failed to upsert wall asset:", response.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
echo.error("Failed to upsert wall asset");
|
|
||||||
if (error instanceof Error) {
|
|
||||||
console.log(error.message);
|
|
||||||
} else {
|
|
||||||
console.log("An unknown error occurred");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -14,6 +14,11 @@ export const getProjectSharedList = async (projectId: string) => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
//console.log("New token received:", newAccessToken);
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to get users");
|
throw new Error("Failed to get users");
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ export const getSearchUsers = async (searchMail: string) => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
//console.log("New token received:", newAccessToken);
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to get users");
|
throw new Error("Failed to get users");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const shareAccess = async (
|
|||||||
const body: any = {
|
const body: any = {
|
||||||
projectId,
|
projectId,
|
||||||
targetUserId,
|
targetUserId,
|
||||||
newAccessPoint
|
newAccessPoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -26,6 +26,11 @@ export const shareAccess = async (
|
|||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
//console.log("New token received:", newAccessToken);
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.error("Failed to clearPanel in the zone");
|
console.error("Failed to clearPanel in the zone");
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export const shareProject = async (addUserId: string, projectId: string) => {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ addUserId, projectId }),
|
body: JSON.stringify({ addUserId, projectId }),
|
||||||
});
|
});
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
//console.log("New token received:", newAccessToken);
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.error("Failed to add project");
|
console.error("Failed to add project");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export const upsertZoneApi = async (
|
|||||||
zoneData: Zone
|
zoneData: Zone
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/upsertZone`, {
|
const response = await fetch(`${url_Backend_dwinzo}/api/V1/UpsertZone`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: "Bearer <access_token>",
|
Authorization: "Bearer <access_token>",
|
||||||
|
|||||||
@@ -587,7 +587,7 @@ export const useDfxUpload = create<any>((set: any) => ({
|
|||||||
dfxWallGenerate: [],
|
dfxWallGenerate: [],
|
||||||
objValue: { x: 0, y: 0, z: 0 },
|
objValue: { x: 0, y: 0, z: 0 },
|
||||||
setDfxUploaded: (x: any) => set({ dfxuploaded: x }),
|
setDfxUploaded: (x: any) => set({ dfxuploaded: x }),
|
||||||
setDfxGenerate: (x: any) => set({ dfxWallGenerate: x }),
|
setDxfWallGenerate: (x: any) => set({ dfxWallGenerate: x }),
|
||||||
setObjValue: (x: any) => set({ objValue: x }),
|
setObjValue: (x: any) => set({ objValue: x }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ interface AssetsStore {
|
|||||||
setOpacity: (modelUuid: string, opacity: number) => void;
|
setOpacity: (modelUuid: string, opacity: number) => void;
|
||||||
|
|
||||||
// Animation controls
|
// Animation controls
|
||||||
setAnimations: (modelUuid: string, animations: string[]) => void;
|
setAnimation: (modelUuid: string, animation: string) => void;
|
||||||
setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void;
|
setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void;
|
||||||
addAnimation: (modelUuid: string, animation: string) => void;
|
addAnimation: (modelUuid: string, animation: string) => void;
|
||||||
removeAnimation: (modelUuid: string, animation: string) => void;
|
removeAnimation: (modelUuid: string, animation: string) => void;
|
||||||
@@ -143,13 +143,14 @@ export const createAssetStore = () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Animation controls
|
// Animation controls
|
||||||
setAnimations: (modelUuid, animations) => {
|
setAnimation: (modelUuid, animation) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
asset.animations = animations;
|
|
||||||
if (!asset.animationState) {
|
if (!asset.animationState) {
|
||||||
asset.animationState = { current: '', playing: false };
|
asset.animationState = { current: animation, playing: false };
|
||||||
|
} else {
|
||||||
|
asset.animationState.current = animation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,13 +9,6 @@ interface BuilderState {
|
|||||||
snappedPosition: [number, number, number] | null;
|
snappedPosition: [number, number, number] | null;
|
||||||
hoveredLine: [Point, Point] | null;
|
hoveredLine: [Point, Point] | null;
|
||||||
|
|
||||||
// Wall Asset
|
|
||||||
selectedWallAsset: Object3D | null;
|
|
||||||
deletableWallAsset: Object3D | null;
|
|
||||||
|
|
||||||
// Floor Asset
|
|
||||||
selectedFloorAsset: Object3D | null;
|
|
||||||
|
|
||||||
// Wall Settings
|
// Wall Settings
|
||||||
selectedWall: Object3D | null;
|
selectedWall: Object3D | null;
|
||||||
wallThickness: number;
|
wallThickness: number;
|
||||||
@@ -58,13 +51,6 @@ interface BuilderState {
|
|||||||
setSnappedPosition: (position: [number, number, number] | null) => void;
|
setSnappedPosition: (position: [number, number, number] | null) => void;
|
||||||
setHoveredLine: (line: [Point, Point] | null) => void;
|
setHoveredLine: (line: [Point, Point] | null) => void;
|
||||||
|
|
||||||
// Setters - Wall Asset
|
|
||||||
setSelectedWallAsset: (asset: Object3D | null) => void;
|
|
||||||
setDeletableWallAsset: (asset: Object3D | null) => void;
|
|
||||||
|
|
||||||
// Setters - Floor Asset
|
|
||||||
setSelectedFloorAsset: (asset: Object3D | null) => void;
|
|
||||||
|
|
||||||
// Setters - Wall
|
// Setters - Wall
|
||||||
setSelectedWall: (wall: Object3D | null) => void;
|
setSelectedWall: (wall: Object3D | null) => void;
|
||||||
setWallThickness: (thickness: number) => void;
|
setWallThickness: (thickness: number) => void;
|
||||||
@@ -114,11 +100,6 @@ export const useBuilderStore = create<BuilderState>()(
|
|||||||
snappedPosition: null,
|
snappedPosition: null,
|
||||||
hoveredLine: null,
|
hoveredLine: null,
|
||||||
|
|
||||||
selectedWallAsset: null,
|
|
||||||
deletableWallAsset: null,
|
|
||||||
|
|
||||||
selectedFloorAsset: null,
|
|
||||||
|
|
||||||
selectedWall: null,
|
selectedWall: null,
|
||||||
wallThickness: 0.5,
|
wallThickness: 0.5,
|
||||||
wallHeight: 7,
|
wallHeight: 7,
|
||||||
@@ -175,28 +156,6 @@ export const useBuilderStore = create<BuilderState>()(
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// === Setters: Wall Asset ===
|
|
||||||
|
|
||||||
setSelectedWallAsset(asset: Object3D | null) {
|
|
||||||
set((state) => {
|
|
||||||
state.selectedWallAsset = asset;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setDeletableWallAsset(asset: Object3D | null) {
|
|
||||||
set((state) => {
|
|
||||||
state.deletableWallAsset = asset;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Setters: Floor Asset ===
|
|
||||||
|
|
||||||
setSelectedFloorAsset(asset: Object3D | null) {
|
|
||||||
set((state) => {
|
|
||||||
state.selectedFloorAsset = asset;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Setters: Wall ===
|
// === Setters: Wall ===
|
||||||
|
|
||||||
setSelectedWall: (wall: Object3D | null) => {
|
setSelectedWall: (wall: Object3D | null) => {
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ interface WallAssetStore {
|
|||||||
wallAssets: WallAsset[];
|
wallAssets: WallAsset[];
|
||||||
setWallAssets: (assets: WallAsset[]) => void;
|
setWallAssets: (assets: WallAsset[]) => void;
|
||||||
addWallAsset: (asset: WallAsset) => void;
|
addWallAsset: (asset: WallAsset) => void;
|
||||||
updateWallAsset: (uuid: string, updated: Partial<WallAsset>) => WallAsset | undefined;
|
updateWallAsset: (uuid: string, updated: Partial<WallAsset>) => void;
|
||||||
setWallAssetPosition: (uuid: string, position: [number, number, number]) => void;
|
removeWallAsset: (uuid: string) => void;
|
||||||
removeWallAsset: (uuid: string) => WallAsset | undefined;
|
|
||||||
clearWallAssets: () => void;
|
clearWallAssets: () => void;
|
||||||
|
|
||||||
setVisibility: (uuid: string, isVisible: boolean) => void;
|
setVisibility: (uuid: string, isVisible: boolean) => void;
|
||||||
@@ -31,36 +30,16 @@ export const createWallAssetStore = () => {
|
|||||||
state.wallAssets.push(asset);
|
state.wallAssets.push(asset);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateWallAsset: (uuid, updated) => {
|
updateWallAsset: (uuid, updated) => set(state => {
|
||||||
let updatedWallAsset: WallAsset | undefined;
|
|
||||||
set(state => {
|
|
||||||
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
|
||||||
if (asset) {
|
|
||||||
Object.assign(asset, updated);
|
|
||||||
updatedWallAsset = JSON.parse(JSON.stringify(asset));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return updatedWallAsset;
|
|
||||||
},
|
|
||||||
|
|
||||||
setWallAssetPosition: (uuid, position) => set(state => {
|
|
||||||
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
asset.position = position;
|
Object.assign(asset, updated);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
removeWallAsset: (uuid) => {
|
removeWallAsset: (uuid) => set(state => {
|
||||||
let removedAsset: WallAsset | undefined;
|
state.wallAssets = state.wallAssets.filter(a => a.modelUuid !== uuid);
|
||||||
set(state => {
|
}),
|
||||||
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
|
||||||
if (asset) {
|
|
||||||
removedAsset = JSON.parse(JSON.stringify(asset));
|
|
||||||
state.wallAssets = state.wallAssets.filter(a => a.modelUuid !== uuid);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return removedAsset;
|
|
||||||
},
|
|
||||||
|
|
||||||
clearWallAssets: () => {
|
clearWallAssets: () => {
|
||||||
set(state => {
|
set(state => {
|
||||||
|
|||||||
2
app/src/types/builderTypes.d.ts
vendored
2
app/src/types/builderTypes.d.ts
vendored
@@ -51,11 +51,9 @@ type Assets = Asset[];
|
|||||||
interface WallAsset {
|
interface WallAsset {
|
||||||
modelUuid: string;
|
modelUuid: string;
|
||||||
modelName: string;
|
modelName: string;
|
||||||
wallAssetType: string;
|
|
||||||
assetId: string;
|
assetId: string;
|
||||||
wallUuid: string;
|
wallUuid: string;
|
||||||
position: [number, number, number];
|
position: [number, number, number];
|
||||||
rotation: [number, number, number];
|
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
opacity: number;
|
opacity: number;
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import useModuleStore, { useSubModuleStore, useThreeDStore } from "../../store/useModuleStore";
|
import useModuleStore, {
|
||||||
|
useSubModuleStore,
|
||||||
|
useThreeDStore,
|
||||||
|
} from "../../store/useModuleStore";
|
||||||
import { usePlayerStore, useToggleStore } from "../../store/useUIToggleStore";
|
import { usePlayerStore, useToggleStore } from "../../store/useUIToggleStore";
|
||||||
import useVersionHistoryVisibleStore, {
|
import useVersionHistoryVisibleStore, {
|
||||||
useActiveSubTool,
|
useActiveSubTool,
|
||||||
useActiveTool,
|
useActiveTool,
|
||||||
useAddAction,
|
useAddAction,
|
||||||
|
useDfxUpload,
|
||||||
useRenameModeStore,
|
useRenameModeStore,
|
||||||
useSaveVersion,
|
useSaveVersion,
|
||||||
useSelectedComment,
|
useSelectedComment,
|
||||||
@@ -49,7 +53,7 @@ const KeyPressListener: React.FC = () => {
|
|||||||
const { setCreateNewVersion } = useVersionHistoryStore();
|
const { setCreateNewVersion } = useVersionHistoryStore();
|
||||||
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
||||||
const { setSelectedComment } = useSelectedComment();
|
const { setSelectedComment } = useSelectedComment();
|
||||||
|
const { setDfxUploaded, setDxfWallGenerate } = useDfxUpload();
|
||||||
const isTextInput = (element: Element | null): boolean =>
|
const isTextInput = (element: Element | null): boolean =>
|
||||||
element instanceof HTMLInputElement ||
|
element instanceof HTMLInputElement ||
|
||||||
element instanceof HTMLTextAreaElement ||
|
element instanceof HTMLTextAreaElement ||
|
||||||
@@ -160,14 +164,14 @@ const KeyPressListener: React.FC = () => {
|
|||||||
setCreateNewVersion(true);
|
setCreateNewVersion(true);
|
||||||
setVersionHistoryVisible(true);
|
setVersionHistoryVisible(true);
|
||||||
setSubModule("properties");
|
setSubModule("properties");
|
||||||
setActiveModule('builder');
|
setActiveModule("builder");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Ctrl+H":
|
case "Ctrl+H":
|
||||||
if (!isPlaying) {
|
if (!isPlaying) {
|
||||||
setVersionHistoryVisible(true);
|
setVersionHistoryVisible(true);
|
||||||
setSubModule("properties");
|
setSubModule("properties");
|
||||||
setActiveModule('builder');
|
setActiveModule("builder");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -195,6 +199,7 @@ const KeyPressListener: React.FC = () => {
|
|||||||
clearComparisonProduct();
|
clearComparisonProduct();
|
||||||
setIsLogListVisible(false);
|
setIsLogListVisible(false);
|
||||||
setIsRenameMode(false);
|
setIsRenameMode(false);
|
||||||
|
setDfxUploaded([]);
|
||||||
setSelectedComment(null);
|
setSelectedComment(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user