Compare commits

...

20 Commits

Author SHA1 Message Date
64d086f808 feat: Implement wall asset management APIs and socket integration for create, update, and delete operations 2025-07-02 10:49:07 +05:30
d6f6c4c901 Merge remote-tracking branch 'origin/dev-collaboration' into dev-r3f-wall 2025-06-30 18:13:23 +05:30
0a039f34b1 feat: Integrate active tool management across builder components; add deletableWallAsset state and related functionality 2025-06-30 18:11:37 +05:30
364b643c72 feat: Enhance wall asset interaction and management; implement position updates and add closest point calculation utility 2025-06-30 17:48:29 +05:30
1a9aef323a feat: Add selectedWallAsset and selectedFloorAsset state management; implement corresponding setters in useBuilderStore 2025-06-30 16:59:27 +05:30
997775c27e feat: Implement wall asset management features including creation, instances, and rendering; enhance wall properties input validation 2025-06-30 16:21:54 +05:30
943ad3ba49 refactor: Update color scheme for Floor and Zone components; enhance snapping logic in ReferenceFloor and ReferenceZone 2025-06-30 12:54:25 +05:30
7124a819b6 feat: Implement zone management features including creation, deletion, and updates
- Added zone handling in the Line component for removing and updating zones based on points.
- Enhanced Point component to support snapping and updating zone positions.
- Introduced zone-related API calls for upserting and deleting zones.
- Updated zone store to manage zones more effectively, including methods for removing zones by points and getting zones by point IDs.
- Improved zone instance rendering to handle dynamic point connections.
- Refactored zone creation logic to allow for better interaction and snapping behavior.
- Updated API endpoints for zone operations to use version 1 of the API.
2025-06-30 12:22:42 +05:30
90df6c2b01 Refactor: Update useEffect dependencies to include activeLayer for ReferenceAisle, ReferenceFloor, ReferenceWall; enhance usePointSnapping with zone snapping functionality and add ReferenceZone component for zone management 2025-06-27 18:06:19 +05:30
fa6506c0be Refactor: Remove deprecated API endpoints and implement new zone management features
- Deleted obsolete API files for wall items, layers, lines, and points.
- Introduced new zone management APIs including create, delete, and update functionalities.
- Enhanced zone state management in the store with new properties for height and color.
- Implemented 2D and 3D zone rendering components for better visualization.
- Added asset fetching functionalities for marketplace integration.
- Updated types to accommodate new zone properties and API responses.
2025-06-27 17:51:57 +05:30
812a4f6aef Refactor wall creator and socket responses; update nav mesh components and remove unused pillar functions
- Updated import path for getClosestIntersection in wallCreator.tsx
- Cleaned up socketResponses.dev.tsx by removing unused imports and code, simplifying the component structure
- Modified navMesh.tsx to remove lines prop and adjust NavMeshDetails accordingly
- Refactored navMeshDetails.tsx to remove lines dependency in useEffect
- Updated polygonGenerator.tsx to remove lines prop and commented out unused code
- Added getClosestIntersection helper function for better modularity
- Removed unused pillar-related functions and commented out code in addAndUpdateReferencePillar.ts, addPillar.ts, deletableHoveredPillar.ts, deletePillar.ts, and updateReferencePolesheight.ts
2025-06-27 16:54:38 +05:30
bfa4d7bbc6 Add Floor2DInstance component and integrate into FloorInstances for 2D floor rendering 2025-06-27 16:10:53 +05:30
540bb44e0c Add floorName property to Floor interface and update related components for floor creation 2025-06-27 16:01:58 +05:30
12f1dd1771 Refactor interaction handling: change click events to double-click for DecalInstance, FloorInstance, and Wall components. Prevent event propagation to ensure proper selection behavior. 2025-06-27 15:58:34 +05:30
edf76fa1c9 Fix bounding box position calculations and update distance finding controls for accurate positioning based on geometry. Enhance move controls to correctly handle floor intersections and adjust object positioning accordingly. 2025-06-27 15:46:06 +05:30
c73bdf4556 completed floor 2025-06-27 15:44:31 +05:30
64f0cdb148 Refactor builder store and related components: update DecalInstance to manage selectedFloor state, enhance FloorInstance with improved material handling and texture application, and modify FloorGroup to remove unnecessary console log. Update Wall component for better shadow handling and adjust useBuilderStore to change default material values. 2025-06-27 11:56:54 +05:30
04c302ea4c Refactor floor management components and APIs: enhance FloorInstance for shape creation, update FloorInstances to handle floor rendering and UUID tracking, and improve FloorCreator with project and version context. Modify floor store methods for better floor management and integrate API calls for floor upsert and deletion. Adjust wall and point components to support new floor functionalities. 2025-06-27 10:48:48 +05:30
5003dc3504 Refactor floor management APIs: implement getFloorsApi, deleteFloorApi, and upsertFloorApi for enhanced floor data handling. Update FloorCreator for improved point snapping and floor creation logic. Modify ReferencePoint to include UUID and user data. Adjust usePointSnapping to handle temporary points more effectively. 2025-06-26 18:23:01 +05:30
1e88006780 Refactor aisle API services: remove createAisleApi, update deleteAisleApi to include versionId, and modify wall asset APIs for consistency in endpoint naming. Enhance floor and wall store functionality with new setters and methods for managing floors and walls, including decal handling. Introduce FloorInstance and FloorInstances components for rendering floor data, and implement FloorCreator for interactive floor creation. Add reference floor visualization with snapping capabilities. Update wall API services for improved error handling and response management. 2025-06-26 17:47:32 +05:30
168 changed files with 6426 additions and 8032 deletions

View File

@@ -33,7 +33,7 @@ import {
import { useProductContext } from "../../../modules/simulation/products/productContext";
import RegularDropDown from "../../ui/inputs/RegularDropDown";
import RenameTooltip from "../../ui/features/RenameTooltip";
import { setAssetsApi } from "../../../services/factoryBuilder/assest/floorAsset/setAssetsApi";
import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
import { useParams } from "react-router-dom";
import { useSceneContext } from "../../../modules/scene/sceneContext";
import { useVersionHistoryStore } from "../../../store/builder/useVersionHistoryStore";

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react";
import Search from "../../ui/inputs/Search";
import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets/getCategoryAsset";
import { getCategoryAsset } from "../../../services/factoryBuilder/asset/assets/getCategoryAsset";
import { fetchAssets } from "../../../services/marketplace/fetchAssets";
import { useSelectedItem } from "../../../store/builder/store";

View File

@@ -1,27 +1,28 @@
import React, { useEffect } from "react";
import Header from "./Header";
import useModuleStore, {
useSubModuleStore,
useSubModuleStore,
} from "../../../store/useModuleStore";
import {
AnalysisIcon,
MechanicsIcon,
PropertiesIcon,
SimulationIcon,
AnalysisIcon,
MechanicsIcon,
PropertiesIcon,
SimulationIcon,
} from "../../icons/SimulationIcons";
import { useToggleStore } from "../../../store/useUIToggleStore";
import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations";
import useVersionHistoryVisibleStore, {
useSaveVersion,
useSelectedFloorItem,
useToolMode,
useSaveVersion,
useSelectedFloorItem,
useToolMode,
} from "../../../store/builder/store";
import {
useSelectedEventData,
useSelectedEventSphere,
useSelectedEventData,
useSelectedEventSphere,
} from "../../../store/simulation/useSimulationStore";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import GlobalProperties from "./properties/GlobalProperties";
import AssetProperties from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties";
@@ -29,215 +30,227 @@ import EventProperties from "./properties/eventProperties/EventProperties";
import VersionHistory from "./versionHisory/VersionHistory";
import AisleProperties from "./properties/AisleProperties";
import WallProperties from "./properties/WallProperties";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import FloorProperties from "./properties/FloorProperties";
import SelectedWallProperties from "./properties/SelectedWallProperties";
import { useSceneContext } from "../../../modules/scene/sceneContext";
import SelectedFloorProperties from "./properties/SelectedFloorProperties";
const SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore();
const { toggleUIRight } = useToggleStore();
const { toolMode } = useToolMode();
const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem();
const { activeModule } = useModuleStore();
const { toggleUIRight } = useToggleStore();
const { toolMode } = useToolMode();
const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore();
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { isVersionSaved } = useSaveVersion();
const { selectedWall } = useBuilderStore();
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { isVersionSaved } = useSaveVersion();
// Reset activeList whenever activeModule changes
useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties");
if (activeModule === "simulation") setSubModule("simulations");
}, [activeModule, setSubModule]);
useEffect(() => {
if (
activeModule !== "mechanics" &&
selectedEventData &&
selectedEventSphere
) {
setSubModule("mechanics");
} else if (!selectedEventData && !selectedEventSphere) {
if (activeModule === "simulation") {
setSubModule("simulations");
}
}
if (activeModule !== "simulation") {
setSubModule("properties");
}
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
// Reset activeList whenever activeModule changes
useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties");
if (activeModule === "simulation") setSubModule("simulations");
}, [activeModule, setSubModule]);
useEffect(() => {
if (
activeModule !== "mechanics" &&
selectedEventData &&
selectedEventSphere
) {
setSubModule("mechanics");
} else if (!selectedEventData && !selectedEventSphere) {
if (activeModule === "simulation") {
setSubModule("simulations");
}
}
if (activeModule !== "simulation") {
setSubModule("properties");
}
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
return (
<div
className={`sidebar-right-wrapper ${toggleUIRight && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"
}`}
>
<Header />
{toggleUIRight && (
<>
{!isVersionSaved && (
<div className="sidebar-actions-container">
{activeModule !== "simulation" && (
<button
id="sidebar-action-list-properties"
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
}`}
onClick={() => {
setSubModule("properties");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">properties</div>
<PropertiesIcon isActive={subModule === "properties"} />
</button>
)}
{activeModule === "simulation" && (
return (
<div
className={`sidebar-right-wrapper ${toggleUIRight && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"
}`}
>
<Header />
{toggleUIRight && (
<>
<button
id="sidebar-action-list-simulation"
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
}`}
onClick={() => {
setSubModule("simulations");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">simulations</div>
<SimulationIcon isActive={subModule === "simulations"} />
</button>
<button
id="sidebar-action-list-mechanics"
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
}`}
onClick={() => {
setSubModule("mechanics");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">mechanics</div>
<MechanicsIcon isActive={subModule === "mechanics"} />
</button>
<button
id="sidebar-action-list-analysis"
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
}`}
onClick={() => {
setSubModule("analysis");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">analysis</div>
<AnalysisIcon isActive={subModule === "analysis"} />
</button>
{!isVersionSaved && (
<div className="sidebar-actions-container">
{activeModule !== "simulation" && (
<button
id="sidebar-action-list-properties"
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
}`}
onClick={() => {
setSubModule("properties");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">properties</div>
<PropertiesIcon isActive={subModule === "properties"} />
</button>
)}
{activeModule === "simulation" && (
<>
<button
id="sidebar-action-list-simulation"
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
}`}
onClick={() => {
setSubModule("simulations");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">simulations</div>
<SimulationIcon isActive={subModule === "simulations"} />
</button>
<button
id="sidebar-action-list-mechanics"
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
}`}
onClick={() => {
setSubModule("mechanics");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">mechanics</div>
<MechanicsIcon isActive={subModule === "mechanics"} />
</button>
<button
id="sidebar-action-list-analysis"
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
}`}
onClick={() => {
setSubModule("analysis");
setVersionHistoryVisible(false);
}}
>
<div className="tooltip">analysis</div>
<AnalysisIcon isActive={subModule === "analysis"} />
</button>
</>
)}
</div>
)}
{viewVersionHistory && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<VersionHistory />
</div>
</div>
)}
{/* process builder */}
{!viewVersionHistory &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem &&
!selectedFloor &&
!selectedWall && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
{(() => {
if (toolMode === "Aisle") {
return <AisleProperties />;
} else if (toolMode === "Wall") {
return <WallProperties />;
} else if (toolMode === "Floor") {
return <FloorProperties />;
} else {
return <GlobalProperties />;
}
})()}
</div>
</div>
)}
{!viewVersionHistory &&
subModule === "properties" &&
activeModule !== "visualization" &&
selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<AssetProperties />
</div>
</div>
)}
{!viewVersionHistory &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem &&
!selectedFloor &&
!selectedAisle &&
selectedWall && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<SelectedWallProperties />
</div>
</div>
)}
{!viewVersionHistory &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem &&
!selectedWall &&
!selectedAisle &&
selectedFloor && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<SelectedFloorProperties />
</div>
</div>
)}
{!viewVersionHistory &&
subModule === "zoneProperties" &&
(activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ZoneProperties />
</div>
</div>
)}
{/* simulation */}
{!isVersionSaved &&
!viewVersionHistory &&
activeModule === "simulation" && (
<>
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
{subModule === "mechanics" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<EventProperties />
</div>
</div>
)}
{subModule === "analysis" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Analysis />
</div>
</div>
)}
</>
)}
{/* realtime visualization */}
{activeModule === "visualization" && <Visualization />}
</>
)}
</div>
)}
{viewVersionHistory && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<VersionHistory />
</div>
</div>
)}
{/* process builder */}
{!viewVersionHistory &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem &&
!selectedWall && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
{(() => {
if (toolMode === "Aisle") {
return <AisleProperties />;
} else if (toolMode === "Wall") {
return <WallProperties />;
} else {
return <GlobalProperties />;
}
})()}
</div>
</div>
)}
{!viewVersionHistory &&
subModule === "properties" &&
activeModule !== "visualization" &&
selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<AssetProperties />
</div>
</div>
)}
{!viewVersionHistory &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem &&
selectedWall && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<SelectedWallProperties />
</div>
</div>
)}
{!viewVersionHistory &&
subModule === "zoneProperties" &&
(activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ZoneProperties />
</div>
</div>
)}
{/* simulation */}
{!isVersionSaved &&
!viewVersionHistory &&
activeModule === "simulation" && (
<>
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
{subModule === "mechanics" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<EventProperties />
</div>
</div>
)}
{subModule === "analysis" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Analysis />
</div>
</div>
)}
</>
)}
{/* realtime visualization */}
{activeModule === "visualization" && <Visualization />}
</>
)}
</div>
);
</div>
);
};
export default SideBarRight;

View File

@@ -0,0 +1,171 @@
import { useEffect, useState } from "react";
import InputToggle from "../../../ui/inputs/InputToggle";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import defaultTexture from '../../../../assets/textures/floor/white.png';
import flootTexture1 from '../../../../assets/textures/floor/factory wall texture.jpg';
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
type Material = {
texture: string;
textureId: string;
textureName: string;
};
const materials = [
{ texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" },
{ texture: flootTexture1, textureId: "Material 1", textureName: "Grunge Concrete Wall" }
];
const FloorProperties = () => {
const { floorDepth, isBeveled, topMaterial, sideMaterial, bevelStrength, setFloorDepth, setIsBeveled, setBevelStrength, setFloorMaterial } = useBuilderStore();
const [activeSurface, setActiveSurface] = useState<"top" | "side">("top");
const [selectedMaterials, setSelectedMaterials] = useState<{ top: Material | null; side: Material | null; }>({ top: null, side: null, });
useEffect(() => {
setSelectedMaterials({
top: materials.find((mat) => mat.textureId === topMaterial) || null,
side: materials.find((mat) => mat.textureId === sideMaterial) || null,
});
}, []);
const handleDepthChange = (val: string) => {
setFloorDepth(parseFloat(val));
};
const handleIsBevelChange = (val: boolean) => {
setIsBeveled(val);
};
const handleBevelChange = (val: string) => {
setBevelStrength(parseFloat(val));
};
const handleSelectMaterial = (material: Material) => {
setSelectedMaterials((prev) => ({
...prev,
[activeSurface]: material,
}));
setFloorMaterial(material.textureId, activeSurface);
};
return (
<div className="wall-properties-container">
<section className="wall-properties-section">
<div className="header">Floor</div>
<div className="wall-properties">
<InputWithDropDown
label="Depth"
value={`${floorDepth}`}
min={0.1}
max={10}
step={0.1}
onChange={handleDepthChange}
/>
<InputToggle
value={isBeveled}
label="Beveled"
inputKey=""
onClick={() => handleIsBevelChange(!isBeveled)}
/>
<InputWithDropDown
label="Bevel Strength"
value={`${bevelStrength}`}
min={1}
max={10}
step={1}
onChange={handleBevelChange}
/>
</div>
</section>
<section>
<div className="header-wrapper">
<div className="header">Materials</div>
</div>
<div className="material-preview">
<div className="sides-wrapper">
<button
className={`side-wrapper ${activeSurface === "top" ? "active" : ""}`}
onClick={() => setActiveSurface("top")}
>
<div className="label">Top</div>
<div className="texture-image">
{selectedMaterials.top && (
<img
draggable={false}
src={selectedMaterials.top.texture}
alt={selectedMaterials.top.textureName}
/>
)}
</div>
</button>
<button
className={`side-wrapper ${activeSurface === "side" ? "active" : ""}`}
onClick={() => setActiveSurface("side")}
>
<div className="label">Side</div>
<div className="texture-image">
{selectedMaterials.side && (
<img
draggable={false}
src={selectedMaterials.side.texture}
alt={selectedMaterials.side.textureName}
/>
)}
</div>
</button>
</div>
<div className="preview">
{selectedMaterials[activeSurface] && (
<img
draggable={false}
src={selectedMaterials[activeSurface]!.texture}
alt={selectedMaterials[activeSurface]!.textureName}
/>
)}
</div>
</div>
<div className="materials">
{materials.length === 0 ? (
<div className="no-materials">No materials added yet.</div>
) : (
<div className="material-container">
{materials.map((material, index) => {
const isSelected = selectedMaterials[activeSurface]?.texture === material.texture;
return (
<button
className={`material-wrapper ${isSelected ? "selectedMaterial" : ""}`}
key={`${material.textureName}_${index}`}
onClick={() => handleSelectMaterial(material)}
>
<div className="material-property">
<div className="material-image">
<img
draggable={false}
src={material.texture}
alt={material.textureName}
/>
</div>
<div className="material-name">{material.textureName}</div>
</div>
</button>
);
})}
</div>
)}
</div>
</section>
</div>
);
};
export default FloorProperties;

View File

@@ -0,0 +1,250 @@
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import InputToggle from "../../../ui/inputs/InputToggle";
import defaultTexture from '../../../../assets/textures/floor/white.png';
import floorTexture1 from '../../../../assets/textures/floor/factory wall texture.jpg';
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../functions/getUserData";
import { useSocketStore } from "../../../../store/builder/store";
// import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
const SelectedFloorProperties = () => {
const [depth, setDepth] = useState("");
const [isBeveled, setIsBeveled] = useState(false);
const [bevelStrength, setBevelStrength] = useState("");
const { selectedFloor } = useBuilderStore();
const { floorStore } = useSceneContext();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { socket } = useSocketStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { getFloorById, updateFloor } = floorStore();
const [activeSurface, setActiveSurface] = useState<"top" | "side">("top");
const materials = [
{ texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" },
{ texture: floorTexture1, textureId: "Material 1", textureName: "Grunge Concrete" }
];
const floor = selectedFloor ? getFloorById(selectedFloor.userData.floorUuid) : null;
useEffect(() => {
if (floor) {
setDepth(floor.floorDepth.toString());
setIsBeveled(floor.isBeveled);
setBevelStrength(floor.bevelStrength.toString());
}
}, [floor]);
const handleDepthChange = (val: string) => {
setDepth(val);
const parsed = parseFloat(val);
if (!isNaN(parsed) && floor) {
const updatedFloor = updateFloor(floor.floorUuid, { floorDepth: parsed });
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
}
};
const handleBevelChange = (val: string) => {
setBevelStrength(val);
const parsed = parseFloat(val);
if (!isNaN(parsed) && floor) {
const updatedFloor = updateFloor(floor.floorUuid, { bevelStrength: parsed });
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
}
};
const handleIsBeveledToggle = () => {
setIsBeveled(!isBeveled);
if (!floor) return;
const updatedFloor = updateFloor(floor.floorUuid, { isBeveled: !floor.isBeveled });
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
};
const handleSelectMaterial = (material: { textureId: string; textureName: string }) => {
if (!floor) return;
const key = activeSurface === "top" ? "topMaterial" : "sideMaterial";
const updatedFloor = updateFloor(floor.floorUuid, { [key]: material.textureId });
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
};
if (!floor) return null;
const selectedMaterials = {
top: materials.find((m) => m.textureId === floor.topMaterial) ?? materials[0],
side: materials.find((m) => m.textureId === floor.sideMaterial) ?? materials[0],
};
return (
<div className="wall-properties-container">
<section className="wall-properties-section">
<div className="header">Floor</div>
<div className="wall-properties">
<InputWithDropDown
label="Depth"
value={depth}
min={0.1}
max={10}
step={0.1}
onChange={handleDepthChange}
/>
<InputToggle
label="Beveled"
inputKey="isBeveled"
value={isBeveled}
onClick={handleIsBeveledToggle}
/>
<InputWithDropDown
label="Bevel Strength"
value={bevelStrength}
min={1}
max={10}
step={1}
onChange={handleBevelChange}
/>
</div>
</section>
<section>
<div className="header-wrapper">
<div className="header">Materials</div>
</div>
<div className="material-preview">
<div className="sides-wrapper">
{(["top", "side"] as const).map((surface) => (
<button
key={surface}
className={`side-wrapper ${activeSurface === surface ? "active" : ""}`}
onClick={() => setActiveSurface(surface)}
>
<div className="label">{surface === "top" ? "Top" : "Side"}</div>
<div className="texture-image">
<img
draggable={false}
src={selectedMaterials[surface].texture}
alt={selectedMaterials[surface].textureName}
/>
</div>
</button>
))}
</div>
<div className="preview">
<img
draggable={false}
src={selectedMaterials[activeSurface].texture}
alt={selectedMaterials[activeSurface].textureName}
/>
</div>
</div>
<div className="materials">
<div className="material-container">
{materials.map((material, index) => {
const isSelected = selectedMaterials[activeSurface].textureId === material.textureId;
return (
<button
className={`material-wrapper ${isSelected ? "selectedMaterial" : ""}`}
key={`${material.textureName}_${index}`}
onClick={() => handleSelectMaterial(material)}
>
<div className="material-property">
<div className="material-image">
<img
draggable={false}
src={material.texture}
alt={material.textureName}
/>
</div>
<div className="material-name">{material.textureName}</div>
</div>
</button>
);
})}
</div>
</div>
</section>
</div>
);
};
export default SelectedFloorProperties;

View File

@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import defaultTexture from '../../../../assets/textures/floor/wall-tex.png';
@@ -6,10 +6,23 @@ import wallTexture1 from '../../../../assets/textures/floor/factory wall texture
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../functions/getUserData";
import { useSocketStore } from "../../../../store/builder/store";
// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
const SelectedWallProperties = () => {
const [height, setHeight] = useState("");
const [thickness, setThickness] = useState("");
const { selectedWall } = useBuilderStore();
const { wallStore } = useSceneContext();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { socket } = useSocketStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { getWallById, updateWall } = wallStore();
const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1");
@@ -21,17 +34,62 @@ const SelectedWallProperties = () => {
const wall = selectedWall ? getWallById(selectedWall.userData.wallUuid) : null;
useEffect(() => {
if (wall) {
setHeight(wall.wallHeight.toString());
setThickness(wall.wallThickness.toString());
}
}, [wall, selectedWall]);
const handleHeightChange = (val: string) => {
setHeight(val);
const height = parseFloat(val);
if (!isNaN(height) && wall) {
updateWall(wall.wallUuid, { wallHeight: height });
const updatedWall = updateWall(wall.wallUuid, { wallHeight: height });
if (updatedWall && projectId) {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
// SOCKET
const data = {
wallData: updatedWall,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
}
};
const handleThicknessChange = (val: string) => {
setThickness(val);
const thickness = parseFloat(val);
if (!isNaN(thickness) && wall) {
updateWall(wall.wallUuid, { wallThickness: thickness });
const updatedWall = updateWall(wall.wallUuid, { wallThickness: thickness });
if (updatedWall && projectId) {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
// SOCKET
const data = {
wallData: updatedWall,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
}
};
@@ -39,23 +97,32 @@ const SelectedWallProperties = () => {
if (!wall) return;
const updated = (activeSide === "side1" ? { insideMaterial: material.textureId } : { outsideMaterial: material.textureId })
const updatedWall = updateWall(wall.wallUuid, updated);
if (updatedWall && projectId) {
updateWall(wall.wallUuid, updated);
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
// SOCKET
const data = {
wallData: updatedWall,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
};
if (!wall) return null;
const selectedMaterials = {
side1: {
texture: materials.find((material) => material.textureId === wall.insideMaterial)?.texture || 'Unknown',
textureId: materials.find((material) => material.textureId === wall.insideMaterial)?.textureId || 'Unknown',
textureName: materials.find((material) => material.textureId === wall.insideMaterial)?.textureName || 'Unknown'
},
side2: {
texture: materials.find((material) => material.textureId === wall.outsideMaterial)?.texture || 'Unknown',
textureId: materials.find((material) => material.textureId === wall.outsideMaterial)?.textureId || 'Unknown',
textureName: materials.find((material) => material.textureId === wall.outsideMaterial)?.textureName || 'Unknown'
}
side1: materials.find((m) => m.textureId === wall.insideMaterial) ?? materials[0],
side2: materials.find((m) => m.textureId === wall.outsideMaterial) ?? materials[0]
};
return (
@@ -65,12 +132,18 @@ const SelectedWallProperties = () => {
<div className="wall-properties">
<InputWithDropDown
label="Height"
value={`${wall.wallHeight}`}
value={height}
min={1}
max={25}
step={1}
onChange={handleHeightChange}
/>
<InputWithDropDown
label="Thickness"
value={`${wall.wallThickness}`}
value={thickness}
min={0.1}
max={2}
step={0.1}
onChange={handleThicknessChange}
/>
</div>

View File

@@ -10,163 +10,158 @@ import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
// Define Material type
type Material = {
texture: string;
textureName: string;
texture: string;
textureId: string;
textureName: string;
};
// Default and initial materials
const defaultMaterial: Material = {
texture: defaultTexture,
textureName: "Default Material",
};
const initialMaterial: Material = {
texture: wallTexture1,
textureName: "Grunge Concrete Wall",
};
const materials = [
{ texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" },
{ texture: wallTexture1, textureId: "Material 1", textureName: "Grunge Concrete Wall" }
];
const WallProperties = () => {
const { wallHeight, wallThickness, setWallHeight, setWallThickness } = useBuilderStore();
const { wallHeight, wallThickness, insideMaterial, outsideMaterial, setWallHeight, setWallThickness } = useBuilderStore();
const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1");
const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1");
const [materials, setMaterials] = useState<Material[]>([
defaultMaterial,
initialMaterial,
]);
const [selectedMaterials, setSelectedMaterials] = useState<{
side1: Material | null;
side2: Material | null;
}>({
side1: null,
side2: null,
});
// Set default material initially for both sides
useEffect(() => {
setSelectedMaterials({
side1: defaultMaterial,
side2: defaultMaterial,
const [selectedMaterials, setSelectedMaterials] = useState<{
side1: Material | null;
side2: Material | null;
}>({
side1: null,
side2: null,
});
}, []);
const handleHeightChange = (newValue: string) => {
setWallHeight(parseFloat(newValue));
};
useEffect(() => {
setSelectedMaterials({
side1: materials.find((mat) => mat.textureId === outsideMaterial) || null,
side2: materials.find((mat) => mat.textureId === insideMaterial) || null,
});
}, []);
const handleThicknessChange = (newValue: string) => {
setWallThickness(parseFloat(newValue));
};
const handleHeightChange = (newValue: string) => {
setWallHeight(parseFloat(newValue));
};
const handleSelectMaterial = (material: Material) => {
setSelectedMaterials((prev) => ({
...prev,
[activeSide]: material,
}));
};
const handleThicknessChange = (newValue: string) => {
setWallThickness(parseFloat(newValue));
};
return (
<div className="wall-properties-container">
<section className="wall-properties-section">
<div className="header">Wall</div>
<div className="wall-properties">
<InputWithDropDown
label="Height"
value={`${wallHeight}`}
onChange={(val) => handleHeightChange(val)}
/>
<InputWithDropDown
label="Thickness"
value={`${wallThickness}`}
onChange={(val) => handleThicknessChange(val)}
/>
</div>
</section>
<section>
<div className="header-wrapper">
<div className="header">Materials</div>
</div>
const handleSelectMaterial = (material: Material) => {
setSelectedMaterials((prev) => ({
...prev,
[activeSide]: material,
}));
};
<div className="material-preview">
<div className="sides-wrapper">
<button
className={`side-wrapper ${activeSide === "side1" ? "active" : ""}`}
onClick={() => setActiveSide("side1")}
>
<div className="label">Side 1</div>
<div className="texture-image">
{selectedMaterials.side1 && (
<img
draggable={false}
src={selectedMaterials.side1.texture}
alt={selectedMaterials.side1.textureName}
/>
)}
</div>
</button>
return (
<div className="wall-properties-container">
<section className="wall-properties-section">
<div className="header">Wall</div>
<div className="wall-properties">
<InputWithDropDown
label="Height"
value={`${wallHeight}`}
min={1}
max={25}
step={1}
onChange={(val) => handleHeightChange(val)}
/>
<InputWithDropDown
label="Thickness"
value={`${wallThickness}`}
min={0.1}
max={2}
step={0.1}
onChange={(val) => handleThicknessChange(val)}
/>
</div>
</section>
<section>
<div className="header-wrapper">
<div className="header">Materials</div>
</div>
<button
className={`side-wrapper ${activeSide === "side2" ? "active" : ""}`}
onClick={() => setActiveSide("side2")}
>
<div className="label">Side 2</div>
<div className="texture-image">
{selectedMaterials.side2 && (
<img
draggable={false}
src={selectedMaterials.side2.texture}
alt={selectedMaterials.side2.textureName}
/>
)}
</div>
</button>
</div>
<div className="material-preview">
<div className="sides-wrapper">
<button
className={`side-wrapper ${activeSide === "side1" ? "active" : ""}`}
onClick={() => setActiveSide("side1")}
>
<div className="label">Side 1</div>
<div className="texture-image">
{selectedMaterials.side1 && (
<img
draggable={false}
src={selectedMaterials.side1.texture}
alt={selectedMaterials.side1.textureName}
/>
)}
</div>
</button>
<div className="preview">
{selectedMaterials[activeSide] && (
<img
draggable={false}
src={selectedMaterials[activeSide]!.texture}
alt={selectedMaterials[activeSide]!.textureName}
/>
)}
</div>
</div>
<div className="materials">
{materials.length === 0 ? (
<div className="no-materials">No materials added yet.</div>
) : (
<div className="material-container">
{materials.map((material, index) => {
const isSelected = selectedMaterials[activeSide]?.texture === material.texture;
return (
<button
className={`material-wrapper ${isSelected ? "selectedMaterial" : ""}`}
key={`${material.textureName}_${index}`}
onClick={() => handleSelectMaterial(material)}
>
<div className="material-property">
<div className="material-image">
<img
draggable={false}
src={material.texture}
alt={material.textureName}
/>
</div>
<div className="material-name">{material.textureName}</div>
<button
className={`side-wrapper ${activeSide === "side2" ? "active" : ""}`}
onClick={() => setActiveSide("side2")}
>
<div className="label">Side 2</div>
<div className="texture-image">
{selectedMaterials.side2 && (
<img
draggable={false}
src={selectedMaterials.side2.texture}
alt={selectedMaterials.side2.textureName}
/>
)}
</div>
</button>
</div>
</button>
);
})}
</div>
)}
<div className="preview">
{selectedMaterials[activeSide] && (
<img
draggable={false}
src={selectedMaterials[activeSide]!.texture}
alt={selectedMaterials[activeSide]!.textureName}
/>
)}
</div>
</div>
<div className="materials">
{materials.length === 0 ? (
<div className="no-materials">No materials added yet.</div>
) : (
<div className="material-container">
{materials.map((material, index) => {
const isSelected = selectedMaterials[activeSide]?.texture === material.texture;
return (
<button
className={`material-wrapper ${isSelected ? "selectedMaterial" : ""}`}
key={`${material.textureName}_${index}`}
onClick={() => handleSelectMaterial(material)}
>
<div className="material-property">
<div className="material-image">
<img
draggable={false}
src={material.texture}
alt={material.textureName}
/>
</div>
<div className="material-name">{material.textureName}</div>
</div>
</button>
);
})}
</div>
)}
</div>
</section>
</div>
</section>
</div>
);
);
};
export default WallProperties;

View File

@@ -17,7 +17,7 @@ import {
useZones,
} from "../../../store/builder/store";
import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
import { setAssetsApi } from "../../../services/factoryBuilder/assest/floorAsset/setAssetsApi";
import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useSceneContext } from "../../../modules/scene/sceneContext";

View File

@@ -7,8 +7,8 @@ import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import defaultMaterial from '../../../assets/textures/floor/wall-tex.png';
import useModuleStore from '../../../store/useModuleStore';
function DecalInstance({ visible = true, decal }: { visible?: boolean, decal: Decal }) {
const { setSelectedWall, selectedDecal, setSelectedDecal } = useBuilderStore();
function DecalInstance({ visible = true, decal, zPosition = decal.decalPosition[2] }: { visible?: boolean, decal: Decal, zPosition?: number }) {
const { setSelectedWall, setSelectedFloor, selectedDecal, setSelectedDecal } = useBuilderStore();
const { togglView } = useToggleView();
const { activeModule } = useModuleStore();
const material = useLoader(THREE.TextureLoader, defaultMaterial);
@@ -17,15 +17,17 @@ function DecalInstance({ visible = true, decal }: { visible?: boolean, decal: De
<Decal
// debug
visible={visible}
position={decal.decalPosition}
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
rotation={[0, 0, decal.decalRotation]}
scale={[decal.decalScale, decal.decalScale, 0.01]}
userData={decal}
onClick={(e) => {
if (visible && !togglView && activeModule === 'builder') {
if (e.object.userData.decalUuid) {
e.stopPropagation();
setSelectedDecal(e.object);
setSelectedWall(null);
setSelectedFloor(null);
}
}
}}

View File

@@ -1,30 +0,0 @@
import addLineToScene from '../../builder/geomentries/lines/addLineToScene';
import * as CONSTANTS from '../../../types/world/worldConstants';
import * as Types from "../../../types/world/worldTypes";
function loadInitialLine(
floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines
): void {
if (!floorPlanGroupLine.current) return
////////// Load the Lines initially if there are any //////////
floorPlanGroupLine.current.children = [];
lines.current.forEach((line) => {
let colour;
if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName) {
colour = CONSTANTS.lineConfig.wallColor;
} else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName) {
colour = CONSTANTS.lineConfig.floorColor;
} else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName) {
colour = CONSTANTS.lineConfig.aisleColor;
}
if (colour) {
addLineToScene(line[0][0], line[1][0], colour, line, floorPlanGroupLine);
}
});
}
export default loadInitialLine;

View File

@@ -1,87 +0,0 @@
import * as THREE from 'three';
import * as CONSTANTS from '../../../types/world/worldConstants';
import * as Types from "../../../types/world/worldTypes";
////////// Load the Boxes initially if there are any //////////
function loadInitialPoint(
lines: Types.RefLines,
floorPlanGroupPoint: Types.RefGroup,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl
): void {
if (!floorPlanGroupPoint.current) return
floorPlanGroupPoint.current.children = [];
currentLayerPoint.current = [];
lines.current.forEach((line) => {
const colour = getPointColor(line[0][3]);
line.forEach((pointData) => {
const [point, id] = pointData;
/////////// Check if a box with this id already exists //////////
const existingBox = floorPlanGroupPoint.current?.getObjectByProperty('uuid', id);
if (existingBox) {
return;
}
const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale);
const material = new THREE.ShaderMaterial({
uniforms: {
uOuterColor: { value: new THREE.Color(colour) }, // Blue color for the border
uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec2 vUv;
uniform vec3 uOuterColor;
uniform vec3 uInnerColor;
void main() {
// Define the size of the white square as a proportion of the face
float borderThickness = 0.2; // Adjust this value for border thickness
if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness &&
vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) {
gl_FragColor = vec4(uInnerColor, 1.0); // White inner square
} else {
gl_FragColor = vec4(uOuterColor, 1.0); // Blue border
}
}
`,
});
const box = new THREE.Mesh(geometry, material);
box.name = "point";
box.uuid = id;
box.userData = { type: line[0][3], color: colour };
box.position.set(point.x, point.y, point.z);
currentLayerPoint.current.push(box);
floorPlanGroupPoint.current?.add(box);
});
});
function getPointColor(lineType: string | undefined): string {
switch (lineType) {
case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor;
case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor;
case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor;
default: return CONSTANTS.pointConfig.defaultOuterColor;
}
}
if (dragPointControls.current) {
dragPointControls.current!.objects = currentLayerPoint.current;
}
}
export default loadInitialPoint;

View File

@@ -1,110 +1,110 @@
import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import { getWallItems } from "../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi";
import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils";
import { getUserData } from "../../../functions/getUserData";
// import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
// import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
// import * as THREE from "three";
// import * as Types from "../../../types/world/worldTypes";
// import { getWallItems } from "../../../services/factoryBuilder/asset/wallAsset/getWallItemsApi";
// import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils";
// import { getUserData } from "../../../functions/getUserData";
async function loadInitialWallItems(
setWallItems: Types.setWallItemSetState,
projectId?: string,
versionId?: string
): Promise<void> {
if (!projectId || !versionId) return;
try {
const { organization, email } = getUserData();
// async function loadInitialWallItems(
// setWallItems: Types.setWallItemSetState,
// projectId?: string,
// versionId?: string
// ): Promise<void> {
// if (!projectId || !versionId) return;
// try {
// const { organization, email } = getUserData();
if (!email) {
console.error("No email found in localStorage");
}
// if (!email) {
// console.error("No email found in localStorage");
// }
const items = await getWallItems(organization, projectId, versionId);
// const items = await getWallItems(organization, projectId, versionId);
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
if (!items || items.length === 0) {
localStorage.removeItem("WallItems");
return;
}
// if (!items || items.length === 0) {
// localStorage.removeItem("WallItems");
// return;
// }
localStorage.setItem("WallItems", JSON.stringify(items));
// localStorage.setItem("WallItems", JSON.stringify(items));
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 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 loadedWallItems = await Promise.all(
items.map(async (item: Types.WallItem) => {
// Check THREE.js cache first
const cachedModel = THREE.Cache.get(item.assetId!);
if (cachedModel) {
return processModel(cachedModel, item);
}
// const loadedWallItems = await Promise.all(
// items.map(async (item: Types.WallItem) => {
// // Check THREE.js cache first
// const cachedModel = THREE.Cache.get(item.assetId!);
// if (cachedModel) {
// return processModel(cachedModel, item);
// }
// Check IndexedDB cache
const cachedModelBlob = await retrieveGLTF(item.assetId!);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
return new Promise<Types.WallItem>((resolve) => {
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.add(item.assetId!, gltf);
resolve(processModel(gltf, item));
});
});
}
// // Check IndexedDB cache
// const cachedModelBlob = await retrieveGLTF(item.assetId!);
// if (cachedModelBlob) {
// const blobUrl = URL.createObjectURL(cachedModelBlob);
// return new Promise<Types.WallItem>((resolve) => {
// loader.load(blobUrl, (gltf) => {
// URL.revokeObjectURL(blobUrl);
// THREE.Cache.add(item.assetId!, gltf);
// resolve(processModel(gltf, item));
// });
// });
// }
// Load from original URL if not cached
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.assetId!}`;
return new Promise<Types.WallItem>((resolve) => {
loader.load(modelUrl, async (gltf) => {
try {
// Cache the model
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
await storeGLTF(item.assetId!, modelBlob);
THREE.Cache.add(item.assetId!, gltf);
resolve(processModel(gltf, item));
} catch (error) {
console.error("Failed to cache model:", error);
resolve(processModel(gltf, item));
}
});
});
})
);
// // Load from original URL if not cached
// const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.assetId!}`;
// return new Promise<Types.WallItem>((resolve) => {
// loader.load(modelUrl, async (gltf) => {
// try {
// // Cache the model
// const modelBlob = await fetch(modelUrl).then((res) => res.blob());
// await storeGLTF(item.assetId!, modelBlob);
// THREE.Cache.add(item.assetId!, gltf);
// resolve(processModel(gltf, item));
// } catch (error) {
// console.error("Failed to cache model:", error);
// resolve(processModel(gltf, item));
// }
// });
// });
// })
// );
setWallItems(loadedWallItems);
} catch (error) {
console.error("Failed to load wall items:", error);
}
}
// setWallItems(loadedWallItems);
// } catch (error) {
// console.error("Failed to load wall items:", error);
// }
// }
function processModel(gltf: GLTF, item: Types.WallItem): Types.WallItem {
const model = gltf.scene.clone();
model.uuid = item.modelUuid!;
// function processModel(gltf: GLTF, item: Types.WallItem): Types.WallItem {
// const model = gltf.scene.clone();
// model.uuid = item.modelUuid!;
model.children[0]?.children?.forEach((child: THREE.Object3D) => {
if (child.name !== "CSG_REF") {
child.castShadow = true;
child.receiveShadow = true;
}
});
// model.children[0]?.children?.forEach((child: THREE.Object3D) => {
// if (child.name !== "CSG_REF") {
// child.castShadow = true;
// child.receiveShadow = true;
// }
// });
return {
type: item.type,
model: model,
modelName: item.modelName,
assetId: item.assetId,
scale: item.scale,
csgscale: item.csgscale,
csgposition: item.csgposition,
position: item.position,
quaternion: item.quaternion,
};
}
// return {
// type: item.type,
// model: model,
// modelName: item.modelName,
// assetId: item.assetId,
// scale: item.scale,
// csgscale: item.csgscale,
// csgposition: item.csgposition,
// position: item.position,
// quaternion: item.quaternion,
// };
// }
export default loadInitialWallItems;
// export default loadInitialWallItems;

View File

@@ -78,7 +78,7 @@ function ArcAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -65,7 +65,7 @@ function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -68,7 +68,7 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -53,7 +53,7 @@ function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -66,7 +66,7 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -53,7 +53,7 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -100,7 +100,7 @@ function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -50,7 +50,7 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onDoubleClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}

View File

@@ -2,13 +2,13 @@ import * as THREE from 'three'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import ReferenceAisle from './referenceAisle';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import ReferencePoint from '../../point/reference/referencePoint';
import { createAisleApi } from '../../../../services/factoryBuilder/aisle/createAisleApi';
import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../version/versionContext';
import { useSceneContext } from '../../../scene/sceneContext';
import ReferenceAisle from './referenceAisle';
import ReferencePoint from '../../point/reference/referencePoint';
function AisleCreator() {
const { scene, camera, raycaster, gl, pointer } = useThree();
@@ -74,6 +74,8 @@ function AisleCreator() {
newPoint.layer = snappedPoint.layer;
}
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) { return }
if (snappedPosition && !snappedPoint) {
newPoint.position = snappedPosition;
}
@@ -104,7 +106,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -127,7 +129,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -149,7 +151,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -170,7 +172,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -193,7 +195,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -215,7 +217,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -236,7 +238,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -258,7 +260,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}

View File

@@ -177,7 +177,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
useEffect(() => {
setTempAisle(null);
}, [toolMode, toggleView, tempPoints.length, aisleType, aisleWidth, aisleColor]);
}, [toolMode, toggleView, tempPoints.length, aisleType, aisleWidth, aisleColor, activeLayer]);
if (!tempAisle) return null;

View File

@@ -1,6 +1,6 @@
import * as THREE from "three"
import * as THREE from "three"
import { useEffect } from 'react'
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
@@ -23,10 +23,10 @@ const gltfLoaderWorker = new Worker(
)
);
function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, readonly plane: RefMesh }) {
function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
const { controls, gl, pointer, camera, raycaster } = useThree();
const { controls, gl, pointer, camera, raycaster, scene } = useThree();
const { setLoadingProgress } = useLoadingProgress();
const { assetStore, eventStore } = useSceneContext();
const { selectedVersionStore } = useVersionContext();
@@ -269,7 +269,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
useEffect(() => {
const canvasElement = gl.domElement;
const onDrop = (event: any) => {
const onDrop = (event: DragEvent) => {
if (!event.dataTransfer?.files[0]) return;
if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') {
@@ -277,7 +277,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, selectedVersion, projectId, userId);
addAssetModel(scene, raycaster, camera, pointer, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, selectedVersion, projectId, userId);
}
};

View File

@@ -3,496 +3,445 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import * as Types from "../../../../types/world/worldTypes";
import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils";
// import { setAssetsApi } from '../../../../services/factoryBuilder/assest/floorAsset/setAssetsApi';
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
import { Socket } from "socket.io-client";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import PointsCalculator from "../../../simulation/events/points/functions/pointsCalculator";
import { getUserData } from "../../../../functions/getUserData";
async function addAssetModel(
raycaster: THREE.Raycaster,
camera: THREE.Camera,
pointer: THREE.Vector2,
floorGroup: Types.RefGroup,
socket: Socket<any>,
selectedItem: any,
setSelectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
plane: Types.RefMesh,
selectedVersion?: Version | null,
projectId?: string,
userId?: string
scene: THREE.Scene,
raycaster: THREE.Raycaster,
camera: THREE.Camera,
pointer: THREE.Vector2,
socket: Socket<any>,
selectedItem: any,
setSelectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
plane: Types.RefMesh,
selectedVersion?: Version | null,
projectId?: string,
userId?: string
): Promise<void> {
////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage //////////
////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage //////////
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
try {
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
try {
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
loader.setDRACOLoader(dracoLoader);
dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
raycaster.setFromCamera(pointer, camera);
const floorIntersections = raycaster.intersectObjects(
floorGroup.current.children,
true
);
const intersectedFloor = floorIntersections.find((intersect) =>
intersect.object.name.includes("Floor")
);
raycaster.setFromCamera(pointer, camera);
const wallFloorsGroup = scene.getObjectByName("Walls-Floors-Group") as Types.Group | null;
const floorsGroup = scene.getObjectByName("Floors-Group") as Types.Group | null;
const floorChildren = floorsGroup?.children ?? [];
const wallFloorChildren = wallFloorsGroup?.children ?? [];
const floorIntersections = raycaster.intersectObjects([...floorChildren, ...wallFloorChildren], true);
const intersectedFloor = floorIntersections.find((intersect) => intersect.object.name.includes("Floor"));
const planeIntersections = raycaster.intersectObject(plane.current!, true);
const intersectedPlane = planeIntersections[0];
const planeIntersections = raycaster.intersectObject(plane.current!, true);
const intersectedPlane = planeIntersections[0];
let intersectPoint: THREE.Vector3 | null = null;
let intersectPoint: THREE.Vector3 | null = null;
if (intersectedFloor && intersectedPlane) {
intersectPoint =
intersectedFloor.distance < intersectedPlane.distance
? new THREE.Vector3(
intersectedFloor.point.x,
Math.round(intersectedFloor.point.y),
intersectedFloor.point.z
)
: new THREE.Vector3(
intersectedPlane.point.x,
0,
intersectedPlane.point.z
);
} else if (intersectedFloor) {
intersectPoint = new THREE.Vector3(
intersectedFloor.point.x,
Math.round(intersectedFloor.point.y),
intersectedFloor.point.z
);
} else if (intersectedPlane) {
intersectPoint = new THREE.Vector3(
intersectedPlane.point.x,
0,
intersectedPlane.point.z
);
}
if (intersectPoint) {
if (intersectPoint.y < 0) {
intersectPoint = new THREE.Vector3(
intersectPoint.x,
0,
intersectPoint.z
);
}
const cachedModel = THREE.Cache.get(selectedItem.id);
if (cachedModel) {
handleModelLoad(
cachedModel,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
return;
} else {
const cachedModelBlob = await retrieveGLTF(selectedItem.id);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(selectedItem.id, gltf);
handleModelLoad(
gltf,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
});
} else {
loader.load(
`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`,
async (gltf) => {
const modelBlob = await fetch(
`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`
).then((res) => res.blob());
await storeGLTF(selectedItem.id, modelBlob);
THREE.Cache.add(selectedItem.id, gltf);
await handleModelLoad(
gltf,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
if (intersectedFloor && intersectedPlane) {
// intersectPoint = intersectedFloor.distance < intersectedPlane.distance ?
// new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z)
// : new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
if (intersectedFloor.distance < intersectedPlane.distance) {
if (intersectedFloor.object.userData.floorUuid) {
intersectPoint = new THREE.Vector3(intersectedFloor.point.x, intersectedFloor.object.userData.floorDepth, intersectedFloor.point.z);
} else {
intersectPoint = new THREE.Vector3(intersectedFloor.point.x, 0, intersectedFloor.point.z);
}
} else {
intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
}
);
} else if (intersectedFloor) {
intersectPoint = new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z);
} else if (intersectedPlane) {
intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
}
}
if (intersectPoint) {
if (intersectPoint.y < 0) {
intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z);
}
const cachedModel = THREE.Cache.get(selectedItem.id);
if (cachedModel) {
handleModelLoad(cachedModel, intersectPoint!, selectedItem, addEvent, addAsset, socket, selectedVersion?.versionId || '', projectId, userId);
return;
} else {
const cachedModelBlob = await retrieveGLTF(selectedItem.id);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(selectedItem.id, gltf);
handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket, selectedVersion?.versionId || '', projectId, userId);
});
} else {
loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`,
async (gltf) => {
const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`).then((res) => res.blob());
await storeGLTF(selectedItem.id, modelBlob);
THREE.Cache.add(selectedItem.id, gltf);
await handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket, selectedVersion?.versionId || '', projectId, userId);
}
);
}
}
}
} catch (error) {
echo.error("Failed to add asset");
} finally {
setSelectedItem({});
}
} catch (error) {
echo.error("Failed to add asset");
} finally {
setSelectedItem({});
}
}
async function handleModelLoad(
gltf: any,
intersectPoint: THREE.Vector3,
selectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
socket: Socket<any>,
versionId: string,
projectId?: string,
userId?: string
gltf: any,
intersectPoint: THREE.Vector3,
selectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
socket: Socket<any>,
versionId: string,
projectId?: string,
userId?: string
) {
const { organization } = getUserData();
const model = gltf.scene.clone();
model.userData = {
name: selectedItem.name,
modelId: selectedItem.id,
modelUuid: model.uuid,
};
model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z);
model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap);
model.traverse((child: any) => {
if (child) {
child.castShadow = true;
child.receiveShadow = true;
}
});
const newFloorItem: Asset = {
modelUuid: model.uuid,
modelName: selectedItem.name,
assetId: selectedItem.id,
position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z],
rotation: [0, 0, 0],
isLocked: false,
isVisible: true,
isCollidable: false,
opacity: 1,
};
// API
// await setAssetsApi(
// organization,
// newFloorItem.modelUuid,
// newFloorItem.modelName,
// newFloorItem.assetId,
// newFloorItem.position,
// { x: 0, y: 0, z: 0 },
// false,
// true,
// );
// SOCKET
if (selectedItem.type) {
const data = PointsCalculator(
selectedItem.type,
gltf.scene.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const eventData: any = {
type: selectedItem.type,
const { organization } = getUserData();
const model = gltf.scene.clone();
model.userData = {
name: selectedItem.name,
modelId: selectedItem.id,
modelUuid: model.uuid,
};
model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z);
model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap);
if (selectedItem.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "transfer",
speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => {
const triggers: TriggerSchema[] = [];
if (data.points && index < data.points.length - 1) {
triggers.push({
triggerUuid: THREE.MathUtils.generateUUID(),
triggerName: `Trigger 1`,
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: newFloorItem.modelName,
modelUuid: newFloorItem.modelUuid,
},
triggeredPoint: {
pointName: `Point`,
pointUuid: "",
},
triggeredAction: {
actionName: `Action 1`,
actionUuid: "",
},
},
});
}
return {
uuid: THREE.MathUtils.generateUUID(),
position: [point.x, point.y, point.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action 1`,
actionType: "default",
material: "Default Material",
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: triggers,
},
};
}),
};
for (let i = 0; i < ConveyorEvent.points.length - 1; i++) {
const currentPoint = ConveyorEvent.points[i];
const nextPoint = ConveyorEvent.points[i + 1];
if (currentPoint.action.triggers.length > 0) {
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid =
nextPoint.uuid;
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid =
nextPoint.action.actionUuid;
model.traverse((child: any) => {
if (child) {
child.castShadow = true;
child.receiveShadow = true;
}
}
addEvent(ConveyorEvent);
eventData.points = ConveyorEvent.points.map((point) => ({
uuid: point.uuid,
position: point.position,
rotation: point.rotation,
}));
} else if (selectedItem.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "vehicle",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 1,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: [],
},
},
};
addEvent(vehicleEvent);
eventData.point = {
uuid: vehicleEvent.point.uuid,
position: vehicleEvent.point.position,
rotation: vehicleEvent.point.rotation,
};
} else if (selectedItem.type === "ArmBot") {
const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "roboticArm",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndPlace",
process: {
startPoint: null,
endPoint: null,
},
triggers: [],
});
const newFloorItem: Asset = {
modelUuid: model.uuid,
modelName: selectedItem.name,
assetId: selectedItem.id,
position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z],
rotation: [0, 0, 0],
isLocked: false,
isVisible: true,
isCollidable: false,
opacity: 1,
};
// API
// await setAssetsApi(
// organization,
// newFloorItem.modelUuid,
// newFloorItem.modelName,
// newFloorItem.assetId,
// newFloorItem.position,
// { x: 0, y: 0, z: 0 },
// false,
// true,
// );
// SOCKET
if (selectedItem.type) {
const data = PointsCalculator(
selectedItem.type,
gltf.scene.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const eventData: any = { type: selectedItem.type, };
if (selectedItem.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "transfer",
speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => {
const triggers: TriggerSchema[] = [];
if (data.points && index < data.points.length - 1) {
triggers.push({
triggerUuid: THREE.MathUtils.generateUUID(),
triggerName: `Trigger 1`,
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: newFloorItem.modelName,
modelUuid: newFloorItem.modelUuid,
},
triggeredPoint: {
pointName: `Point`,
pointUuid: "",
},
triggeredAction: {
actionName: `Action 1`,
actionUuid: "",
},
},
});
}
return {
uuid: THREE.MathUtils.generateUUID(),
position: [point.x, point.y, point.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action 1`,
actionType: "default",
material: "Default Material",
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: triggers,
},
};
}),
};
for (let i = 0; i < ConveyorEvent.points.length - 1; i++) {
const currentPoint = ConveyorEvent.points[i];
const nextPoint = ConveyorEvent.points[i + 1];
if (currentPoint.action.triggers.length > 0) {
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid;
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid;
}
}
addEvent(ConveyorEvent);
eventData.points = ConveyorEvent.points.map((point) => ({
uuid: point.uuid,
position: point.position,
rotation: point.rotation,
}));
} else if (selectedItem.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "vehicle",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 1,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: [],
},
},
};
addEvent(vehicleEvent);
eventData.point = {
uuid: vehicleEvent.point.uuid,
position: vehicleEvent.point.position,
rotation: vehicleEvent.point.rotation,
};
} else if (selectedItem.type === "ArmBot") {
const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "roboticArm",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndPlace",
process: {
startPoint: null,
endPoint: null,
},
triggers: [],
},
],
},
};
addEvent(roboticArmEvent);
eventData.point = {
uuid: roboticArmEvent.point.uuid,
position: roboticArmEvent.point.position,
rotation: roboticArmEvent.point.rotation,
};
} else if (selectedItem.type === "StaticMachine") {
const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "machine",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "process",
processTime: 10,
swapMaterial: "Default Material",
triggers: [],
},
},
};
addEvent(machineEvent);
eventData.point = {
uuid: machineEvent.point.uuid,
position: machineEvent.point.position,
rotation: machineEvent.point.rotation,
};
} else if (selectedItem.type === "Storage") {
const storageEvent: StorageEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "storageUnit",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "store",
storageCapacity: 10,
triggers: [],
},
},
};
addEvent(storageEvent);
eventData.point = {
uuid: storageEvent.point.uuid,
position: storageEvent.point.position,
rotation: storageEvent.point.rotation,
};
}
const completeData = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
],
},
};
addEvent(roboticArmEvent);
eventData.point = {
uuid: roboticArmEvent.point.uuid,
position: roboticArmEvent.point.position,
rotation: roboticArmEvent.point.rotation,
};
} else if (selectedItem.type === "StaticMachine") {
const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "machine",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "process",
processTime: 10,
swapMaterial: "Default Material",
triggers: [],
},
},
};
addEvent(machineEvent);
eventData.point = {
uuid: machineEvent.point.uuid,
position: machineEvent.point.position,
rotation: machineEvent.point.rotation,
};
} else if (selectedItem.type === "Storage") {
const storageEvent: StorageEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "storageUnit",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "store",
storageCapacity: 10,
triggers: [],
},
},
};
addEvent(storageEvent);
eventData.point = {
uuid: storageEvent.point.uuid,
position: storageEvent.point.position,
rotation: storageEvent.point.rotation,
};
isLocked: false,
isVisible: true,
socketId: socket.id,
eventData: eventData,
versionId: versionId,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", completeData);
const asset: Asset = {
modelUuid: completeData.modelUuid,
modelName: completeData.modelName,
assetId: completeData.assetId,
position: completeData.position,
rotation: [
completeData.rotation.x,
completeData.rotation.y,
completeData.rotation.z,
] as [number, number, number],
isLocked: completeData.isLocked,
isCollidable: false,
isVisible: completeData.isVisible,
opacity: 1,
eventData: completeData.eventData,
};
addAsset(asset);
} else {
const data = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false,
isVisible: true,
socketId: socket.id,
versionId: versionId,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", data);
const asset = {
modelUuid: data.modelUuid,
modelName: data.modelName,
assetId: data.assetId,
position: data.position,
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [
number,
number,
number
],
isLocked: data.isLocked,
isCollidable: false,
isVisible: data.isVisible,
opacity: 1,
};
addAsset(asset);
}
const completeData = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false,
isVisible: true,
socketId: socket.id,
eventData: eventData,
versionId: versionId,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", completeData);
const asset: Asset = {
modelUuid: completeData.modelUuid,
modelName: completeData.modelName,
assetId: completeData.assetId,
position: completeData.position,
rotation: [
completeData.rotation.x,
completeData.rotation.y,
completeData.rotation.z,
] as [number, number, number],
isLocked: completeData.isLocked,
isCollidable: false,
isVisible: completeData.isVisible,
opacity: 1,
eventData: completeData.eventData,
};
addAsset(asset);
} else {
const data = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false,
isVisible: true,
socketId: socket.id,
versionId: versionId,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", data);
const asset = {
modelUuid: data.modelUuid,
modelName: data.modelName,
assetId: data.assetId,
position: data.position,
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [
number,
number,
number
],
isLocked: data.isLocked,
isCollidable: false,
isVisible: data.isVisible,
opacity: 1,
};
addAsset(asset);
}
}
export default addAssetModel;

View File

@@ -4,7 +4,7 @@ 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 { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
import { useActiveTool, useDeletableFloorItem, 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 { CameraControls, Html } from '@react-three/drei';
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
@@ -34,6 +34,7 @@ function Model({ asset }: { readonly asset: Asset }) {
const { socket } = useSocketStore();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { setSelectedFloorItem } = useSelectedFloorItem();
const { limitDistance } = useLimitDistance();
const { renderDistance } = useRenderDistance();
const [isRendered, setIsRendered] = useState(false);
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
@@ -162,10 +163,16 @@ function Model({ asset }: { readonly asset: Asset }) {
useFrame(() => {
const assetPosition = new THREE.Vector3(...asset.position);
if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) {
setIsRendered(true);
} else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) {
setIsRendered(false);
if (limitDistance) {
if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) {
setIsRendered(true);
} else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) {
setIsRendered(false);
}
} else {
if (!isRendered) {
setIsRendered(true);
}
}
})

View File

@@ -1,139 +1,68 @@
////////// Three and React Three Fiber Imports //////////
import * as THREE from "three";
import { useEffect, useRef, useState } from "react";
import { useThree, useFrame } from "@react-three/fiber";
////////// Component Imports //////////
import DistanceText from "./geomentries/lines/distanceText/distanceText";
import ReferenceDistanceText from "./geomentries/lines/distanceText/referenceDistanceText";
import { useEffect, useRef } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { Geometry } from "@react-three/csg";
////////// Zustand State Imports //////////
import {
useToggleView,
useActiveLayer,
useWallVisibility,
useRoofVisibility,
useShadows,
useUpdateScene,
useWalls,
useToolMode,
useRefTextUpdate,
useRenderDistance,
useLimitDistance,
} from "../../store/builder/store";
////////// 3D Function Imports //////////
import loadWalls from "./geomentries/walls/loadWalls";
import * as Types from "../../types/world/worldTypes";
import SocketResponses from "../collaboration/socket/socketResponses.dev";
import FloorPlanGroup from "./groups/floorPlanGroup";
import FloorGroup from "./groups/floorGroup";
import Draw from "./functions/draw";
import WallsAndWallItems from "./groups/wallsAndWallItems";
import Ground from "../scene/environment/ground";
import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment";
import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
import ZoneGroup from "./groups/zoneGroup";
import MeasurementTool from "../scene/tools/measurementTool";
import NavMesh from "../simulation/vehicle/navMesh/navMesh";
import CalculateAreaGroup from "./groups/calculateAreaGroup";
import LayoutImage from "./layout/layoutImage";
import AssetsGroup from "./asset/assetsGroup";
import { Bvh } from "@react-three/drei";
import DxfFile from "./dfx/LoadBlueprint";
import { useParams } from "react-router-dom";
import AislesGroup from "./aisle/aislesGroup";
import WallGroup from "./wall/wallGroup";
import FloorGroup from "./floor/floorGroup";
import ZoneGroup from "./zone/zoneGroup";
import { useParams } from "react-router-dom";
import { useBuilderStore } from "../../store/builder/useBuilderStore";
import { getUserData } from "../../functions/getUserData";
import WallAssetGroup from "./wallAsset/wallAssetGroup";
export default function Builder() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
const csg = useRef(); // Reference for CSG object, used for 3D modeling.
const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects.
const scene = useRef() as Types.RefScene; // Reference to the scene.
const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control.
const state = useThree<Types.ThreeState>();
const plane = useRef<THREE.Mesh>(null);
const csgRef = useRef<any>(null);
// 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 snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line.
const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end).
const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc...
const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active.
const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point.
const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start).
const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation.
const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn.
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn.
const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn.
const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw.
const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not.
const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed.
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group.
const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn.
const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created.
const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls.
const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted.
const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted.
const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted.
const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted.
const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc...
const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position.
const [selectedItemsIndex, setSelectedItemsIndex] = useState<Types.Number | null>(null); // State for tracking the index of the selected item.
const { activeLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx.
const { toggleView } = useToggleView(); // State for toggling between 2D and 3D.
const { toolMode, setToolMode } = useToolMode();
const { toggleView } = useToggleView();
const { setToolMode } = useToolMode();
const { setRoofVisibility } = useRoofVisibility();
const { setWallVisibility } = useWallVisibility();
const { setShadows } = useShadows();
const { setRenderDistance } = useRenderDistance();
const { setLimitDistance } = useLimitDistance();
const { setUpdateScene } = useUpdateScene();
const { setWalls } = useWalls();
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
const { projectId } = useParams();
const { setHoveredPoint, setHoveredLine } = useBuilderStore();
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(() => {
setRefTextUpdate((prevUpdate: number) => prevUpdate - 1);
if (toggleView) {
Layer2DVisibility(
activeLayer,
floorPlanGroup,
floorPlanGroupLine,
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls
);
} else {
if (!toggleView) {
setHoveredLine(null);
setHoveredPoint(null);
state.gl.domElement.style.cursor = 'default';
setToolMode('cursor');
loadWalls(lines, setWalls);
setUpdateScene(true);
line.current = [];
}
}, [toggleView]);
@@ -152,131 +81,45 @@ export default function Builder() {
fetchVisibility();
}, []);
////////// UseFrame is Here //////////
useFrame(() => {
if (toolMode) {
Draw(
state,
plane,
cursorPosition,
floorPlanGroupPoint,
snappedPoint,
isSnapped,
isSnappedUUID,
line,
ispreSnapped,
floorPlanGroup,
ReferenceLineMesh,
LineCreated,
setRefTextUpdate,
Tube,
anglesnappedPoint,
isAngleSnapped,
toolMode
);
if (csgRef.current) {
csgRef.current.update();
}
});
////////// Return //////////
})
return (
<>
<Ground grid={grid} plane={plane} />
<Ground plane={plane} />
<Bvh firstHitOnly>
<DistanceText key={toggleView} />
</Bvh>
<SocketResponses />
<ReferenceDistanceText
key={refTextupdate}
line={ReferenceLineMesh.current}
/>
<AssetsGroup plane={plane} />
<Bvh firstHitOnly>
<SocketResponses
floorPlanGroup={floorPlanGroup}
lines={lines}
floorGroup={floorGroup}
scene={scene}
onlyFloorlines={onlyFloorlines}
currentLayerPoint={currentLayerPoint}
floorPlanGroupPoint={floorPlanGroupPoint}
floorPlanGroupLine={floorPlanGroupLine}
dragPointControls={dragPointControls}
/>
</Bvh>
<mesh name='Walls-And-WallAssets-Group'>
<Geometry ref={csgRef} useGroups>
<WallsAndWallItems
CSGGroup={CSGGroup}
setSelectedItemsIndex={setSelectedItemsIndex}
selectedItemsIndex={selectedItemsIndex}
currentWallItem={currentWallItem}
csg={csg}
lines={lines}
hoveredDeletableWallItem={hoveredDeletableWallItem}
/>
<WallGroup />
<Bvh firstHitOnly>
<WallAssetGroup />
<FloorGroup
floorGroup={floorGroup}
lines={lines}
referencePole={referencePole}
hoveredDeletablePillar={hoveredDeletablePillar}
/>
</Geometry>
</mesh>
<FloorPlanGroup
floorPlanGroup={floorPlanGroup}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
floorGroup={floorGroup}
currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls}
hoveredDeletablePoint={hoveredDeletablePoint}
hoveredDeletableLine={hoveredDeletableLine}
plane={plane}
line={line}
lines={lines}
onlyFloorline={onlyFloorline}
onlyFloorlines={onlyFloorlines}
ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated}
isSnapped={isSnapped}
ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint}
/>
<AislesGroup />
<ZoneGroup />
<FloorGroup />
<AssetsGroup
floorGroup={floorGroup}
plane={plane}
/>
<ZoneGroup />
<AislesGroup />
<MeasurementTool />
<MeasurementTool />
<CalculateAreaGroup />
<CalculateAreaGroup />
<NavMesh />
<NavMesh lines={lines} />
<DxfFile />
<DxfFile
lines={lines}
dragPointControls={dragPointControls}
currentLayerPoint={currentLayerPoint}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
/>
<LayoutImage />
</Bvh>
{/* <WallGroup /> */}
<LayoutImage />
</>
);
}

View File

@@ -1,40 +1,21 @@
import { useEffect, useRef } from 'react';
import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
import { LineBasicMaterial, Line } from 'three';
import loadInitialPoint from '../IntialLoad/loadInitialPoint';
import loadInitialLine from '../IntialLoad/loadInitialLine';
import { TransformControls } from '@react-three/drei';
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
import * as Types from '../../../types/world/worldTypes';
import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
import { useVersionContext } from '../version/versionContext';
// Interface defining the props for the DxfFile component
interface DxfFileProps {
lines: Types.RefLines; // Reference to lines in the DXF file
floorPlanGroupPoint: Types.RefGroup; // Reference to floor plan points group
dragPointControls: Types.RefDragControl; // Reference to drag controls
floorPlanGroupLine: Types.RefGroup; // Reference to floor plan lines group
currentLayerPoint: Types.RefMeshArray; // Reference to current layer points
}
/**
* DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene.
* It processes the DXF data to create points and lines representing walls and allows
* transformation controls for interactive editing.
*/
const DxfFile = ({
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls,
floorPlanGroupLine,
lines,
}: DxfFileProps) => {
const DxfFile = () => {
// State management hooks
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload();
const { setUpdateScene } = useUpdateScene();
const { toggleView } = useToggleView();
const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
@@ -50,55 +31,34 @@ const DxfFile = ({
* Loads initial points and lines from the DXF data and updates the scene.
*/
useEffect(() => {
if (
dfxWallGenerate &&
dragPointControls &&
floorPlanGroupPoint &&
currentLayerPoint &&
floorPlanGroupLine
) {
if (dfxWallGenerate) {
// Store generated lines in ref
lines.current.push(...dfxWallGenerate);
dfxWallGenerate.map((line: any) => {
const lineData = arrayLineToObject(line as Types.Line);
// lines.current.push(...dfxWallGenerate);
// dfxWallGenerate.map((line: any) => {
// const lineData = arrayLineToObject(line as Types.Line);
//REST
// //REST
// setLine(organization, lineData.layer!, lineData.line!, lineData.type!);
// // setLine(organization, lineData.layer!, lineData.line!, lineData.type!);
//SOCKET
// //SOCKET
const input = {
organization,
layer: lineData.layer,
line: lineData.line,
type: lineData.type,
socketId: socket.id,
versionId: selectedVersion?.versionId || '',
projectId,
userId
}
// const input = {
// organization,
// layer: lineData.layer,
// line: lineData.line,
// type: lineData.type,
// socketId: socket.id,
// versionId: selectedVersion?.versionId || '',
// projectId,
// userId
// }
console.log('input: ', input);
socket.emit('v1:Line:create', input);
// socket.emit('v1:Line:create', input);
})
// Load initial points and lines from DXF data
loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls);
loadInitialLine(floorPlanGroupLine, lines);
// Trigger scene update
setUpdateScene(true);
// })
}
}, [
lines,
floorPlanGroupLine,
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls,
dfxWallGenerate,
]);
}, [dfxWallGenerate]);
/**
* Handles transformation changes for individual lines.

View File

@@ -1,100 +0,0 @@
import * as THREE from "three";
import { DragControls } from "three/examples/jsm/controls/DragControls";
import * as CONSTANTS from "../../../types/world/worldConstants";
import DragPoint from "../geomentries/points/dragPoint";
import * as Types from "../../../types/world/worldTypes";
// import { updatePoint } from '../../../services/factoryBuilder/lines/updatePointApi';
import { Socket } from "socket.io-client";
import { getUserData } from "../../../functions/getUserData";
export default async function addDragControl(
dragPointControls: Types.RefDragControl,
currentLayerPoint: Types.RefMeshArray,
state: Types.ThreeState,
floorPlanGroupPoint: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines,
onlyFloorlines: Types.RefOnlyFloorLines,
socket: Socket<any>,
projectId?: string,
versionId?: string
) {
////////// Dragging Point and also change the size to indicate during hover //////////
dragPointControls.current = new DragControls(
currentLayerPoint.current,
state.camera,
state.gl.domElement
);
dragPointControls.current.enabled = false;
const { userId, organization, email } = getUserData();
dragPointControls.current.addEventListener("drag", function (event) {
const object = event.object;
if (object.visible) {
(state.controls as any).enabled = false;
DragPoint(
event as any,
floorPlanGroupPoint,
floorPlanGroupLine,
state.scene,
lines,
onlyFloorlines
);
} else {
(state.controls as any).enabled = true;
}
});
dragPointControls.current.addEventListener("dragstart", function (event) { });
dragPointControls.current.addEventListener("dragend", async function (event) {
if (!dragPointControls.current) return;
//REST
// await updatePoint(
// organization,
// { "x": event.object.position.x, "y": 0.01, "z": event.object.position.z },
// event.object.uuid,
// )
//SOCKET
const data = {
organization,
position: {
x: event.object.position.x,
y: 0.01,
z: event.object.position.z,
},
uuid: event.object.uuid,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:update", data);
if (state.controls) {
(state.controls as any).enabled = true;
}
});
dragPointControls.current.addEventListener("hoveron", function (event: any) {
if ((event.object as Types.Mesh).name === "point") {
event.object.material.uniforms.uInnerColor.value.set(
event.object.userData.color
);
}
});
dragPointControls.current.addEventListener("hoveroff", function (event: any) {
if ((event.object as Types.Mesh).name === "point") {
event.object.material.uniforms.uInnerColor.value.set(
new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor)
);
}
});
}

View File

@@ -1,8 +0,0 @@
import * as Types from "../../../types/world/worldTypes";
export default function handleContextMenu(
menuVisible: Types.Boolean,
setMenuVisible: Types.BooleanState
): void {
// setMenuVisible(true)
}

View File

@@ -1,61 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../types/world/worldTypes";
function handleMeshDown(
event: Types.MeshEvent,
currentWallItem: Types.RefMesh,
setSelectedWallItem: Types.setSelectedWallItemSetState,
setSelectedItemsIndex: Types.setSelectedItemsIndexSetState,
wallItems: Types.wallItems,
toggleView: Types.Boolean
): void {
////////// To select which of the Wall item and CSG is selected to be dragged //////////
if (!toggleView) {
if (currentWallItem.current) {
currentWallItem.current.children.forEach((child) => {
if ((child as THREE.Mesh).isMesh && child.name !== "CSG_REF") {
const material = (child as THREE.Mesh).material;
if (Array.isArray(material)) {
material.forEach(mat => {
if (mat instanceof THREE.MeshStandardMaterial) {
mat.emissive = new THREE.Color("black");
}
});
} else if (material instanceof THREE.MeshStandardMaterial) {
material.emissive = new THREE.Color("black");
}
}
});
currentWallItem.current = null;
setSelectedWallItem(null);
setSelectedItemsIndex(null);
}
if (event.intersections.length > 0) {
if (event.object) {
const wallItemModel = event.object;
let currentObject = wallItemModel as THREE.Object3D;
while (currentObject) {
if (currentObject.name === "Scene") {
break;
}
currentObject = currentObject.parent as THREE.Object3D;
}
if (!currentObject) return;
const clickedIndex = wallItems.findIndex((item) => item.model?.uuid === currentObject.uuid);
if (clickedIndex !== -1) {
setSelectedItemsIndex(clickedIndex);
const wallItemModel = wallItems[clickedIndex]?.model;
if (wallItemModel) {
setSelectedWallItem(wallItemModel);
}
}
}
}
}
}
export default handleMeshDown;

View File

@@ -1,34 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../types/world/worldTypes";
function handleMeshMissed(
currentWallItem: Types.RefMesh,
setSelectedWallItem: Types.setSelectedWallItemSetState,
setSelectedItemsIndex: Types.setSelectedItemsIndexSetState
): void {
////////// If an item is selected and then clicked outside other than the selected object, this runs and removes the color of the selected object and sets setSelectedWallItem and setSelectedItemsIndex as null //////////
if (currentWallItem.current) {
currentWallItem.current.children.forEach((child) => {
if ((child as THREE.Mesh).isMesh && child.name !== "CSG_REF") {
const material = (child as THREE.Mesh).material;
if (Array.isArray(material)) {
material.forEach(mat => {
if (mat instanceof THREE.MeshStandardMaterial) {
mat.emissive = new THREE.Color("black");
}
});
} else if (material instanceof THREE.MeshStandardMaterial) {
material.emissive = new THREE.Color("black");
}
}
});
currentWallItem.current = null;
setSelectedWallItem(null);
setSelectedItemsIndex(null);
}
}
export default handleMeshMissed;

View File

@@ -0,0 +1,50 @@
import { useMemo } from 'react';
import { DoubleSide, Shape, Vector2 } from 'three';
import { Extrude } from '@react-three/drei';
import * as Constants from '../../../../../types/world/worldConstants';
function Floor2DInstance({ floor }: { floor: Floor }) {
const savedTheme: string | null = localStorage.getItem("theme");
const shape = useMemo(() => {
const shape = new Shape();
const points = floor.points.map(p => new Vector2(p.position[0], p.position[2]));
if (points.length < 3) return null;
shape.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
}
return shape;
}, [floor]);
if (!shape) return null;
return (
<mesh
castShadow
receiveShadow
name={`Floor-2D-${floor.floorUuid}`}
rotation={[Math.PI / 2, 0, 0]}
position={[0, 0, 0]}
userData={floor}
>
<Extrude
name={`Floor-${floor.floorUuid}`}
args={[shape, {
depth: Constants.floorConfig.height,
}]}
userData={floor}
>
<meshBasicMaterial
color={savedTheme === "dark" ? "#808080" : "#808080"}
side={DoubleSide}
transparent
opacity={0.4}
depthWrite={false}
/>
</Extrude>
</mesh>
);
}
export default Floor2DInstance;

View File

@@ -0,0 +1,108 @@
import { useMemo } from 'react';
import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace } from 'three';
import { useLoader } from '@react-three/fiber';
import { Extrude } from '@react-three/drei';
import useModuleStore from '../../../../../store/useModuleStore';
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
import { useToggleView } from '../../../../../store/builder/store';
import * as Constants from '../../../../../types/world/worldConstants';
import texturePath from "../../../../../assets/textures/floor/white.png";
import texturePathDark from "../../../../../assets/textures/floor/black.png";
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
function FloorInstance({ floor }: { floor: Floor }) {
const { togglView } = useToggleView();
const { activeModule } = useModuleStore();
const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore();
const savedTheme = localStorage.getItem('theme');
const materials: Record<string, string> = {
"Default Material": savedTheme === "dark" ? texturePathDark : texturePath,
"Material 1": savedTheme === "dark" ? material1 : material1,
};
const shape = useMemo(() => {
const shape = new Shape();
const points = floor.points.map(p => new Vector2(p.position[0], p.position[2]));
if (points.length < 3) return null;
shape.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
}
return shape;
}, [floor]);
const textureScale = Constants.floorConfig.textureScale;
const [topTexture, sideTexture] = useLoader(
TextureLoader,
[
materials[floor.topMaterial] || materials['Default Material'],
materials[floor.sideMaterial] || materials['Default Material']
]
);
if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null;
[topTexture, sideTexture].forEach(tex => {
tex.wrapS = tex.wrapT = RepeatWrapping;
tex.repeat.set(textureScale, textureScale);
tex.colorSpace = SRGBColorSpace;
});
if (!shape) return null;
return (
<mesh
castShadow
receiveShadow
name={`Floor-${floor.floorUuid}`}
rotation={[Math.PI / 2, 0, 0]}
position={[0, !floor.isBeveled ? (floor.floorDepth - 0.1) : (floor.floorDepth - 0.2), 0]}
userData={floor}
onDoubleClick={(e) => {
if (!togglView && activeModule === 'builder') {
if (e.object.userData.floorUuid) {
e.stopPropagation();
setSelectedFloor(e.object);
setSelectedDecal(null);
}
}
}}
onPointerMissed={() => {
if (selectedFloor && selectedFloor.userData.floorUuid === floor.floorUuid) {
setSelectedFloor(null);
}
}}
>
<Extrude
name={`Floor-${floor.floorUuid}`}
args={[shape, {
depth: !floor.isBeveled ? floor.floorDepth : (floor.floorDepth - 0.1),
bevelEnabled: floor.isBeveled,
bevelSegments: floor.bevelStrength,
bevelOffset: -0.1,
bevelSize: 0.1,
bevelThickness: 0.1,
}]}
userData={floor}
>
<meshStandardMaterial
attach="material-0"
color={Constants.floorConfig.defaultColor}
map={topTexture}
side={DoubleSide}
/>
<meshStandardMaterial
attach="material-1"
color={Constants.floorConfig.defaultColor}
map={sideTexture}
side={DoubleSide}
/>
</Extrude>
</mesh>
);
}
export default FloorInstance;

View File

@@ -0,0 +1,133 @@
import React, { useEffect, useMemo } from 'react';
import { Vector3 } from 'three';
import { Html } from '@react-three/drei';
import { useSceneContext } from '../../../scene/sceneContext';
import { useToggleView } from '../../../../store/builder/store';
import Line from '../../line/line';
import Point from '../../point/point';
import FloorInstance from './Instance/floorInstance';
import Floor2DInstance from './Instance/floor2DInstance';
function FloorInstances() {
const { floorStore } = useSceneContext();
const { floors } = floorStore();
const { toggleView } = useToggleView();
useEffect(() => {
// console.log('floors: ', floors);
}, [floors])
const allPoints = useMemo(() => {
const points: Point[] = [];
const seenUuids = new Set<string>();
floors.forEach(floor => {
floor.points.forEach(point => {
if (!seenUuids.has(point.pointUuid)) {
seenUuids.add(point.pointUuid);
points.push(point);
}
});
});
return points;
}, [floors]);
const allLines = useMemo(() => {
const lines: { start: Point; end: Point; key: string }[] = [];
const seenUuids = new Set<string>();
floors.forEach((floor) => {
const points = floor.points;
if (points.length < 2) return;
for (let i = 0; i < points.length; i++) {
const current = points[i];
const next = points[(i + 1) % points.length];
const lineKey = `${current.pointUuid}-${next.pointUuid}`;
if (current.pointUuid !== next.pointUuid && !seenUuids.has(lineKey)) {
seenUuids.add(lineKey);
lines.push({
start: current,
end: next,
key: lineKey
});
}
}
});
return lines;
}, [floors]);
return (
<>
{!toggleView && floors.length > 0 && (
<mesh name='Floors-Group'>
{floors.map((floor) => (
<FloorInstance key={floor.floorUuid} floor={floor} />
))}
</mesh>
)}
{toggleView && floors.length > 0 && (
<mesh name='Floors-2D-Group'>
{floors.map((floor) => (
<Floor2DInstance key={floor.floorUuid} floor={floor} />
))}
</mesh>
)}
{toggleView && (
<>
<group name='Floor-Points-Group'>
{allPoints.map((point) => (
<Point key={point.pointUuid} point={point} />
))}
</group>
<group name='Floor-Lines-Group'>
{allLines.map(({ start, end, key }) => (
<Line key={key} points={[start, end]} />
))}
{allLines.map((line) => {
const { start, end, key } = line;
const textPosition = new Vector3().addVectors(new Vector3(...start.position), new Vector3(...end.position)).divideScalar(2);
const distance = new Vector3(...start.position).distanceTo(new Vector3(...end.position));
return (
<React.Fragment key={key}>
{toggleView &&
<Html
key={`${start.pointUuid}_${end.pointUuid}`}
userData={line}
position={[textPosition.x, 1, textPosition.z]}
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
prepend
sprite
>
<div
key={key}
className={`distance ${key}`}
>
{distance.toFixed(2)} m
</div>
</Html>
}
</React.Fragment>
)
})}
</group>
</>
)}
</>
)
}
export default FloorInstances;

View File

@@ -0,0 +1,267 @@
import * as THREE from 'three'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useSceneContext } from '../../../scene/sceneContext';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../version/versionContext';
import { getUserData } from '../../../../functions/getUserData';
import ReferencePoint from '../../point/reference/referencePoint';
import ReferenceFloor from './referenceFloor';
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
function FloorCreator() {
const { scene, camera, raycaster, gl, pointer } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore();
const { floorStore } = useSceneContext();
const { addFloor, getFloorPointById, getFloorByPoints } = floorStore();
const drag = useRef(false);
const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const [tempPoints, setTempPoints] = useState<Point[]>([]);
const [isCreating, setIsCreating] = useState(false);
const { floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore();
useEffect(() => {
const canvasElement = gl.domElement;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = true;
drag.current = false;
}
};
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = false;
}
};
const onMouseMove = () => {
if (isLeftMouseDown) {
drag.current = true;
}
};
const onMouseClick = () => {
if (drag.current || !toggleView) return;
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (!position) return;
const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Point');
// const floorIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Line');
// if (floorIntersect && !pointIntersects) {
// }
const newPoint: Point = {
pointUuid: THREE.MathUtils.generateUUID(),
pointType: 'Floor',
position: [position.x, position.y, position.z],
layer: activeLayer
};
if (snappedPosition && snappedPoint) {
newPoint.pointUuid = snappedPoint.pointUuid;
newPoint.position = snappedPosition;
newPoint.layer = snappedPoint.layer;
}
if (snappedPoint && snappedPoint.pointUuid === tempPoints[tempPoints.length - 1]?.pointUuid) { return }
if (snappedPosition && !snappedPoint) {
newPoint.position = snappedPosition;
}
if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) {
const floor: Floor = {
floorUuid: THREE.MathUtils.generateUUID(),
floorName: "Floor",
points: tempPoints,
topMaterial,
sideMaterial,
floorDepth,
isBeveled,
bevelStrength,
decals: [],
};
addFloor(floor);
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: floor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
setTempPoints([]);
setIsCreating(false);
} else if (isCreating && snappedPoint && !tempPoints.some(p => p.pointUuid === snappedPoint.pointUuid)) {
setTempPoints(prev => [...prev, newPoint]);
setIsCreating(true);
} else if (pointIntersects) {
if (tempPoints.length > 2 && isCreating && pointIntersects.object.uuid === tempPoints[0].pointUuid) {
const floor: Floor = {
floorUuid: THREE.MathUtils.generateUUID(),
floorName: "Floor",
points: tempPoints,
topMaterial,
sideMaterial,
floorDepth,
isBeveled,
bevelStrength,
decals: [],
};
addFloor(floor);
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: floor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
setTempPoints([]);
setIsCreating(false);
} else if (tempPoints.length === 0 || (tempPoints.length > 1 && !tempPoints.slice(1).some(p => p.pointUuid === pointIntersects.object.uuid))) {
tempPoints.push(pointIntersects.object.userData as Point);
setIsCreating(true);
}
} else {
setTempPoints(prev => [...prev, newPoint]);
setIsCreating(true);
}
};
const onContext = (event: any) => {
event.preventDefault();
if (isCreating) {
if (tempPoints.length >= 3) {
const floor: Floor = {
floorUuid: THREE.MathUtils.generateUUID(),
floorName: "Floor",
points: tempPoints,
topMaterial,
sideMaterial,
floorDepth,
isBeveled,
bevelStrength,
decals: [],
};
addFloor(floor);
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: floor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
}
setTempPoints([]);
setIsCreating(false);
}
};
if (toolMode === "Floor" && toggleView) {
if (tempPoints.length === 0) {
setSnappedPosition(null);
setSnappedPoint(null);
}
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("click", onMouseClick);
canvasElement.addEventListener("contextmenu", onContext);
} else {
setTempPoints([]);
setIsCreating(false);
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext);
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext);
};
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
return (
<>
{toggleView &&
<>
<group name='Floor-Reference-Points-Group'>
{tempPoints.map((point) => (
<ReferencePoint key={point.pointUuid} point={point} />
))}
</group>
{tempPoints.length > 0 &&
<ReferenceFloor tempPoints={tempPoints} />
}
</>
}
</>
)
}
export default FloorCreator

View File

@@ -0,0 +1,165 @@
import { useEffect, useRef, useState, useMemo } from 'react';
import * as THREE from 'three';
import { useFrame, useThree } from '@react-three/fiber';
import { Extrude, Html } from '@react-three/drei';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
import ReferenceLine from '../../line/reference/referenceLine';
interface ReferenceFloorProps {
tempPoints: Point[];
}
function ReferenceFloor({ tempPoints }: Readonly<ReferenceFloorProps>) {
const { floorDepth, isBeveled, bevelStrength, setSnappedPosition, setSnappedPoint } = useBuilderStore();
const { pointer, raycaster, camera } = useThree();
const { toolMode } = useToolMode();
const { toggleView } = useToggleView();
const { activeLayer } = useActiveLayer();
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
const finalPosition = useRef<[number, number, number] | null>(null);
const [tempFloor, setTempFloor] = useState<Floor | null>(null);
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[tempPoints.length - 1]?.position || null);
const { snapFloorPoint } = usePointSnapping({ uuid: 'temp-Floor', pointType: 'Floor', position: directionalSnap.position || [0, 0, 0], });
useFrame(() => {
if (toolMode === 'Floor' && toggleView && tempPoints.length > 0) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
raycaster.ray.intersectPlane(plane, intersectionPoint);
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
if (!intersectionPoint) return;
const snapped = snapFloorPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints.length > 2 ? [tempPoints[0]] : []);
if (snapped.isSnapped && snapped.snappedPoint) {
finalPosition.current = snapped.position;
setSnappedPosition(snapped.position);
setSnappedPoint(snapped.snappedPoint);
} else if (directionalSnap.isSnapped) {
finalPosition.current = directionalSnap.position;
setSnappedPosition(directionalSnap.position);
setSnappedPoint(null);
} else {
finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z];
setSnappedPosition(null);
setSnappedPoint(null);
}
if (!finalPosition.current) return;
const floorPoints: Point[] = [
...tempPoints,
{
pointUuid: 'temp-point',
pointType: 'Floor',
position: finalPosition.current,
layer: activeLayer,
},
];
setTempFloor({
floorUuid: 'temp-floor',
floorName: 'temp-floor',
points: floorPoints,
topMaterial: 'default',
sideMaterial: 'default',
floorDepth,
bevelStrength,
isBeveled,
decals: [],
});
} else if (tempFloor !== null) {
setTempFloor(null);
}
});
useEffect(() => {
setTempFloor(null);
}, [toolMode, toggleView, tempPoints.length, floorDepth, bevelStrength, isBeveled, activeLayer]);
const allLines = useMemo(() => {
if (!tempFloor || tempFloor.points.length < 2) return [];
const lines: [Point, Point][] = [];
const pts = tempFloor.points;
for (let i = 0; i < pts.length - 1; i++) {
lines.push([pts[i], pts[i + 1]]);
}
return lines;
}, [tempFloor]);
if (!tempFloor) return null;
return (
<group name="Floor-Reference-Group">
{allLines.map(([p1, p2], idx) => {
const v1 = new THREE.Vector3(...p1.position);
const v2 = new THREE.Vector3(...p2.position);
const mid = new THREE.Vector3().addVectors(v1, v2).multiplyScalar(0.5);
const dist = v1.distanceTo(v2);
return (
<group key={`${p1.pointUuid}-${p2.pointUuid}`}>
<ReferenceLine points={[p1, p2]} />
{toggleView && (
<Html
position={[mid.x, 1, mid.z]}
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
prepend
sprite
>
<div className={`distance ref-line-${idx}`}>{dist.toFixed(2)} m</div>
</Html>
)}
</group>
);
})}
{tempPoints.length >= 3 && (
<Floor floor={tempFloor.points} />
)}
</group>
);
}
export default ReferenceFloor;
function Floor({ floor }: { floor: Point[] }) {
const savedTheme: string | null = localStorage.getItem('theme');
const shape = useMemo(() => {
const shape = new THREE.Shape();
const points = floor.map(p => new THREE.Vector2(p.position[0], p.position[2]));
if (points.length < 3) return null;
shape.moveTo(points[0].x, points[0].y);
points.forEach((pt) => { shape.lineTo(pt.x, pt.y); });
return shape;
}, [floor]);
if (!shape) return null;
return (
<group name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
<Extrude
args={[shape, { depth: 0.001, bevelEnabled: false }]}
position={[0, 0, 0]}
receiveShadow
>
<meshStandardMaterial color={savedTheme === "dark" ? "#808080" : "#808080"} depthWrite={false} transparent opacity={0.3} side={THREE.DoubleSide} />
</Extrude>
</group>
);
}

View File

@@ -0,0 +1,55 @@
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 FloorCreator from './floorCreator/floorCreator';
import FloorInstances from './Instances/floorInstances';
import { getFloorsApi } from '../../../services/factoryBuilder/floor/getFloorsApi';
function FloorGroup() {
const { togglView } = useToggleView();
const { setSelectedFloor, setSelectedDecal } = useBuilderStore();
const { activeModule } = useModuleStore();
const { activeTool } = useActiveTool();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { floorStore } = useSceneContext();
const { setFloors } = floorStore();
const { projectId } = useParams();
useEffect(() => {
if (togglView || activeModule !== 'builder') {
setSelectedFloor(null);
setSelectedDecal(null);
}
}, [togglView, activeModule, activeTool])
useEffect(() => {
if (projectId && selectedVersion) {
getFloorsApi(projectId, selectedVersion?.versionId || '').then((floors) => {
if (floors && floors.length > 0) {
setFloors(floors);
} else {
setFloors([]);
}
}).catch((err) => {
console.log(err);
})
}
}, [projectId, selectedVersion?.versionId])
return (
<>
<FloorCreator />
<FloorInstances />
</>
)
}
export default FloorGroup

View File

@@ -1,87 +0,0 @@
import * as THREE from 'three';
import * as CONSTANTS from '../../../types/world/worldConstants';
import * as Types from "../../../types/world/worldTypes";
function DeletableLineorPoint(
state: Types.ThreeState,
plane: Types.RefMesh,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
hoveredDeletableLine: Types.RefMesh,
hoveredDeletablePoint: Types.RefMesh
): void {
////////// Altering the color of the hovered line or point during the deletion time //////////
if (!plane.current) return;
let intersects = state.raycaster.intersectObject(plane.current, true);
let visibleIntersectLines;
if (floorPlanGroupLine.current) { visibleIntersectLines = state.raycaster?.intersectObjects(floorPlanGroupLine.current.children, true); }
const visibleIntersectLine = visibleIntersectLines?.find(intersect => intersect.object.visible) as THREE.Line | undefined || null;
let visibleIntersectPoints;
if (floorPlanGroupPoint.current) {
visibleIntersectPoints = state.raycaster?.intersectObjects(floorPlanGroupPoint.current.children, true);
}
const visibleIntersectPoint = visibleIntersectPoints?.find(intersect => intersect.object.visible) as THREE.Mesh | undefined;
function getLineColor(lineType: string | undefined): string {
switch (lineType) {
case CONSTANTS.lineConfig.wallName: return CONSTANTS.lineConfig.wallColor;
case CONSTANTS.lineConfig.floorName: return CONSTANTS.lineConfig.floorColor;
case CONSTANTS.lineConfig.aisleName: return CONSTANTS.lineConfig.aisleColor;
default: return CONSTANTS.lineConfig.defaultColor;
}
}
if (intersects.length > 0) {
if (visibleIntersectPoint) {
if (hoveredDeletableLine.current) {
const lineType = hoveredDeletableLine.current.userData.linePoints[1]?.[3];
const color = getLineColor(lineType);
(hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color(color);
hoveredDeletableLine.current = null;
}
hoveredDeletablePoint.current = (visibleIntersectPoint as any).object;
(hoveredDeletablePoint.current as any).material.uniforms.uInnerColor.value.set(new THREE.Color("red"));
(hoveredDeletablePoint.current as any).material.uniforms.uOuterColor.value.set(new THREE.Color("red"));
// (hoveredDeletablePoint.current as THREE.Mesh).scale.set(1.5, 1.5, 1.5);
} else if (hoveredDeletablePoint.current) {
(hoveredDeletablePoint.current as any).material.uniforms.uInnerColor.value.set(CONSTANTS.pointConfig.defaultInnerColor);
(hoveredDeletablePoint.current as any).material.uniforms.uOuterColor.value.set((hoveredDeletablePoint.current as any).userData.color);
// hoveredDeletablePoint.current.scale.set(1, 1, 1);
hoveredDeletablePoint.current = null;
}
if (visibleIntersectLine && !visibleIntersectPoint) {
if (hoveredDeletableLine.current) {
const lineType = hoveredDeletableLine.current.userData.linePoints[1]?.[3];
const color = getLineColor(lineType);
(hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color(color);
hoveredDeletableLine.current = null;
}
if (hoveredDeletablePoint.current) {
(hoveredDeletablePoint.current as any).material.uniforms.uInnerColor.value.set(CONSTANTS.pointConfig.defaultInnerColor);
(hoveredDeletablePoint.current as any).material.uniforms.uOuterColor.value.set((hoveredDeletablePoint.current as any).userData.color);
// hoveredDeletablePoint.current.scale.set(1, 1, 1);
hoveredDeletablePoint.current = null;
}
hoveredDeletableLine.current = (visibleIntersectLine as any).object;
if (hoveredDeletableLine.current) {
(hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color("red");
}
} else if (hoveredDeletableLine.current) {
const lineType = hoveredDeletableLine.current.userData.linePoints[1]?.[3];
const color = getLineColor(lineType);
(hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color(color);
hoveredDeletableLine.current = null;
}
}
}
export default DeletableLineorPoint;

View File

@@ -1,93 +0,0 @@
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from '../../../types/world/worldConstants';
import createAndMoveReferenceLine from "../geomentries/lines/createAndMoveReferenceLine";
async function Draw(
state: Types.ThreeState,
plane: Types.RefMesh,
cursorPosition: Types.Vector3,
floorPlanGroupPoint: Types.RefGroup,
snappedPoint: Types.RefVector3,
isSnapped: Types.RefBoolean,
isSnappedUUID: Types.RefString,
line: Types.RefLine,
ispreSnapped: Types.RefBoolean,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
setRefTextUpdate: any,
Tube: Types.RefTubeGeometry,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean,
toolMode: Types.String,
): Promise<void> {
////////// Snapping the cursor during the drawing time and also changing the color of the intersected lines //////////
if (!plane.current) return;
const intersects = state.raycaster.intersectObject(plane.current, true);
if (intersects.length > 0 && (toolMode === "Wall" || toolMode === "Floor")) {
const intersectionPoint = intersects[0].point;
cursorPosition.copy(intersectionPoint);
const snapThreshold = 1;
if (line.current.length === 0) {
for (const point of floorPlanGroupPoint.current.children) {
const pointType = point.userData.type;
const canSnap =
((toolMode === "Wall") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName)) ||
((toolMode === "Floor") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName))
if (canSnap && cursorPosition.distanceTo(point.position) < snapThreshold + 0.5 && point.visible) {
cursorPosition.copy(point.position);
snappedPoint.current = point.position;
ispreSnapped.current = true;
isSnapped.current = false;
isSnappedUUID.current = point.uuid;
break;
} else {
ispreSnapped.current = false;
}
}
} else if (line.current.length > 0 && line.current[0]) {
for (const point of floorPlanGroupPoint.current.children) {
const pointType = point.userData.type;
let canSnap =
((toolMode === "Wall") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName)) ||
((toolMode === "Floor") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName))
if (canSnap && cursorPosition.distanceTo(point.position) < snapThreshold && point.visible) {
cursorPosition.copy(point.position);
snappedPoint.current = point.position;
isSnapped.current = true;
ispreSnapped.current = false;
isSnappedUUID.current = point.uuid;
break;
} else {
isSnapped.current = false;
}
}
createAndMoveReferenceLine(
line.current[0][0],
cursorPosition,
isSnapped,
ispreSnapped,
line,
setRefTextUpdate,
floorPlanGroup,
ReferenceLineMesh,
LineCreated,
Tube,
anglesnappedPoint,
isAngleSnapped
);
}
}
}
export default Draw;

View File

@@ -1,62 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import texturePath from "../../../../assets/textures/floor/white.png";
import texturePathDark from "../../../../assets/textures/floor/black.png";
// Cache for materials
const materialCache = new Map<string, THREE.Material>();
export default function addFloorToScene(
shape: THREE.Shape,
layer: number,
floorGroup: Types.RefGroup,
userData: any,
) {
const savedTheme: string | null = localStorage.getItem('theme');
const textureLoader = new THREE.TextureLoader();
const textureScale = CONSTANTS.floorConfig.textureScale;
const materialKey = `floorMaterial_${textureScale}`;
let material: THREE.Material;
if (materialCache.has(materialKey)) {
material = materialCache.get(materialKey) as THREE.Material;
} else {
const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath);
// const floorTexture = textureLoader.load(texturePath);
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set(textureScale, textureScale);
floorTexture.colorSpace = THREE.SRGBColorSpace;
material = new THREE.MeshStandardMaterial({
map: floorTexture,
side: THREE.DoubleSide,
});
materialCache.set(materialKey, material);
}
const extrudeSettings = {
depth: CONSTANTS.floorConfig.height,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const mesh = new THREE.Mesh(geometry, material);
mesh.receiveShadow = true;
mesh.position.y = (layer) * CONSTANTS.wallConfig.height;
mesh.rotateX(Math.PI / 2);
mesh.name = `Floor_Layer_${layer}`;
// Store UUIDs for debugging or future processing
mesh.userData.uuids = userData;
floorGroup.current.add(mesh);
}

View File

@@ -1,275 +0,0 @@
import * as THREE from "three";
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import addPointToScene from "../points/addPointToScene";
import addLineToScene from "../lines/addLineToScene";
import splitLine from "../lines/splitLine";
import removeReferenceLine from "../lines/removeReferenceLine";
import getClosestIntersection from "../lines/getClosestIntersection";
import arrayLineToObject from "../lines/lineConvertions/arrayLineToObject";
// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi';
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
async function drawOnlyFloor(
raycaster: THREE.Raycaster,
state: Types.ThreeState,
camera: THREE.Camera,
plane: Types.RefMesh,
floorPlanGroupPoint: Types.RefGroup,
snappedPoint: Types.RefVector3,
isSnapped: Types.RefBoolean,
isSnappedUUID: Types.RefString,
line: Types.RefLine,
ispreSnapped: Types.RefBoolean,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean,
onlyFloorline: Types.RefOnlyFloorLine,
onlyFloorlines: Types.RefOnlyFloorLines,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl,
setNewLines: any,
setDeletedLines: any,
activeLayer: Types.Number,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): Promise<void> {
////////// Creating lines Based on the positions clicked //////////
if (!plane.current) return;
const { userId, organization, email } = getUserData();
const intersects = raycaster.intersectObject(plane.current, true);
const intersectsLines = raycaster.intersectObjects(
floorPlanGroupLine.current.children,
true
);
const intersectsPoint = raycaster.intersectObjects(
floorPlanGroupPoint.current.children,
true
);
const VisibleintersectsPoint = intersectsPoint.find(
(intersect) => intersect.object.visible
);
const visibleIntersect = intersectsLines.find(
(intersect) =>
intersect.object.visible &&
intersect.object.name !== CONSTANTS.lineConfig.referenceName
);
if (
(intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) &&
intersectsLines.length > 0 &&
!isSnapped.current &&
!ispreSnapped.current
) {
////////// Clicked on a preexisting Line //////////
if (
visibleIntersect &&
(intersectsLines[0].object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.floorName ||
intersectsLines[0].object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.wallName)
) {
let pointColor, lineColor;
if (
intersectsLines[0].object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.wallName
) {
pointColor = CONSTANTS.pointConfig.wallOuterColor;
lineColor = CONSTANTS.lineConfig.wallColor;
} else {
pointColor = CONSTANTS.pointConfig.floorOuterColor;
lineColor = CONSTANTS.lineConfig.floorColor;
}
let IntersectsPoint = new THREE.Vector3(
intersects[0].point.x,
0.01,
intersects[0].point.z
);
if (
isAngleSnapped.current &&
line.current.length > 0 &&
anglesnappedPoint.current
) {
IntersectsPoint = anglesnappedPoint.current;
}
if (visibleIntersect.object instanceof THREE.Mesh) {
const ThroughPoint =
visibleIntersect.object.geometry.parameters.path.getPoints(
CONSTANTS.lineConfig.lineIntersectionPoints
);
let intersectionPoint = getClosestIntersection(
ThroughPoint,
IntersectsPoint
);
if (intersectionPoint) {
const newLines = splitLine(
visibleIntersect,
intersectionPoint,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
lines,
setDeletedLines,
floorPlanGroupLine,
socket,
pointColor,
lineColor,
intersectsLines[0].object.userData.linePoints[0][3],
projectId
);
setNewLines([newLines[0], newLines[1]]);
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.floorName,
]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
projectId,
versionId,
userId,
};
console.log('input: ', input);
socket.emit("v1:Line:create", input);
setNewLines([newLines[0], newLines[1], line.current]);
onlyFloorline.current.push(line.current as Types.Line);
onlyFloorlines.current.push(onlyFloorline.current);
onlyFloorline.current = [];
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.floorColor,
line.current,
floorPlanGroupLine
);
removeReferenceLine(
floorPlanGroup,
ReferenceLineMesh,
LineCreated,
line
);
}
return;
}
}
}
}
if (intersects.length > 0 && intersectsLines.length === 0) {
////////// Clicked on an empty place or a point //////////
let intersectionPoint = intersects[0].point;
if (
isAngleSnapped.current &&
line.current.length > 0 &&
anglesnappedPoint.current
) {
intersectionPoint = anglesnappedPoint.current;
}
if (isSnapped.current && line.current.length > 0 && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (ispreSnapped.current && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (!isSnapped.current && !ispreSnapped.current) {
addPointToScene(
intersectionPoint,
CONSTANTS.pointConfig.floorOuterColor,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
CONSTANTS.lineConfig.floorName
);
} else {
ispreSnapped.current = false;
isSnapped.current = false;
}
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.floorName,
]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
onlyFloorline.current.push(line.current as Types.Line);
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
console.log('input: ', input);
socket.emit("v1:Line:create", input);
setNewLines([line.current]);
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.floorColor,
line.current,
floorPlanGroupLine
);
const lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
if (isSnapped.current) {
////////// Add this to stop the drawing mode after snapping //////////
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
onlyFloorlines.current.push(onlyFloorline.current);
onlyFloorline.current = [];
}
}
}
export default drawOnlyFloor;

View File

@@ -1,50 +0,0 @@
import * as THREE from 'three';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import addRoofToScene from '../roofs/addRoofToScene';
import * as Types from "../../../../types/world/worldTypes";
import loadOnlyFloors from './loadOnlyFloors';
import addFloorToScene from './addFloorToScene';
import getRoomsFromLines from '../lines/getRoomsFromLines';
async function loadFloor(
lines: Types.RefLines,
floorGroup: Types.RefGroup,
): Promise<void> {
if (!floorGroup.current) return;
floorGroup.current.children = [];
if (lines.current.length > 2) {
const linesByLayer = lines.current.reduce((acc: { [key: number]: any[] }, pair) => {
const layer = pair[0][2];
if (!acc[layer]) acc[layer] = [];
acc[layer].push(pair);
return acc;
}, {});
for (const layer in linesByLayer) {
// Only Floor Polygons
loadOnlyFloors(floorGroup, linesByLayer, layer);
const rooms: Types.Rooms = await getRoomsFromLines({ current: linesByLayer[layer] });
rooms.forEach(({ coordinates: room, layer }) => {
const userData = room.map(point => point.uuid);
const shape = new THREE.Shape();
shape.moveTo(room[0].position.x, room[0].position.z);
room.forEach(point => shape.lineTo(point.position.x, point.position.z));
shape.closePath();
// Floor Polygons
addFloorToScene(shape, (layer - 1) * CONSTANTS.wallConfig.height, floorGroup, userData);
// Roof Polygons
addRoofToScene(shape, (layer - 1) * CONSTANTS.wallConfig.height, userData, floorGroup);
});
}
}
}
export default loadFloor;

View File

@@ -1,183 +0,0 @@
import * as THREE from 'three';
import * as turf from '@turf/turf';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
function loadOnlyFloors(
floorGroup: Types.RefGroup,
linesByLayer: any,
layer: any,
): void {
////////// Creating polygon floor based on the onlyFloorlines.current which does not add roof to it, The lines are still stored in Lines.current as well //////////
let floorsInLayer = linesByLayer[layer];
floorsInLayer = floorsInLayer.filter((line: any) => line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName);
const floorResult = floorsInLayer.map((pair: [THREE.Vector3, string, number, string][]) =>
pair.map((point) => ({
position: [point[0].x, point[0].z],
uuid: point[1]
}))
);
const FloorLineFeatures = floorResult.map((line: any) => turf.lineString(line.map((p: any) => p.position)));
function identifyPolygonsAndConnectedLines(FloorLineFeatures: any) {
const floorpolygons = [];
const connectedLines = [];
const unprocessedLines = [...FloorLineFeatures]; // Copy the features
while (unprocessedLines.length > 0) {
const currentLine = unprocessedLines.pop();
const coordinates = currentLine.geometry.coordinates;
// Check if the line is closed (forms a polygon)
if (
coordinates[0][0] === coordinates[coordinates.length - 1][0] &&
coordinates[0][1] === coordinates[coordinates.length - 1][1]
) {
floorpolygons.push(turf.polygon([coordinates])); // Add as a polygon
continue;
}
// Check if the line connects to another line
let connected = false;
for (let i = unprocessedLines.length - 1; i >= 0; i--) {
const otherCoordinates = unprocessedLines[i].geometry.coordinates;
// Check if lines share a start or end point
if (
coordinates[0][0] === otherCoordinates[otherCoordinates.length - 1][0] &&
coordinates[0][1] === otherCoordinates[otherCoordinates.length - 1][1]
) {
// Merge lines
const mergedCoordinates = [...otherCoordinates, ...coordinates.slice(1)];
unprocessedLines[i] = turf.lineString(mergedCoordinates);
connected = true;
break;
} else if (
coordinates[coordinates.length - 1][0] === otherCoordinates[0][0] &&
coordinates[coordinates.length - 1][1] === otherCoordinates[0][1]
) {
// Merge lines
const mergedCoordinates = [...coordinates, ...otherCoordinates.slice(1)];
unprocessedLines[i] = turf.lineString(mergedCoordinates);
connected = true;
break;
}
}
if (!connected) {
connectedLines.push(currentLine); // Add unconnected line as-is
}
}
return { floorpolygons, connectedLines };
}
const { floorpolygons, connectedLines } = identifyPolygonsAndConnectedLines(FloorLineFeatures);
function convertConnectedLinesToPolygons(connectedLines: any) {
return connectedLines.map((line: any) => {
const coordinates = line.geometry.coordinates;
// If the line has more than two points, close the polygon
if (coordinates.length > 2) {
const firstPoint = coordinates[0];
const lastPoint = coordinates[coordinates.length - 1];
// Check if already closed; if not, close it
if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) {
coordinates.push(firstPoint);
}
// Convert the closed line into a polygon
return turf.polygon([coordinates]);
}
// If not enough points for a polygon, return the line unchanged
return line;
});
}
const convertedConnectedPolygons = convertConnectedLinesToPolygons(connectedLines);
if (convertedConnectedPolygons.length > 0) {
const validPolygons = convertedConnectedPolygons.filter(
(polygon: any) => polygon.geometry?.type === "Polygon"
);
if (validPolygons.length > 0) {
floorpolygons.push(...validPolygons);
}
}
function convertPolygonsToOriginalFormat(floorpolygons: any, originalLines: [THREE.Vector3, string, number, string][][]) {
return floorpolygons.map((polygon: any) => {
const coordinates = polygon.geometry.coordinates[0]; // Extract the coordinates array (assume it's a single polygon)
// Map each coordinate back to its original structure
const mappedPoints = coordinates.map((coord: [number, number]) => {
const [x, z] = coord;
// Find the original point matching this coordinate
const originalPoint = originalLines.flat().find(([point]) => point.x === x && point.z === z);
if (!originalPoint) {
console.error(`Original point for coordinate [${x}, ${z}] not found.`);
}
return originalPoint;
});
// Create pairs of consecutive points
const pairs: typeof originalLines = [];
for (let i = 0; i < mappedPoints.length - 1; i++) {
pairs.push([mappedPoints[i], mappedPoints[i + 1]]);
}
return pairs;
});
}
const convertedFloorPolygons: Types.OnlyFloorLines = convertPolygonsToOriginalFormat(floorpolygons, floorsInLayer);
convertedFloorPolygons.forEach((floor) => {
const points: THREE.Vector3[] = [];
floor.forEach((lineSegment) => {
const startPoint = lineSegment[0][0];
points.push(new THREE.Vector3(startPoint.x, startPoint.y, startPoint.z));
});
const lastLine = floor[floor.length - 1];
const endPoint = lastLine[1][0];
points.push(new THREE.Vector3(endPoint.x, endPoint.y, endPoint.z));
const shape = new THREE.Shape();
shape.moveTo(points[0].x, points[0].z);
points.forEach(point => shape.lineTo(point.x, point.z));
shape.closePath();
const extrudeSettings = {
depth: CONSTANTS.floorConfig.height,
bevelEnabled: false
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.floorConfig.defaultColor, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.position.y = (floor[0][0][2] - 1) * CONSTANTS.wallConfig.height;
mesh.rotateX(Math.PI / 2);
mesh.name = `Only_Floor_Line_${floor[0][0][2]}`;
mesh.userData = floor;
floorGroup?.current?.add(mesh);
});
}
export default loadOnlyFloors;

View File

@@ -1,24 +0,0 @@
import * as Types from "../../../../types/world/worldTypes";
function updateFloorLines(
onlyFloorlines: Types.RefOnlyFloorLines,
DragedPoint: Types.Mesh | { uuid: string, position: Types.Vector3 }
): void {
////////// Update onlyFloorlines.current if it contains the dragged point //////////
onlyFloorlines.current.forEach((floorline) => {
floorline.forEach((line) => {
line.forEach((point) => {
const [position, uuid] = point;
if (uuid === DragedPoint.uuid) {
position.x = DragedPoint.position.x;
position.y = 0.01;
position.z = DragedPoint.position.z;
}
});
});
});
}
export default updateFloorLines;

View File

@@ -1,104 +0,0 @@
import { toast } from "react-toastify";
import RemoveConnectedLines from "../lines/removeConnectedLines";
import * as Types from "../../../../types/world/worldTypes";
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
// import { deleteLayer } from '../../../../services/factoryBuilder/lines/deleteLayerApi';
async function DeleteLayer(
removedLayer: Types.Number,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
onlyFloorlines: Types.RefOnlyFloorLines,
floorGroup: Types.RefGroup,
setDeletedLines: any,
setRemovedLayer: Types.setRemoveLayerSetState,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): Promise<void> {
////////// Remove the Lines from the lines.current based on the removed layer and rearrange the layer number that are higher than the removed layer //////////
const removedLines: Types.Lines = lines.current.filter(
(line) => line[0][2] === removedLayer
);
const { userId, organization } = getUserData();
//REST
// await deleteLayer(organization, removedLayer);
//SOCKET
const data = {
organization,
layer: removedLayer,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:delete:layer", data);
////////// Remove Points and lines from the removed layer //////////
removedLines.forEach((line) => {
line.forEach((removedPoint) => {
RemoveConnectedLines(
removedPoint[1],
floorPlanGroupLine,
floorPlanGroupPoint,
setDeletedLines,
lines
);
});
});
////////// Update the remaining lines layer values in the userData and in lines.current //////////
let remaining = lines.current.filter((line) => line[0][2] !== removedLayer);
let updatedLines: Types.Lines = [];
remaining.forEach((line) => {
let newLines: Types.Line = [...line];
if (newLines[0][2] > removedLayer) {
newLines[0][2] -= 1;
newLines[1][2] -= 1;
}
const matchingLine = floorPlanGroupLine.current.children.find(
(l) =>
l.userData.linePoints[0][1] === line[0][1] &&
l.userData.linePoints[1][1] === line[1][1]
);
if (matchingLine) {
const updatedUserData = matchingLine.userData;
updatedUserData.linePoints[0][2] = newLines[0][2];
updatedUserData.linePoints[1][2] = newLines[1][2];
}
updatedLines.push(newLines);
});
lines.current = updatedLines;
localStorage.setItem("Lines", JSON.stringify(lines.current));
////////// Also remove OnlyFloorLines and update it in localstorage //////////
onlyFloorlines.current = onlyFloorlines.current.filter((floor) => {
return floor[0][0][2] !== removedLayer;
});
const meshToRemove: any = floorGroup.current?.children.find(
(mesh) => mesh.name === `Only_Floor_Line_${removedLayer}`
);
if (meshToRemove) {
(<any>meshToRemove.material).dispose();
(<any>meshToRemove.geometry).dispose();
floorGroup.current?.remove(meshToRemove);
}
echo.success("Layer Removed!");
setRemovedLayer(null);
}
export default DeleteLayer;

View File

@@ -1,35 +0,0 @@
import * as Types from "../../../../types/world/worldTypes";
function Layer2DVisibility(
activeLayer: Types.Number,
floorPlanGroup: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl
): void {
if (floorPlanGroup.current && dragPointControls.current) {
currentLayerPoint.current = [];
floorPlanGroupLine.current.children.forEach((line) => {
const linePoints = line.userData.linePoints;
const point1 = floorPlanGroupPoint.current.getObjectByProperty('uuid', linePoints[0][1]) as Types.Mesh;
const point2 = floorPlanGroupPoint.current.getObjectByProperty('uuid', linePoints[1][1]) as Types.Mesh;
if (linePoints[0][2] !== activeLayer && linePoints[1][2] !== activeLayer) {
point1.visible = false;
point2.visible = false;
line.visible = false;
} else {
point1.visible = true;
point2.visible = true;
line.visible = true;
currentLayerPoint.current.push(point1, point2);
}
});
dragPointControls.current!.objects = currentLayerPoint.current;
}
}
export default Layer2DVisibility;

View File

@@ -1,24 +0,0 @@
import * as THREE from "three";
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
function addLineToScene(
start: Types.Vector3,
end: Types.Vector3,
colour: Types.Color,
userData: Types.UserData,
floorPlanGroupLine: Types.RefGroup
): void {
////////// A function that creates and adds lines based on the start, end, and colour from the params, Also adds the userData in the mesh userData //////////
const path = new THREE.CatmullRomCurve3([start, end]);
const geometry = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false);
const material = new THREE.MeshBasicMaterial({ color: colour });
const mesh = new THREE.Mesh(geometry, material);
floorPlanGroupLine.current.add(mesh);
mesh.userData.linePoints = userData;
}
export default addLineToScene;

View File

@@ -1,98 +0,0 @@
import * as THREE from "three";
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
function createAndMoveReferenceLine(
point: Types.Vector3,
cursorPosition: Types.Vector3,
isSnapped: Types.RefBoolean,
ispreSnapped: Types.RefBoolean,
line: Types.RefLine,
setRefTextUpdate: Types.NumberIncrementState,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
Tube: Types.RefTubeGeometry,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean
): void {
////////// Creating new and maintaining the old reference line and also snap the reference line based on its angle //////////
const startPoint = point;
const dx = cursorPosition.x - startPoint.x;
const dz = cursorPosition.z - startPoint.z;
let angle = Math.atan2(dz, dx);
angle = (angle * 180) / Math.PI;
angle = (angle + 360) % 360;
const snapAngles = [0, 90, 180, 270, 360];
const snapThreshold = 2.5;
const closestSnapAngle = snapAngles.reduce((prev, curr) =>
Math.abs(curr - angle) < Math.abs(prev - angle) ? curr : prev
);
if (!isSnapped.current && !ispreSnapped.current && line.current.length > 0) {
if (Math.abs(closestSnapAngle - angle) <= snapThreshold) {
const snappedAngleRad = (closestSnapAngle * Math.PI) / 180;
const distance = Math.sqrt(dx * dx + dz * dz);
const snappedX = startPoint.x + distance * Math.cos(snappedAngleRad);
const snappedZ = startPoint.z + distance * Math.sin(snappedAngleRad);
if (
cursorPosition.distanceTo(
new THREE.Vector3(snappedX, 0.01, snappedZ)
) < 2
) {
cursorPosition.set(snappedX, 0.01, snappedZ);
isAngleSnapped.current = true;
anglesnappedPoint.current = new THREE.Vector3(
snappedX,
0.01,
snappedZ
);
} else {
isAngleSnapped.current = false;
anglesnappedPoint.current = null;
}
} else {
isAngleSnapped.current = false;
anglesnappedPoint.current = null;
}
} else {
isAngleSnapped.current = false;
anglesnappedPoint.current = null;
}
if (!LineCreated.current) {
setRefTextUpdate((prevUpdate) => prevUpdate - 1);
const path = new THREE.LineCurve3(startPoint, cursorPosition);
Tube.current = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false);
const material = new THREE.MeshBasicMaterial({ color: CONSTANTS.lineConfig.helperColor });
ReferenceLineMesh.current = new THREE.Mesh(Tube.current, material);
ReferenceLineMesh.current.name = CONSTANTS.lineConfig.referenceName;
ReferenceLineMesh.current.userData = {
linePoints: { startPoint, cursorPosition },
};
floorPlanGroup.current?.add(ReferenceLineMesh.current);
LineCreated.current = true;
} else {
if (ReferenceLineMesh.current) {
const path = new THREE.LineCurve3(startPoint, new THREE.Vector3(cursorPosition.x, 0.01, cursorPosition.z));
Tube.current = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false);
if (ReferenceLineMesh.current) {
ReferenceLineMesh.current.userData = {
linePoints: { startPoint, cursorPosition },
};
ReferenceLineMesh.current.geometry.dispose();
ReferenceLineMesh.current.geometry = Tube.current;
}
}
}
}
export default createAndMoveReferenceLine;

View File

@@ -1,92 +0,0 @@
import { Socket } from "socket.io-client";
// import { deleteLineApi } from "../../../../services/factoryBuilder/lines/deleteLineApi";
import * as Types from "../../../../types/world/worldTypes";
import { toast } from "react-toastify";
import { getUserData } from "../../../../functions/getUserData";
function deleteLine(
hoveredDeletableLine: Types.RefMesh,
onlyFloorlines: Types.RefOnlyFloorLines,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
setDeletedLines: any,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): void {
const { userId, organization, email } = getUserData();
////////// Deleting a line and the points if they are not connected to any other line //////////
if (!hoveredDeletableLine.current) {
return;
}
const linePoints = hoveredDeletableLine.current.userData.linePoints;
const connectedpoints = [linePoints[0][1], linePoints[1][1]];
//REST
// deleteLineApi(
// organization,
// [
// { "uuid": linePoints[0][1] },
// { "uuid": linePoints[1][1] }
// ]
// )
//SOCKET
const data = {
organization,
line: [{ uuid: linePoints[0][1] }, { uuid: linePoints[1][1] }],
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:delete", data);
onlyFloorlines.current = onlyFloorlines.current
.map((floorline) =>
floorline.filter(
(line) =>
line[0][1] !== connectedpoints[0] && line[1][1] !== connectedpoints[1]
)
)
.filter((floorline) => floorline.length > 0);
lines.current = lines.current.filter((item) => item !== linePoints);
(<any>hoveredDeletableLine.current.material).dispose();
(<any>hoveredDeletableLine.current.geometry).dispose();
floorPlanGroupLine.current.remove(hoveredDeletableLine.current);
setDeletedLines([linePoints]);
connectedpoints.forEach((pointUUID) => {
let isConnected = false;
floorPlanGroupLine.current.children.forEach((line) => {
const linePoints = line.userData.linePoints;
const uuid1 = linePoints[0][1];
const uuid2 = linePoints[1][1];
if (uuid1 === pointUUID || uuid2 === pointUUID) {
isConnected = true;
}
});
if (!isConnected) {
floorPlanGroupPoint.current.children.forEach((point: any) => {
if (point.uuid === pointUUID) {
(<any>point.material).dispose();
(<any>point.geometry).dispose();
floorPlanGroupPoint.current.remove(point);
}
});
}
});
echo.success("Line Removed!");
}
export default deleteLine;

View File

@@ -1,226 +0,0 @@
import { useEffect, useState } from "react";
import { getLines } from "../../../../../services/factoryBuilder/lines/getLinesApi";
import * as THREE from "three";
import {
useActiveLayer,
useDeletedLines,
useNewLines,
useRoomsState,
useToggleView,
} from "../../../../../store/builder/store";
import objectLinesToArray from "../lineConvertions/objectLinesToArray";
import { Html } from "@react-three/drei";
import { Vector2 } from "three";
import * as Types from "../../../../../types/world/worldTypes";
import getRoomsFromLines from "../getRoomsFromLines";
import * as turf from '@turf/turf';
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../../functions/getUserData";
import { useVersionContext } from "../../../version/versionContext";
const DistanceText = () => {
const [lines, setLines] = useState<
{
distance: string;
position: THREE.Vector3;
userData: Types.Line;
layer: string;
}[]
>([]);
const { activeLayer } = useActiveLayer();
const { toggleView } = useToggleView();
const { newLines, setNewLines } = useNewLines();
const { deletedLines, setDeletedLines } = useDeletedLines();
const [linesState, setLinesState] = useState<Types.Lines>([]);
const { roomsState, setRoomsState } = useRoomsState();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { organization, email } = getUserData();
useEffect(() => {
if (linesState.length === 0) return;
const getLines = async () => {
const points3D = linesState.map(line => {
const startPoint = line[0][0]; // First point of each wall line
return [startPoint.x, 0, startPoint.z];
});
// Ensure the polygon is closed
if (
points3D[0][0] !== points3D[points3D.length - 1][0] ||
points3D[0][1] !== points3D[points3D.length - 1][1]
) {
points3D.push(points3D[0]);
}
// Convert to 2D for turf (x, z)
const coords2D = points3D.map(p => [p[0], p[1]]);
const projected = points3D.map((p: any) => new Vector2(p[0], p[1]));
// Shoelace formula for 2D polygon
let area = 0;
const n = projected.length;
for (let i = 0; i < n - 1; i++) {
const curr = projected[i];
const next = projected[i + 1];
area += curr.x * next.y - next.x * curr.y;
}
// return Math.abs(area) / 2;
// Build polygon and compute area
// const polygon = turf.polygon([coords2D]);
// const area = turf.area(polygon);
// const area = computeAreaFrom3DPoints(coords2D)
//
if (lines.length > 2) {
const linesByLayer = linesState.reduce((acc: { [key: number]: any[] }, pair) => {
const layer = pair[0][2];
if (!acc[layer]) acc[layer] = [];
acc[layer].push(pair);
return acc;
}, {});
for (const layer in linesByLayer) {
const rooms: Types.Rooms = await getRoomsFromLines({ current: linesByLayer[layer] });
setRoomsState(rooms)
}
}
}
getLines();
}, [linesState, roomsState])
useEffect(() => {
if (!email || !selectedVersion) return;
getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
data = objectLinesToArray(data);
setLinesState(data);
const lines = data
.filter((line: Types.Line) => line[0][2] === activeLayer)
.map((line: Types.Line) => {
const point1 = new THREE.Vector3(
line[0][0].x,
line[0][0].y,
line[0][0].z
);
const point2 = new THREE.Vector3(
line[1][0].x,
line[1][0].y,
line[1][0].z
);
const distance = point1.distanceTo(point2);
const midpoint = new THREE.Vector3()
.addVectors(point1, point2)
.divideScalar(2);
return {
distance: distance.toFixed(1),
position: midpoint,
userData: line,
layer: activeLayer,
};
});
setLines(lines);
});
}, [activeLayer, selectedVersion?.versionId]);
useEffect(() => {
if (newLines.length > 0) {
if (newLines[0][0][2] !== activeLayer) return;
const newLinesData = newLines.map((line: Types.Line) => {
const point1 = new THREE.Vector3(
line[0][0].x,
line[0][0].y,
line[0][0].z
);
const point2 = new THREE.Vector3(
line[1][0].x,
line[1][0].y,
line[1][0].z
);
const distance = point1.distanceTo(point2);
const midpoint = new THREE.Vector3()
.addVectors(point1, point2)
.divideScalar(2);
return {
distance: distance.toFixed(1),
position: midpoint,
userData: line,
layer: activeLayer,
};
});
setLines((prevLines) => [...prevLines, ...newLinesData]);
setLinesState((prevLines) => [...prevLines, ...newLines]);
setNewLines([]);
}
}, [newLines, activeLayer]);
useEffect(() => {
if ((deletedLines as Types.Lines).length > 0) {
setLines((prevLines) =>
prevLines.filter(
(line) =>
!deletedLines.some(
(deletedLine: any) =>
deletedLine[0][1] === line.userData[0][1] &&
deletedLine[1][1] === line.userData[1][1]
)
)
);
setLinesState(prev =>
prev.filter(line =>
!(deletedLines as Types.Lines).some(
deleted =>
line[0][1] === deleted[0][1] && line[1][1] === deleted[1][1]
)
)
);
setDeletedLines([]);
setRoomsState([])
}
}, [deletedLines]);
return (
<>
{toggleView && (
<group name="Distance_Text">
{lines.map((text) => (
<Html
// data
key={`${text.userData[0][1]}_${text.userData[1][1]}`}
userData={text.userData}
position={[text.position.x, 1, text.position.z]}
// class
wrapperClass="distance-text-wrapper"
className="distance-text"
// other
zIndexRange={[1, 0]}
prepend
sprite
>
<div
key={`${text.userData[0][1]}_${text.userData[1][1]}`}
className={`distance line-${text.userData[0][1]}_${text.userData[1][1]}_${text.layer}`}
>
{text.distance} m
</div>
</Html>
))}
</group>
)}
</>
);
};
export default DistanceText;

View File

@@ -1,71 +0,0 @@
import * as THREE from "three";
import { Html } from "@react-three/drei";
import { useState, useEffect } from "react";
import { useActiveLayer } from "../../../../../store/builder/store";
const ReferenceDistanceText = ({ line }: { line: any }) => {
interface TextState {
distance: string;
position: THREE.Vector3;
userData: any;
layer: any;
}
const [text, setTexts] = useState<TextState | null>(null);
const { activeLayer } = useActiveLayer();
useEffect(() => {
if (line) {
if (line.parent === null) {
setTexts(null);
return;
}
const distance = line.userData.linePoints.cursorPosition.distanceTo(
line.userData.linePoints.startPoint
);
const midpoint = new THREE.Vector3()
.addVectors(
line.userData.linePoints.cursorPosition,
line.userData.linePoints.startPoint
)
.divideScalar(2);
const newTexts = {
distance: distance.toFixed(1),
position: midpoint,
userData: line,
layer: activeLayer,
};
setTexts(newTexts);
}
});
return (
<group name="Reference_Distance_Text">
<mesh>
{text !== null && (
<Html
// data
key={text.distance}
userData={text.userData}
position={[text.position.x, 1, text.position.z]}
// class
wrapperClass="distance-text-wrapper"
className="distance-text"
// other
zIndexRange={[1, 0]}
prepend
sprite
>
<div
className={`Reference_Distance line-${text.userData.userData}`}
>
{text.distance} m
</div>
</Html>
)}
</mesh>
</group>
);
};
export default ReferenceDistanceText;

View File

@@ -1,218 +0,0 @@
import * as THREE from "three";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import addPointToScene from "../points/addPointToScene";
import addLineToScene from "./addLineToScene";
import splitLine from "./splitLine";
import removeReferenceLine from "./removeReferenceLine";
import getClosestIntersection from "./getClosestIntersection";
import * as Types from "../../../../types/world/worldTypes";
import arrayLineToObject from "./lineConvertions/arrayLineToObject";
// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi';
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
async function drawWall(
raycaster: THREE.Raycaster,
plane: Types.RefMesh,
floorPlanGroupPoint: Types.RefGroup,
snappedPoint: Types.RefVector3,
isSnapped: Types.RefBoolean,
isSnappedUUID: Types.RefString,
line: Types.RefLine,
ispreSnapped: Types.RefBoolean,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl,
setNewLines: any,
setDeletedLines: any,
activeLayer: Types.Number,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): Promise<void> {
const { userId, organization } = getUserData();
////////// Creating lines Based on the positions clicked //////////
////////// Allows the user lines that represents walls and roof, floor if forms a polygon //////////
if (!plane.current) return;
let intersects = raycaster.intersectObject(plane.current, true);
let intersectsLines = raycaster.intersectObjects(floorPlanGroupLine.current.children, true);
let intersectsPoint = raycaster.intersectObjects(floorPlanGroupPoint.current.children, true);
const VisibleintersectsPoint = intersectsPoint.find((intersect) => intersect.object.visible);
const visibleIntersect = intersectsLines.find(
(intersect) =>
intersect.object.visible &&
intersect.object.name !== CONSTANTS.lineConfig.referenceName &&
intersect.object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.wallName
);
if ((intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) && intersectsLines.length > 0 && !isSnapped.current && !ispreSnapped.current) {
////////// Clicked on a preexisting Line //////////
if (visibleIntersect && intersects) {
let IntersectsPoint = new THREE.Vector3(intersects[0].point.x, 0.01, intersects[0].point.z);
if (isAngleSnapped.current && anglesnappedPoint.current) {
IntersectsPoint = anglesnappedPoint.current;
}
if (visibleIntersect.object instanceof THREE.Mesh) {
const ThroughPoint = visibleIntersect.object.geometry.parameters.path.getPoints(CONSTANTS.lineConfig.lineIntersectionPoints);
let intersectionPoint = getClosestIntersection(ThroughPoint, IntersectsPoint);
if (intersectionPoint) {
const newLines = splitLine(
visibleIntersect,
intersectionPoint,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
lines,
setDeletedLines,
floorPlanGroupLine,
socket,
CONSTANTS.pointConfig.wallOuterColor,
CONSTANTS.lineConfig.wallColor,
CONSTANTS.lineConfig.wallName,
projectId,
versionId
);
setNewLines([newLines[0], newLines[1]]);
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.wallName,
]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
const data = arrayLineToObject(line.current as Types.Line);
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:create", input);
setNewLines([newLines[0], newLines[1], line.current]);
lines.current.push(line.current as Types.Line);
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.wallColor,
line.current,
floorPlanGroupLine
);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
return;
}
}
}
}
if (intersects && intersects.length > 0) {
////////// Clicked on a emply place or a point //////////
let intersectionPoint = intersects[0].point;
if (isAngleSnapped.current && line.current.length > 0 && anglesnappedPoint.current) {
intersectionPoint = anglesnappedPoint.current;
}
if (isSnapped.current && line.current.length > 0 && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (ispreSnapped.current && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (!isSnapped.current && !ispreSnapped.current) {
addPointToScene(
intersectionPoint,
CONSTANTS.pointConfig.wallOuterColor,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
CONSTANTS.lineConfig.wallName
);
} else {
ispreSnapped.current = false;
isSnapped.current = false;
}
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.wallName,
]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
const data = arrayLineToObject(line.current as Types.Line);
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:create", input);
setNewLines([line.current]);
lines.current.push(line.current as Types.Line);
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.wallColor,
line.current,
floorPlanGroupLine
);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
if (isSnapped.current) {
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
}
}
export default drawWall;

View File

@@ -1,86 +0,0 @@
import * as THREE from 'three';
import * as turf from '@turf/turf';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
async function getRoomsFromLines(lines: Types.RefLines) {
const rooms: Types.Rooms = [];
if (lines.current.length > 2) {
const linesByLayer = lines.current.reduce((acc: { [key: number]: any[] }, pair) => {
const layer = pair[0][2];
if (!acc[layer]) acc[layer] = [];
acc[layer].push(pair);
return acc;
}, {});
////////// Use turf.polygonize to create polygons from the line points //////////
for (const layer in linesByLayer) {
let linesInLayer = linesByLayer[layer];
linesInLayer = linesInLayer.filter(line => line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName);
const result = linesInLayer.map((pair: [THREE.Vector3, string, number, string][]) =>
pair.map((point) => ({
position: [point[0].x, point[0].z],
uuid: point[1]
}))
);
const lineFeatures = result.map(line => turf.lineString(line.map(p => p.position)));
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
let union: any[] = [];
polygons.features.forEach((feature) => {
union.push(feature);
});
if (union.length > 1) {
const unionResult = turf.union(turf.featureCollection(union));
if (unionResult?.geometry.type === "MultiPolygon") {
unionResult?.geometry.coordinates.forEach((poly) => {
const Coordinates = poly[0].map(([x, z]) => {
const matchingPoint = result.flat().find(r =>
r.position[0].toFixed(10) === x.toFixed(10) &&
r.position[1].toFixed(10) === z.toFixed(10)
);
return {
position: new THREE.Vector3(x, 0, z),
uuid: matchingPoint ? matchingPoint.uuid : ''
};
});
rooms.push({ coordinates: Coordinates.reverse(), layer: parseInt(layer) });
});
} else if (unionResult?.geometry.type === "Polygon") {
const Coordinates = unionResult?.geometry.coordinates[0].map(([x, z]) => {
const matchingPoint = result.flat().find(r =>
r.position[0].toFixed(10) === x.toFixed(10) &&
r.position[1].toFixed(10) === z.toFixed(10)
);
return {
position: new THREE.Vector3(x, 0, z),
uuid: matchingPoint ? matchingPoint.uuid : ''
};
});
rooms.push({ coordinates: Coordinates.reverse(), layer: parseInt(layer) });
}
} else if (union.length === 1) {
const Coordinates = union[0].geometry.coordinates[0].map(([x, z]: [number, number]) => {
const matchingPoint = result.flat().find(r =>
r.position[0].toFixed(10) === x.toFixed(10) &&
r.position[1].toFixed(10) === z.toFixed(10)
);
return {
position: new THREE.Vector3(x, 0, z),
uuid: matchingPoint ? matchingPoint.uuid : ''
};
});
rooms.push({ coordinates: Coordinates, layer: parseInt(layer) });
}
}
}
return rooms;
}
export default getRoomsFromLines;

View File

@@ -1,24 +0,0 @@
import * as Types from "../../../../../types/world/worldTypes";
export default function arrayLineToObject(array: Types.Line) {
if (!Array.isArray(array)) {
return {};
}
// Extract common properties from the first point
const commonLayer = array[0][2];
const commonType = array[0][3];
// Map points into a structured format
const line = array.map(([position, uuid]) => ({
position,
uuid,
}));
// Create the final structured object
return {
layer: commonLayer,
type: commonType,
line,
};
}

View File

@@ -1,30 +0,0 @@
import * as Types from "../../../../../types/world/worldTypes";
export default function arrayLinesToObject(array: Array<Types.Line>) {
if (!Array.isArray(array)) {
return [];
}
return array.map((lineArray) => {
if (!Array.isArray(lineArray)) {
return null;
}
// Extract common properties from the first point
const commonLayer = lineArray[0][2];
const commonType = lineArray[0][3];
// Map points into a structured format
const line = lineArray.map(([position, uuid]) => ({
position,
uuid,
}));
// Create the final structured object
return {
layer: commonLayer,
type: commonType,
line,
};
}).filter((item) => item !== null); // Filter out invalid entries
}

View File

@@ -1,13 +0,0 @@
import * as THREE from 'three';
export default function objectLineToArray(structuredObject: any) {
if (!structuredObject || !structuredObject.line) {
return [];
}
// Destructure common properties
const { layer, type, line } = structuredObject;
// Map points back to the original array format
return line.map(({ position, uuid }: any) => [new THREE.Vector3(position.x, position.y, position.z), uuid, layer, type]);
}

View File

@@ -1,20 +0,0 @@
import * as THREE from 'three';
export default function objectLinesToArray(structuredObjects: any): any {
if (!Array.isArray(structuredObjects)) {
return [];
}
return structuredObjects.map((structuredObject) => {
if (!structuredObject || !structuredObject.line) {
return [];
}
const { layer, type, line } = structuredObject;
return line.map(({ position, uuid }: any) => {
const vector = new THREE.Vector3(position.x, position.y, position.z);
return [vector, uuid, layer, type];
});
});
}

View File

@@ -1,66 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function RemoveConnectedLines(
DeletedPointUUID: Types.String,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
setDeletedLines: any,
lines: Types.RefLines,
): void {
////////// Check if any and how many lines are connected to the deleted point //////////
const removableLines: THREE.Mesh[] = [];
const connectedpoints: string[] = [];
const removedLinePoints: [number, string, number][][] = []; // Array to hold linePoints of removed lines
floorPlanGroupLine.current.children.forEach((line) => {
const linePoints = line.userData.linePoints as [number, string, number][];
const uuid1 = linePoints[0][1];
const uuid2 = linePoints[1][1];
if (uuid1 === DeletedPointUUID || uuid2 === DeletedPointUUID) {
connectedpoints.push(uuid1 === DeletedPointUUID ? uuid2 : uuid1);
removableLines.push(line as THREE.Mesh);
removedLinePoints.push(linePoints);
}
});
if (removableLines.length > 0) {
removableLines.forEach((line) => {
lines.current = lines.current.filter(item => item !== line.userData.linePoints);
(<any>line.material).dispose();
(<any>line.geometry).dispose();
floorPlanGroupLine.current.remove(line);
});
}
setDeletedLines(removedLinePoints)
////////// Check and Remove point that are no longer connected to any lines //////////
connectedpoints.forEach((pointUUID) => {
let isConnected = false;
floorPlanGroupLine.current.children.forEach((line) => {
const linePoints = line.userData.linePoints as [number, string, number][];
const uuid1 = linePoints[0][1];
const uuid2 = linePoints[1][1];
if (uuid1 === pointUUID || uuid2 === pointUUID) {
isConnected = true;
}
});
if (!isConnected) {
floorPlanGroupPoint.current.children.forEach((point: any) => {
if (point.uuid === pointUUID) {
(<any>point.material).dispose();
(<any>point.geometry).dispose();
floorPlanGroupPoint.current.remove(point);
}
});
}
});
}
export default RemoveConnectedLines;

View File

@@ -1,22 +0,0 @@
import * as Types from "../../../../types/world/worldTypes";
function removeReferenceLine(
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
line: Types.RefLine
): void {
////////// Removes Dangling reference line if the draw mode is ended or any other case //////////
line.current = [];
if (ReferenceLineMesh.current) {
(<any>ReferenceLineMesh.current.material).dispose();
(<any>ReferenceLineMesh.current.geometry).dispose();
floorPlanGroup.current.remove(ReferenceLineMesh.current);
LineCreated.current = false;
ReferenceLineMesh.current = undefined;
}
}
export default removeReferenceLine;

View File

@@ -1,153 +0,0 @@
import * as THREE from "three";
import addLineToScene from "./addLineToScene";
import addPointToScene from "../points/addPointToScene";
import * as Types from "../../../../types/world/worldTypes";
import arrayLineToObject from "../lines/lineConvertions/arrayLineToObject";
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
// import { deleteLineApi } from '../../../../services/factoryBuilder/lines/deleteLineApi';
// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi';
function splitLine(
visibleIntersect: Types.IntersectionEvent,
intersectionPoint: Types.Vector3,
currentLayerPoint: Types.RefMeshArray,
floorPlanGroupPoint: Types.RefGroup,
dragPointControls: Types.RefDragControl,
isSnappedUUID: Types.RefString,
lines: Types.RefLines,
setDeletedLines: any,
floorPlanGroupLine: { current: THREE.Group },
socket: Socket<any>,
pointColor: Types.String,
lineColor: Types.String,
lineType: Types.String,
projectId?: string,
versionId?: string,
): [Types.Line, Types.Line] {
////////// Removing the clicked line and splitting it with the clicked position adding a new point and two new lines //////////
const { userId, organization, email } = getUserData();
(visibleIntersect.object as any).material.dispose();
(visibleIntersect.object as any).geometry.dispose();
floorPlanGroupLine.current.remove(visibleIntersect.object);
setDeletedLines([visibleIntersect.object.userData.linePoints]);
//REST
// deleteLineApi(
// organization,
// [
// { "uuid": visibleIntersect.object.userData.linePoints[0][1] },
// { "uuid": visibleIntersect.object.userData.linePoints[1][1] }
// ]
// )
//SOCKET
const data = {
organization,
line: [
{ uuid: visibleIntersect.object.userData.linePoints[0][1] },
{ uuid: visibleIntersect.object.userData.linePoints[1][1] },
],
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:delete", data);
const point = addPointToScene(
intersectionPoint,
pointColor,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
lineType
);
const oldLinePoints = visibleIntersect.object.userData.linePoints;
lines.current = lines.current.filter((item) => item !== oldLinePoints);
const clickedPoint: Types.Point = [
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
point.uuid,
oldLinePoints[0][2],
lineType,
];
const start = oldLinePoints[0];
const end = oldLinePoints[1];
const newLine1: Types.Line = [start, clickedPoint];
const newLine2: Types.Line = [clickedPoint, end];
const line1 = arrayLineToObject(newLine1);
const line2 = arrayLineToObject(newLine2);
//REST
// setLine(organization, line1.layer!, line1.line!, line1.type!);
//SOCKET
const input1 = {
organization,
layer: line1.layer,
line: line1.line,
type: line1.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
console.log('input1: ', input1);
socket.emit("v1:Line:create", input1);
//REST
// setLine(organization, line2.layer!, line2.line!, line2.type!);
//SOCKET
const input2 = {
organization,
layer: line2.layer,
line: line2.line,
type: line2.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
console.log('input2: ', input2);
socket.emit("v1:Line:create", input2);
lines.current.push(newLine1, newLine2);
addLineToScene(
newLine1[0][0],
newLine1[1][0],
lineColor,
newLine1,
floorPlanGroupLine
);
addLineToScene(
newLine2[0][0],
newLine2[1][0],
lineColor,
newLine2,
floorPlanGroupLine
);
return [newLine1, newLine2];
}
export default splitLine;

View File

@@ -1,42 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function updateDistanceText(
scene: THREE.Scene,
floorPlanGroupLine: Types.RefGroup,
affectedLines: Types.NumberArray
): void {
////////// Updating the Distance Texts of the lines that are affected during drag //////////
const DistanceGroup = scene.getObjectByName('Distance_Text') as THREE.Group;
affectedLines.forEach((lineIndex) => {
const mesh = floorPlanGroupLine.current.children[lineIndex] as THREE.Mesh;
const linePoints = mesh.userData.linePoints;
if (linePoints) {
const distance = linePoints[0][0].distanceTo(linePoints[1][0]).toFixed(1);
const position = new THREE.Vector3().addVectors(linePoints[0][0], linePoints[1][0]).divideScalar(2);
if (!DistanceGroup || !linePoints) {
return
}
DistanceGroup.children.forEach((text) => {
const textMesh = text as THREE.Mesh;
if (textMesh.userData[0][1] === linePoints[0][1] && textMesh.userData[1][1] === linePoints[1][1]) {
textMesh.position.set(position.x, 1, position.z);
const className = `distance line-${textMesh.userData[0][1]}_${textMesh.userData[1][1]}_${linePoints[0][2]}`;
const element = document.getElementsByClassName(className)[0] as HTMLElement;
if (element) {
element.innerHTML = `${distance} m`;
}
}
});
}
});
}
export default updateDistanceText;

View File

@@ -1,24 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from '../../../../types/world/worldConstants';
function updateLines(
floorPlanGroupLine: Types.RefGroup,
affectedLines: Types.NumberArray
): void {
////////// Updating the positions for the affected lines only based on the updated positions //////////
affectedLines.forEach((lineIndex) => {
const mesh = floorPlanGroupLine.current.children[lineIndex] as Types.Mesh;
const linePoints = mesh.userData.linePoints as Types.Line;
if (linePoints) {
const newPositions = linePoints.map(([pos]) => pos);
const newPath = new THREE.CatmullRomCurve3(newPositions);
mesh.geometry.dispose();
mesh.geometry = new THREE.TubeGeometry(newPath, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false);
}
});
}
export default updateLines;

View File

@@ -1,32 +0,0 @@
import * as Types from "../../../../types/world/worldTypes";
function updateLinesPositions(
DragedPoint: Types.Mesh | { uuid: string, position: Types.Vector3 },
lines: Types.RefLines
): Types.NumberArray {
////////// Updating the lines position based on the dragged point's position //////////
const objectUUID = DragedPoint.uuid;
const affectedLines: Types.NumberArray = [];
lines.current.forEach((line, index) => {
let lineUpdated = false;
line.forEach((point) => {
const [position, uuid] = point;
if (uuid === objectUUID) {
position.x = DragedPoint.position.x;
position.y = 0.01;
position.z = DragedPoint.position.z;
lineUpdated = true;
}
});
if (lineUpdated) {
affectedLines.push(index);
}
});
return affectedLines;
}
export default updateLinesPositions;

View File

@@ -1,18 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function vectorizeLinesCurrent(
lines: Types.Lines
): Types.Lines {
////////// Storing a vector3 array in localstorage makes the prototype functions go puff. This function brings back the prototype functions by creating it again //////////
return lines.map((line) => {
const p1: Types.Point = [new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), line[0][1], line[0][2], line[0][3],];
const p2: Types.Point = [new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), line[1][1], line[0][2], line[1][3],];
return [p1, p2];
});
}
export default vectorizeLinesCurrent;

View File

@@ -1,54 +0,0 @@
import * as THREE from 'three';
import updateReferencePolesheight from './updateReferencePolesheight';
import * as Types from "../../../../types/world/worldTypes";
function addAndUpdateReferencePillar(
raycaster: THREE.Raycaster,
floorGroup: Types.RefGroup,
referencePole: Types.RefMesh
): void {
////////// Find Pillars position and scale based on the pointer interaction //////////
let Roofs = raycaster.intersectObjects(floorGroup.current.children, true);
const intersected = Roofs.find(intersect => intersect.object.name.includes("Roof") || intersect.object.name.includes("Floor"));
if (intersected) {
const intersectionPoint = intersected.point;
raycaster.ray.origin.copy(intersectionPoint);
raycaster.ray.direction.set(0, -1, 0);
const belowIntersections = raycaster.intersectObjects(floorGroup.current.children, true);
const validIntersections = belowIntersections.filter(intersect => intersect.object.name.includes("Floor"));
let distance: Types.Number;
if (validIntersections.length > 1) {
let valid = validIntersections.find(intersectedBelow => intersected.point.distanceTo(intersectedBelow.point) > 3);
if (valid) {
updateReferencePolesheight(intersectionPoint, valid.distance, referencePole, floorGroup);
} else {
const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z);
distance = intersected.point.distanceTo(belowPoint);
if (distance > 3) {
updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup);
}
}
} else {
const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z);
distance = intersected.point.distanceTo(belowPoint);
if (distance > 3) {
updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup);
}
}
} else {
if (referencePole.current) {
(<any>referencePole.current.material).dispose();
(<any>referencePole.current.geometry).dispose();
floorGroup.current.remove(referencePole.current);
referencePole.current = null;
}
}
}
export default addAndUpdateReferencePillar;

View File

@@ -1,24 +0,0 @@
import * as THREE from 'three';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
function addPillar(
referencePole: Types.RefMesh,
floorGroup: Types.RefGroup
): void {
////////// Add Pillars to the scene based on the reference. current poles position and scale //////////
if (referencePole.current) {
let pole: THREE.Mesh;
const geometry = referencePole.current.userData.geometry.clone();
const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.columnConfig.defaultColor });
pole = new THREE.Mesh(geometry, material);
pole.rotateX(Math.PI / 2);
pole.name = "Pole";
pole.position.set(referencePole.current.userData.position.x, referencePole.current.userData.position.y, referencePole.current.userData.position.z);
floorGroup.current.add(pole);
}
}
export default addPillar;

View File

@@ -1,34 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function DeletableHoveredPillar(
state: Types.ThreeState,
floorGroup: Types.RefGroup,
hoveredDeletablePillar: Types.RefMesh
): void {
////////// Altering the color of the hovered Pillar during the Deletion time //////////
const intersects = state.raycaster.intersectObjects(floorGroup.current.children, true);
const poleIntersect = intersects.find(intersect => intersect.object.name === "Pole");
if (poleIntersect) {
if (poleIntersect.object.name !== "Pole") {
return;
}
if (hoveredDeletablePillar.current) {
(hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black");
hoveredDeletablePillar.current = undefined;
}
hoveredDeletablePillar.current = poleIntersect.object as THREE.Mesh; // Type assertion
(hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("red");
} else {
if (hoveredDeletablePillar.current) {
(hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black");
hoveredDeletablePillar.current = undefined;
}
}
}
export default DeletableHoveredPillar;

View File

@@ -1,21 +0,0 @@
import { toast } from 'react-toastify';
import * as Types from "../../../../types/world/worldTypes";
function DeletePillar(
hoveredDeletablePillar: Types.RefMesh,
floorGroup: Types.RefGroup
): void {
////////// Deleting the hovered Pillar from the itemsGroup //////////
if (hoveredDeletablePillar.current) {
(<any>hoveredDeletablePillar.current.material).dispose();
(<any>hoveredDeletablePillar.current.geometry).dispose();
floorGroup.current.remove(hoveredDeletablePillar.current);
echo.success("Pillar Removed!");
hoveredDeletablePillar.current = undefined;
}
}
export default DeletePillar;

View File

@@ -1,40 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function updateReferencePolesheight(
intersectionPoint: Types.Vector3,
distance: Types.Number,
referencePole: Types.RefMesh,
floorGroup: Types.RefGroup
): void {
////////// Add a Reference Pillar and update its position and scale based on the pointer interaction //////////
if (referencePole.current) {
(<any>referencePole.current.material).dispose();
(<any>referencePole.current.geometry).dispose();
floorGroup.current.remove(referencePole.current);
referencePole.current.geometry.dispose();
}
const shape = new THREE.Shape();
shape.moveTo(0.5, 0);
shape.absarc(0, 0, 0.5, 0, 2 * Math.PI, false);
const extrudeSettings = {
depth: distance,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshBasicMaterial({ color: "green", transparent: true, opacity: 0.5 });
referencePole.current = new THREE.Mesh(geometry, material);
referencePole.current.rotateX(Math.PI / 2);
referencePole.current.position.set(intersectionPoint.x, intersectionPoint.y - 0.01, intersectionPoint.z);
referencePole.current.userData = { geometry: geometry, distance: distance, position: { x: intersectionPoint.x, y: intersectionPoint.y - 0.01, z: intersectionPoint.z } };
floorGroup.current.add(referencePole.current);
}
export default updateReferencePolesheight;

View File

@@ -1,65 +0,0 @@
import * as THREE from 'three';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
function addPointToScene(
position: Types.Vector3,
colour: Types.Color,
currentLayerPoint: Types.RefMeshArray,
floorPlanGroupPoint: Types.RefGroup,
dragPointControls: Types.RefDragControl | undefined,
uuid: Types.RefString | undefined,
Type: Types.String
): Types.Mesh {
////////// A function that creates and adds a cube (point) with an outline based on the position and colour given as params, It also updates the drag controls objects and sets the box uuid in uuid.current //////////
const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale);
const material = new THREE.ShaderMaterial({
uniforms: {
uOuterColor: { value: new THREE.Color(colour) }, // Blue color for the border
uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec2 vUv;
uniform vec3 uOuterColor;
uniform vec3 uInnerColor;
void main() {
// Define the size of the white square as a proportion of the face
float borderThickness = 0.2; // Adjust this value for border thickness
if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness &&
vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) {
gl_FragColor = vec4(uInnerColor, 1.0); // White inner square
} else {
gl_FragColor = vec4(uOuterColor, 1.0); // Blue border
}
}
`,
});
const point = new THREE.Mesh(geometry, material);
point.name = "point";
point.userData = { type: Type, color: colour };
point.position.set(position.x, 0.01, position.z);
currentLayerPoint.current.push(point);
floorPlanGroupPoint.current.add(point);
if (uuid) {
uuid.current = point.uuid;
}
if (dragPointControls) {
dragPointControls.current!.objects = currentLayerPoint.current;
}
return point;
}
export default addPointToScene;

View File

@@ -1,72 +0,0 @@
import * as Types from "../../../../types/world/worldTypes";
import { toast } from "react-toastify";
import RemoveConnectedLines from "../lines/removeConnectedLines";
// import { deletePointApi } from "../../../../services/factoryBuilder/lines/deletePointApi";
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
function deletePoint(
hoveredDeletablePoint: Types.RefMesh,
onlyFloorlines: Types.RefOnlyFloorLines,
floorPlanGroupPoint: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines,
setDeletedLines: any,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): void {
////////// Deleting a Point and the lines that are connected to it //////////
if (!hoveredDeletablePoint.current) {
return;
}
const { userId, organization, email } = getUserData();
(<any>hoveredDeletablePoint.current.material).dispose();
(<any>hoveredDeletablePoint.current.geometry).dispose();
floorPlanGroupPoint.current.remove(hoveredDeletablePoint.current);
const DeletedPointUUID = hoveredDeletablePoint.current.uuid;
//REST
// deletePointApi(organization, DeletedPointUUID);
//SOCKET
const data = {
organization,
uuid: DeletedPointUUID,
socketId: socket.id,
versionId,
projectId,
userId,
};
// console.log('data: ', data);
socket.emit("v1:Line:delete:point", data);
////////// Update onlyFloorlines.current to remove references to the deleted point //////////
onlyFloorlines.current = onlyFloorlines.current
.map((floorline) =>
floorline.filter(
(line) =>
line[0][1] !== DeletedPointUUID && line[1][1] !== DeletedPointUUID
)
)
.filter((floorline) => floorline.length > 0);
RemoveConnectedLines(
DeletedPointUUID,
floorPlanGroupLine,
floorPlanGroupPoint,
setDeletedLines,
lines
);
echo.success("Point Removed!");
}
export default deletePoint;

View File

@@ -1,44 +0,0 @@
import * as THREE from "three";
import * as Types from "../../../../types/world/worldTypes"
import * as CONSTANTS from '../../../../types/world/worldConstants';
import updateLinesPositions from "../lines/updateLinesPositions";
import updateLines from "../lines/updateLines";
import updateDistanceText from "../lines/updateDistanceText";
import updateFloorLines from "../floors/updateFloorLines";
function DragPoint(
event: Types.IntersectionEvent,
floorPlanGroupPoint: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
scene: THREE.Scene,
lines: Types.RefLines,
onlyFloorlines: Types.RefOnlyFloorLines
): void {
////////// Calling the line updation of the affected lines and Snapping of the point during the drag //////////
const snapThreshold = CONSTANTS.pointConfig.snappingThreshold;
const DragedPoint = event.object as Types.Mesh;
floorPlanGroupPoint.current.children.forEach((point) => {
let canSnap =
((DragedPoint.userData.type === CONSTANTS.lineConfig.wallName) && (point.userData.type === CONSTANTS.lineConfig.wallName || point.userData.type === CONSTANTS.lineConfig.floorName)) ||
((DragedPoint.userData.type === CONSTANTS.lineConfig.floorName) && (point.userData.type === CONSTANTS.lineConfig.wallName || point.userData.type === CONSTANTS.lineConfig.floorName)) ||
((DragedPoint.userData.type === CONSTANTS.lineConfig.aisleName) && point.userData.type === CONSTANTS.lineConfig.aisleName);
if (canSnap && point.uuid !== DragedPoint.uuid && point.visible) {
const distance = DragedPoint.position.distanceTo(point.position);
if (distance < snapThreshold) {
DragedPoint.position.copy(point.position);
}
}
});
const affectedLines = updateLinesPositions(DragedPoint, lines);
updateLines(floorPlanGroupLine, affectedLines);
updateDistanceText(scene, floorPlanGroupLine, affectedLines);
updateFloorLines(onlyFloorlines, DragedPoint);
}
export default DragPoint;

View File

@@ -1,37 +0,0 @@
import * as Types from "../../../../types/world/worldTypes";
function removeSoloPoint(
line: Types.RefLine,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup
): void {
////////// Remove the point if there is only one point and if it is not connected to any other line and also the reference line //////////
if (line.current[0]) {
const pointUUID = line.current[0][1];
let isConnected = false;
floorPlanGroupLine.current.children.forEach((line) => {
const linePoints = line.userData.linePoints;
const uuid1 = linePoints[0][1];
const uuid2 = linePoints[1][1];
if (uuid1 === pointUUID || uuid2 === pointUUID) {
isConnected = true;
}
});
if (!isConnected) {
floorPlanGroupPoint.current.children.forEach((point: any) => {
if (point.uuid === pointUUID) {
(<any>point.material).dispose();
(<any>point.geometry).dispose();
floorPlanGroupPoint.current.remove(point);
}
});
}
line.current = [];
}
}
export default removeSoloPoint;

View File

@@ -1,32 +0,0 @@
import * as THREE from 'three';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
function addRoofToScene(
shape: Types.Shape,
floor: Types.Number,
userData: Types.UserData,
floorGroup: Types.RefGroup
): void {
////////// Creating a Polygon roof from the shape of the Polygon floor //////////
const extrudeSettings: THREE.ExtrudeGeometryOptions = {
depth: CONSTANTS.roofConfig.height,
bevelEnabled: false
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.roofConfig.defaultColor, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = CONSTANTS.wallConfig.height + floor;
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.rotateX(Math.PI / 2);
mesh.userData.uuids = userData;
mesh.name = `Roof_Layer_${(floor / CONSTANTS.wallConfig.height) + 1}`;
floorGroup.current.add(mesh);
}
export default addRoofToScene;

View File

@@ -1,47 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function hideRoof(
visibility: Types.Boolean,
floorGroup: Types.RefGroup,
camera: THREE.Camera
): void {
////////// Toggles the visibility of the roof based on the camera position and the Roof visibility button on UI //////////
const v = new THREE.Vector3();
const u = new THREE.Vector3();
if (visibility === true && floorGroup.current) {
for (const child of floorGroup.current.children) {
if (child.name.includes("Roof")) {
const roofChild = child as Types.Mesh;
roofChild.getWorldDirection(v);
camera?.getWorldDirection(u);
if (roofChild.material) {
const materials = Array.isArray(roofChild.material) ? roofChild.material : [roofChild.material];
materials.forEach(material => {
material.visible = v.dot(u) < 0.25;
});
}
}
}
} else {
if (floorGroup.current) {
for (const child of floorGroup.current.children) {
if (child.name.includes("Roof")) {
const roofChild = child as Types.Mesh;
if (roofChild.material) {
const materials = Array.isArray(roofChild.material) ? roofChild.material : [roofChild.material];
materials.forEach(material => {
material.visible = false;
});
}
}
}
}
}
}
export default hideRoof;

View File

@@ -1,164 +0,0 @@
import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { toast } from "react-toastify";
import * as THREE from "three";
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import { Socket } from "socket.io-client";
import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { getUserData } from "../../../../functions/getUserData";
async function AddWallItems(
selected: any,
raycaster: THREE.Raycaster,
CSGGroup: Types.RefMesh,
setWallItems: Types.setWallItemSetState,
socket: Socket<any>,
projectId?: string,
versionId?: string
): Promise<void> {
const { userId, organization } = getUserData();
let intersects = raycaster?.intersectObject(CSGGroup.current!, true);
const wallRaycastIntersection = intersects?.find((child) =>
child.object.name.includes("WallRaycastReference")
);
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
if (!wallRaycastIntersection) return;
const intersectionPoint = wallRaycastIntersection;
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);
// Check THREE.js cache first
const cachedModel = THREE.Cache.get(selected.id);
if (cachedModel) {
handleModelLoad(cachedModel);
return;
}
// Check IndexedDB cache
const cachedModelBlob = await retrieveGLTF(selected.id);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(selected.id, gltf);
handleModelLoad(gltf);
});
return;
}
// Load from backend if not in any cache
loader.load(
`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`,
async (gltf) => {
try {
const modelBlob = await fetch(
`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`
).then((res) => res.blob());
await storeGLTF(selected.id, modelBlob);
THREE.Cache.add(selected.id, gltf);
await handleModelLoad(gltf);
} catch (error) {
console.error("Failed to cache model:", error);
handleModelLoad(gltf);
}
}
);
async function handleModelLoad(gltf: GLTF) {
const model = gltf.scene.clone();
model.userData = { wall: intersectionPoint.object.parent };
model.children[0].children.forEach((child) => {
if (child.name !== "CSG_REF") {
child.castShadow = true;
child.receiveShadow = true;
}
});
const boundingBox = new THREE.Box3().setFromObject(model);
const size = new THREE.Vector3();
boundingBox.getSize(size);
const csgscale = [size.x, size.y, size.z] as [number, number, number];
const center = new THREE.Vector3();
boundingBox.getCenter(center);
const csgposition = [center.x, center.y, center.z] as [
number,
number,
number
];
let positionY =
selected.subCategory === "fixed-move" ? 0 : intersectionPoint.point.y;
if (positionY === 0) {
positionY =
Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) *
CONSTANTS.wallConfig.height;
}
const newWallItem = {
type: selected.subCategory,
model: model,
modelName: selected.name,
assetId: selected.id,
scale: [1, 1, 1] as [number, number, number],
csgscale: csgscale,
csgposition: csgposition,
position: [
intersectionPoint.point.x,
positionY,
intersectionPoint.point.z,
] as [number, number, number],
quaternion:
intersectionPoint.object.quaternion.clone() as Types.QuaternionType,
};
const data = {
organization,
modelUuid: model.uuid,
modelName: newWallItem.modelName,
assetId: selected.id,
type: selected.subCategory,
csgposition: newWallItem.csgposition,
csgscale: newWallItem.csgscale,
position: newWallItem.position,
quaternion: newWallItem.quaternion,
scale: newWallItem.scale,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:wallItems:set", data);
setWallItems((prevItems) => {
const updatedItems = [...prevItems, newWallItem];
const WallItemsForStorage = updatedItems.map((item) => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
echo.success("Model Added!");
return updatedItems;
});
}
}
export default AddWallItems;

View File

@@ -1,62 +0,0 @@
import { getUserData } from "../../../../functions/getUserData";
import * as Types from "../../../../types/world/worldTypes";
// import { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi';
import { Socket } from "socket.io-client";
function DeleteWallItems(
hoveredDeletableWallItem: Types.RefMesh,
setWallItems: Types.setWallItemSetState,
wallItems: Types.wallItems,
socket: Socket<any>,
projectId?: string,
versionId? : string,
): void {
////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage //////////
const { userId, organization, email } = getUserData();
if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) {
setWallItems([]);
let WallItemsRef = wallItems;
const removedItem = WallItemsRef.find(
(item) => item.model?.uuid === hoveredDeletableWallItem.current?.uuid
);
const Items = WallItemsRef.filter(
(item) => item.model?.uuid !== hoveredDeletableWallItem.current?.uuid
);
setTimeout(async () => {
WallItemsRef = Items;
setWallItems(WallItemsRef);
//REST
// await deleteWallItem(organization, removedItem?.model?.uuid!, removedItem?.modelName!)
//SOCKET
const data = {
organization,
modelUuid: removedItem?.model?.uuid!,
modelName: removedItem?.modelName!,
socketId: socket.id,
projectId,
versionId,
userId,
};
socket.emit("v1:wallItems:delete", data);
const WallItemsForStorage = WallItemsRef.map((item) => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
hoveredDeletableWallItem.current = null;
}, 50);
}
}
export default DeleteWallItems;

View File

@@ -1,45 +0,0 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function hideWalls(
visibility: Types.Boolean,
scene: THREE.Scene,
camera: THREE.Camera
): void {
const wallNormal = new THREE.Vector3();
const cameraToWall = new THREE.Vector3();
const cameraDirection = new THREE.Vector3();
if (visibility === true) {
for (const children of scene.children) {
if (children.name === "Walls" && children.children[0]?.children.length > 0) {
children.children[0].children.forEach((child: any) => {
if (child.children[0]?.userData.WallType === "RoomWall") {
const wallMesh = child.children[0];
wallMesh.getWorldDirection(wallNormal);
cameraToWall.copy(wallMesh.position).sub(camera.position).normalize();
camera.getWorldDirection(cameraDirection);
const isFacingCamera = wallNormal.dot(cameraToWall) > 0;
const isInFrontOfCamera = cameraDirection.dot(cameraToWall) > -0.3;
if (wallMesh.material) {
wallMesh.material.visible = isFacingCamera && isInFrontOfCamera;
}
}
});
}
}
} else {
for (const children of scene.children) {
if (children.name === "Walls" && children.children[0]?.children.length > 0) {
children.children[0].children.forEach((child: any) => {
if (child.children[0]?.userData.WallType === "RoomWall" && child.children[0].material) {
child.children[0].material.visible = true;
}
});
}
}
}
}
export default hideWalls;

View File

@@ -1,140 +0,0 @@
import * as THREE from 'three';
import * as turf from '@turf/turf';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";
import getRoomsFromLines from '../lines/getRoomsFromLines';
async function loadWalls(
lines: Types.RefLines,
setWalls: any,
): Promise<void> {
////////// Removes the old walls if any, Checks if there is any overlapping in lines if any updates it , starts function that creates floor and roof //////////
const Walls: Types.Walls = [];
const Rooms: Types.Rooms = [];
localStorage.setItem("Lines", JSON.stringify(lines.current));
if (lines.current.length > 1) {
////////// Add Walls that are forming a room //////////
const wallSet = new Set<string>();
const rooms: Types.Rooms = await getRoomsFromLines(lines);
Rooms.push(...rooms);
Rooms.forEach(({ coordinates: room, layer }) => {
for (let i = 0; i < room.length - 1; i++) {
const uuid1 = room[i].uuid;
const uuid2 = room[(i + 1) % room.length].uuid;
const wallId = `${uuid1}_${uuid2}`;
if (!wallSet.has(wallId)) {
const p1 = room[i].position;
const p2 = room[(i + 1) % room.length].position;
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(0, CONSTANTS.wallConfig.height);
shape.lineTo(p2.distanceTo(p1), CONSTANTS.wallConfig.height);
shape.lineTo(p2.distanceTo(p1), 0);
shape.lineTo(0, 0);
const extrudeSettings = {
depth: CONSTANTS.wallConfig.width,
bevelEnabled: false
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const angle = Math.atan2(p2.z - p1.z, p2.x - p1.x);
Walls.push([geometry, [0, -angle, 0], [p1.x, (layer - 1) * CONSTANTS.wallConfig.height, p1.z], "RoomWall", layer]);
wallSet.add(wallId);
}
}
});
////////// Add Walls that are not forming any room //////////
lines.current.forEach(line => {
if (line[0][3] && line[1][3] !== CONSTANTS.lineConfig.wallName) {
return;
}
const [uuid1, uuid2] = line.map(point => point[1]);
let isInRoom = false;
const lineLayer = line[0][2];
for (let room of Rooms) {
const roomLayer = room.layer;
if (roomLayer !== lineLayer) continue;
for (let i = 0; i < room.coordinates.length - 1; i++) {
const roomUuid1 = room.coordinates[i].uuid;
const roomUuid2 = room.coordinates[(i + 1) % room.coordinates.length].uuid;
if (
(uuid1 === roomUuid1 && uuid2 === roomUuid2) ||
(uuid1 === roomUuid2 && uuid2 === roomUuid1)
) {
isInRoom = true;
break;
}
}
if (isInRoom) break;
}
if (!isInRoom) {
const p1 = new THREE.Vector3(line[0][0].x, 0, line[0][0].z);
const p2 = new THREE.Vector3(line[1][0].x, 0, line[1][0].z);
let isCollinear = false;
for (let room of Rooms) {
if (room.layer !== lineLayer) continue;
for (let i = 0; i < room.coordinates.length - 1; i++) {
const roomP1 = room.coordinates[i].position;
const roomP2 = room.coordinates[(i + 1) % room.coordinates.length].position;
const lineFeature = turf.lineString([[p1.x, p1.z], [p2.x, p2.z]]);
const roomFeature = turf.lineString([[roomP1.x, roomP1.z], [roomP2.x, roomP2.z]]);
if (turf.booleanOverlap(lineFeature, roomFeature)) {
isCollinear = true;
break;
}
}
if (isCollinear) break;
}
if (!isCollinear) {
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(0, CONSTANTS.wallConfig.height);
shape.lineTo(p2.distanceTo(p1), CONSTANTS.wallConfig.height);
shape.lineTo(p2.distanceTo(p1), 0);
shape.lineTo(0, 0);
const extrudeSettings = {
depth: CONSTANTS.wallConfig.width,
bevelEnabled: false
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const angle = Math.atan2(p2.z - p1.z, p2.x - p1.x);
Walls.push([geometry, [0, -angle, 0], [p1.x, (lineLayer - 1) * CONSTANTS.wallConfig.height, p1.z], "SegmentWall", lineLayer]);
}
}
});
setWalls(Walls);
} else {
setWalls([]);
}
}
export default loadWalls;
// A----- B----- C
// | | |
// | | |
// | | |
// F----- E----- D
// 1. A -> B, B -> C, C -> D, D -> E, E -> F, F -> A, B -> E
// 2. E -> F, F -> A, A -> B, B -> E, E -> D, D -> C, C -> B

View File

@@ -1,50 +0,0 @@
import * as THREE from 'three';
import * as Types from '../../../../types/world/worldTypes';
import * as CONSTANTS from '../../../../types/world/worldConstants';
const baseMaterial = new THREE.ShaderMaterial({
side: THREE.DoubleSide,
vertexShader: `
varying vec2 vUv;
void main(){
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vUv = uv;
}
`,
fragmentShader: `
varying vec2 vUv;
uniform vec3 uOuterColor;
void main(){
float alpha = 1.0 - vUv.y;
gl_FragColor = vec4(uOuterColor, alpha);
}
`,
uniforms: {
uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.defaultColor) },
},
transparent: true,
});
export default function addZonesToScene(
line: Types.Line,
floorGroupZone: Types.RefGroup,
color: THREE.Color
) {
const point1 = line[0][0];
const point2 = line[1][0];
const length = (new THREE.Vector3(point2.x, point2.y, point2.z)).distanceTo(new THREE.Vector3(point1.x, point1.y, point1.z));
const angle = Math.atan2(point2.z - point1.z, point2.x - point1.x);
const geometry = new THREE.PlaneGeometry(length, 10);
const material = baseMaterial.clone();
material.uniforms.uOuterColor.value.set(color.r, color.g, color.b);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set((point1.x + point2.x) / 2, ((line[0][2] - 1) * CONSTANTS.wallConfig.height) + 5, (point1.z + point2.z) / 2);
mesh.rotation.y = -angle;
floorGroupZone.current.add(mesh);
}

View File

@@ -1,19 +0,0 @@
import * as Types from '../../../../types/world/worldTypes';
import * as THREE from 'three';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import addZonesToScene from './addZonesToScene';
export default function loadZones(
lines: Types.RefLines,
floorGroupZone: Types.RefGroup
) {
if (!floorGroupZone.current) return
floorGroupZone.current.children = [];
const zones = lines.current.filter((line) => line[0][3] && line[1][3] === CONSTANTS.lineConfig.zoneName);
if (zones.length > 0) {
zones.forEach((zone: Types.Line) => {
addZonesToScene(zone, floorGroupZone, new THREE.Color(CONSTANTS.zoneConfig.color))
})
}
}

View File

@@ -1,127 +0,0 @@
import { useFrame, useThree } from "@react-three/fiber";
import {
useAddAction,
useRoofVisibility,
useToggleView,
useWallVisibility,
useUpdateScene,
useRenameModeStore,
useToolMode,
} from "../../../store/builder/store";
import hideRoof from "../geomentries/roofs/hideRoof";
import hideWalls from "../geomentries/walls/hideWalls";
import addAndUpdateReferencePillar from "../geomentries/pillars/addAndUpdateReferencePillar";
import { useEffect } from "react";
import addPillar from "../geomentries/pillars/addPillar";
import DeletePillar from "../geomentries/pillars/deletePillar";
import DeletableHoveredPillar from "../geomentries/pillars/deletableHoveredPillar";
import loadFloor from "../geomentries/floors/loadFloor";
import { useLeftData, useTopData } from "../../../store/visualization/useZone3DWidgetStore";
const FloorGroup = ({
floorGroup,
lines,
referencePole,
hoveredDeletablePillar,
}: any) => {
const state = useThree();
const { roofVisibility } = useRoofVisibility();
const { wallVisibility } = useWallVisibility();
const { toggleView } = useToggleView();
const { scene, camera, raycaster, gl } = useThree();
const { addAction } = useAddAction();
const { toolMode } = useToolMode();
const { updateScene, setUpdateScene } = useUpdateScene();
const { setTop } = useTopData();
const { setLeft } = useLeftData();
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
useEffect(() => {
if (updateScene) {
loadFloor(lines, floorGroup);
setUpdateScene(false);
}
}, [updateScene]);
useEffect(() => {
if (!addAction) {
if (referencePole.current) {
(referencePole.current as any).material.dispose();
(referencePole.current.geometry as any).dispose();
floorGroup.current.remove(referencePole.current);
referencePole.current = undefined;
}
}
}, [addAction]);
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
let isLeftMouseDown = false;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
}
};
const onMouseUp = (evt: any) => {
setIsRenameMode(false);
if (evt.button === 0) {
isLeftMouseDown = false;
if (!drag) {
if (addAction === "pillar") {
addPillar(referencePole, floorGroup);
}
if (toolMode === '3D-Delete') {
DeletePillar(hoveredDeletablePillar, floorGroup);
}
}
}
};
const onMouseMove = (evt: any) => {
if (!canvasElement) return;
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = evt.clientX - canvasRect.left;
const relativeY = evt.clientY - canvasRect.top;
// if (!isRenameMode) {
// setTop(relativeY);
// setLeft(relativeX);
// }
if (isLeftMouseDown) {
drag = true;
}
};
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
};
}, [toolMode, addAction, isRenameMode]);
useFrame(() => {
hideRoof(roofVisibility, floorGroup, camera);
hideWalls(wallVisibility, scene, camera);
if (addAction === "pillar") {
addAndUpdateReferencePillar(raycaster, floorGroup, referencePole);
}
if (toolMode === '3D-Delete') {
DeletableHoveredPillar(state, floorGroup, hoveredDeletablePillar);
}
});
return (
<group ref={floorGroup} visible={!toggleView} name="floorGroup"></group>
);
};
export default FloorGroup;

View File

@@ -1,191 +0,0 @@
import { useEffect } from "react";
import * as Types from '../../../types/world/worldTypes';
import { useActiveLayer, useDeletedLines, useToolMode, useNewLines, useRemovedLayer, useSocketStore, useToggleView, useUpdateScene } from "../../../store/builder/store";
import Layer2DVisibility from "../geomentries/layers/layer2DVisibility";
import { useFrame, useThree } from "@react-three/fiber";
import DeletableLineorPoint from "../functions/deletableLineOrPoint";
import removeSoloPoint from "../geomentries/points/removeSoloPoint";
import removeReferenceLine from "../geomentries/lines/removeReferenceLine";
import DeleteLayer from "../geomentries/layers/deleteLayer";
import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi";
import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
import loadInitialPoint from "../IntialLoad/loadInitialPoint";
import loadInitialLine from "../IntialLoad/loadInitialLine";
import deletePoint from "../geomentries/points/deletePoint";
import deleteLine from "../geomentries/lines/deleteLine";
import drawWall from "../geomentries/lines/drawWall";
import drawOnlyFloor from "../geomentries/floors/drawOnlyFloor";
import addDragControl from "../eventDeclaration/dragControlDeclaration";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, floorGroup, currentLayerPoint, dragPointControls, hoveredDeletablePoint, hoveredDeletableLine, plane, line, lines, onlyFloorline, onlyFloorlines, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
const state = useThree();
const { camera, gl, raycaster, controls } = state;
const { activeLayer } = useActiveLayer();
const { toggleView } = useToggleView();
const { toolMode } = useToolMode();
const { removedLayer, setRemovedLayer } = useRemovedLayer();
const { setUpdateScene } = useUpdateScene();
const { setNewLines } = useNewLines();
const { setDeletedLines } = useDeletedLines();
const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { organization } = getUserData();
useEffect(() => {
if (toolMode === 'move') {
addDragControl(dragPointControls, currentLayerPoint, state, floorPlanGroupPoint, floorPlanGroupLine, lines, onlyFloorlines, socket, projectId, selectedVersion?.versionId || '',);
}
return () => {
if (dragPointControls.current) {
dragPointControls.current.enabled = false;
}
};
}, [toolMode, state]);
useEffect(() => {
if (!selectedVersion) return;
getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
const Lines: Types.Lines = objectLinesToArray(data);
if (Lines) {
lines.current = Lines;
loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls);
loadInitialLine(floorPlanGroupLine, lines);
setUpdateScene(true);
}
})
}, [selectedVersion?.versionId]);
useEffect(() => {
if (!toggleView) {
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
}, [toggleView]);
useEffect(() => {
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}, [toolMode]);
useEffect(() => {
if (toolMode === 'move' && toggleView) {
if (dragPointControls.current) {
dragPointControls.current.enabled = true;
}
} else {
if (dragPointControls.current) {
dragPointControls.current.enabled = false;
}
}
}, [toolMode, toggleView, state]);
useEffect(() => {
Layer2DVisibility(activeLayer, floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, currentLayerPoint, dragPointControls);
}, [activeLayer]);
useEffect(() => {
if (removedLayer !== null) {
DeleteLayer(removedLayer, lines, floorPlanGroupLine, floorPlanGroupPoint, onlyFloorlines, floorGroup, setDeletedLines, setRemovedLayer, socket, projectId, selectedVersion?.versionId || '',);
}
}, [removedLayer]);
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
let isLeftMouseDown = false;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
}
};
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
}
if (controls) {
(controls as any).enabled = true;
}
}
const onMouseMove = () => {
if (isLeftMouseDown) {
drag = true;
}
};
const onContextMenu = (e: any) => {
e.preventDefault();
if (toolMode === "Wall" || toolMode === "Floor") {
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
};
const onMouseClick = (evt: any) => {
if (!plane.current || drag) return;
if (toolMode === "2D-Delete") {
if (hoveredDeletablePoint.current !== null) {
deletePoint(hoveredDeletablePoint, onlyFloorlines, floorPlanGroupPoint, floorPlanGroupLine, lines, setDeletedLines, socket, projectId, selectedVersion?.versionId || '',);
}
if (hoveredDeletableLine.current !== null) {
deleteLine(hoveredDeletableLine, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, socket, projectId, selectedVersion?.versionId || '',);
}
}
if (toolMode === "Wall") {
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',);
}
if (toolMode === "Floor") {
drawOnlyFloor(raycaster, state, camera, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, onlyFloorline, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',);
}
}
if (toolMode === "2D-Delete" || toolMode === "Wall" || toolMode === "Floor") {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("click", onMouseClick);
canvasElement.addEventListener("contextmenu", onContextMenu);
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContextMenu);
};
}, [toolMode, activeLayer])
useFrame(() => {
if (toolMode === '2D-Delete') {
DeletableLineorPoint(state, plane, floorPlanGroupLine, floorPlanGroupPoint, hoveredDeletableLine, hoveredDeletablePoint);
}
})
return (
<group ref={floorPlanGroup} visible={toggleView} name="floorPlanGroup">
<group ref={floorPlanGroupLine} name="floorPlanGroupLine"></group>
<group ref={floorPlanGroupPoint} name="floorPlanGroupPoint"></group>
</group>
)
}
export default FloorPlanGroup;

View File

@@ -1,291 +1,291 @@
import { useEffect } from "react";
import {
useObjectPosition,
useObjectRotation,
useSelectedWallItem,
useSocketStore,
useWallItems,
useSelectedItem,
useToolMode,
} from "../../../store/builder/store";
import { Csg } from "../csg/csg";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import handleMeshMissed from "../eventFunctions/handleMeshMissed";
import DeleteWallItems from "../geomentries/walls/deleteWallItems";
import loadInitialWallItems from "../IntialLoad/loadInitialWallItems";
import AddWallItems from "../geomentries/walls/addWallItems";
import useModuleStore from "../../../store/useModuleStore";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
// import { useEffect } from "react";
// import {
// useObjectPosition,
// useObjectRotation,
// useSelectedWallItem,
// useSocketStore,
// useWallItems,
// useSelectedItem,
// useToolMode,
// } from "../../../store/builder/store";
// import { Csg } from "../csg/csg";
// import * as Types from "../../../types/world/worldTypes";
// import * as CONSTANTS from "../../../types/world/worldConstants";
// import * as THREE from "three";
// import { useThree } from "@react-three/fiber";
// import handleMeshMissed from "../eventFunctions/handleMeshMissed";
// import DeleteWallItems from "../geomentries/walls/deleteWallItems";
// import loadInitialWallItems from "../IntialLoad/loadInitialWallItems";
// import AddWallItems from "../geomentries/walls/addWallItems";
// import useModuleStore from "../../../store/useModuleStore";
// import { useParams } from "react-router-dom";
// import { getUserData } from "../../../functions/getUserData";
// import { useVersionContext } from "../version/versionContext";
const WallItemsGroup = ({
currentWallItem,
hoveredDeletableWallItem,
selectedItemsIndex,
setSelectedItemsIndex,
CSGGroup,
}: any) => {
const state = useThree();
const { socket } = useSocketStore();
const { pointer, camera, raycaster } = state;
const { toolMode } = useToolMode();
const { wallItems, setWallItems } = useWallItems();
const { setObjectPosition } = useObjectPosition();
const { setObjectRotation } = useObjectRotation();
const { setSelectedWallItem } = useSelectedWallItem();
const { activeModule } = useModuleStore();
const { selectedItem } = useSelectedItem();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { userId, organization } = getUserData();
// const WallItemsGroup = ({
// currentWallItem,
// hoveredDeletableWallItem,
// selectedItemsIndex,
// setSelectedItemsIndex,
// CSGGroup,
// }: any) => {
// const state = useThree();
// const { socket } = useSocketStore();
// const { pointer, camera, raycaster } = state;
// const { toolMode } = useToolMode();
// const { wallItems, setWallItems } = useWallItems();
// const { setObjectPosition } = useObjectPosition();
// const { setObjectRotation } = useObjectRotation();
// const { setSelectedWallItem } = useSelectedWallItem();
// const { activeModule } = useModuleStore();
// const { selectedItem } = useSelectedItem();
// const { selectedVersionStore } = useVersionContext();
// const { selectedVersion } = selectedVersionStore();
// const { projectId } = useParams();
// const { userId, organization } = getUserData();
useEffect(() => {
// Load Wall Items from the backend
if (!projectId || !selectedVersion) return;
loadInitialWallItems(setWallItems, projectId, selectedVersion?.versionId);
}, [selectedVersion?.versionId]);
// useEffect(() => {
// // Load Wall Items from the backend
// if (!projectId || !selectedVersion) return;
// loadInitialWallItems(setWallItems, projectId, selectedVersion?.versionId);
// }, [selectedVersion?.versionId]);
////////// Update the Position value changes in the selected item //////////
// ////////// Update the Position value changes in the selected item //////////
useEffect(() => {
const canvasElement = state.gl.domElement;
function handlePointerMove(e: any) {
if (selectedItemsIndex !== null && toolMode === 'cursor' && e.buttons === 1) {
const Raycaster = state.raycaster;
const intersects = Raycaster.intersectObjects(CSGGroup.current?.children[0].children!, true);
const Object = intersects.find((child) => child.object.name.includes("WallRaycastReference"));
// useEffect(() => {
// const canvasElement = state.gl.domElement;
// function handlePointerMove(e: any) {
// if (selectedItemsIndex !== null && toolMode === 'cursor' && e.buttons === 1) {
// const Raycaster = state.raycaster;
// const intersects = Raycaster.intersectObjects(CSGGroup.current?.children[0].children!, true);
// const Object = intersects.find((child) => child.object.name.includes("WallRaycastReference"));
if (Object) {
(state.controls as any)!.enabled = false;
setWallItems((prevItems: any) => {
const updatedItems = [...prevItems];
let position: [number, number, number] = [0, 0, 0];
// if (Object) {
// (state.controls as any)!.enabled = false;
// setWallItems((prevItems: any) => {
// const updatedItems = [...prevItems];
// let position: [number, number, number] = [0, 0, 0];
if (updatedItems[selectedItemsIndex].type === "fixed-move") {
position = [
Object!.point.x,
Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) *
CONSTANTS.wallConfig.height,
Object!.point.z,
];
} else if (updatedItems[selectedItemsIndex].type === "free-move") {
position = [Object!.point.x, Object!.point.y, Object!.point.z];
}
// if (updatedItems[selectedItemsIndex].type === "fixed-move") {
// position = [
// Object!.point.x,
// Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) *
// CONSTANTS.wallConfig.height,
// Object!.point.z,
// ];
// } else if (updatedItems[selectedItemsIndex].type === "free-move") {
// position = [Object!.point.x, Object!.point.y, Object!.point.z];
// }
requestAnimationFrame(() => {
setObjectPosition(new THREE.Vector3(...position));
setObjectRotation({
x: THREE.MathUtils.radToDeg(Object!.object.rotation.x),
y: THREE.MathUtils.radToDeg(Object!.object.rotation.y),
z: THREE.MathUtils.radToDeg(Object!.object.rotation.z),
});
});
// requestAnimationFrame(() => {
// setObjectPosition(new THREE.Vector3(...position));
// setObjectRotation({
// x: THREE.MathUtils.radToDeg(Object!.object.rotation.x),
// y: THREE.MathUtils.radToDeg(Object!.object.rotation.y),
// z: THREE.MathUtils.radToDeg(Object!.object.rotation.z),
// });
// });
updatedItems[selectedItemsIndex] = {
...updatedItems[selectedItemsIndex],
position: position,
quaternion: Object!.object.quaternion.clone() as Types.QuaternionType,
};
// updatedItems[selectedItemsIndex] = {
// ...updatedItems[selectedItemsIndex],
// position: position,
// quaternion: Object!.object.quaternion.clone() as Types.QuaternionType,
// };
return updatedItems;
});
}
}
}
// return updatedItems;
// });
// }
// }
// }
async function handlePointerUp() {
const Raycaster = state.raycaster;
const intersects = Raycaster.intersectObjects(
CSGGroup.current?.children[0].children!,
true
);
const Object = intersects.find((child) =>
child.object.name.includes("WallRaycastReference")
);
if (Object) {
if (selectedItemsIndex !== null) {
let currentItem: any = null;
setWallItems((prevItems: any) => {
const updatedItems = [...prevItems];
const WallItemsForStorage = updatedItems.map((item) => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
// async function handlePointerUp() {
// const Raycaster = state.raycaster;
// const intersects = Raycaster.intersectObjects(
// CSGGroup.current?.children[0].children!,
// true
// );
// const Object = intersects.find((child) =>
// child.object.name.includes("WallRaycastReference")
// );
// if (Object) {
// if (selectedItemsIndex !== null) {
// let currentItem: any = null;
// setWallItems((prevItems: any) => {
// const updatedItems = [...prevItems];
// const WallItemsForStorage = updatedItems.map((item) => {
// const { model, ...rest } = item;
// return {
// ...rest,
// modelUuid: model?.uuid,
// };
// });
currentItem = updatedItems[selectedItemsIndex];
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
return updatedItems;
});
// currentItem = updatedItems[selectedItemsIndex];
// localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
// return updatedItems;
// });
setTimeout(async () => {
// setTimeout(async () => {
//REST
// //REST
// await setWallItem(
// organization,
// currentItem?.model?.uuid,
// currentItem.modelName,
// currentItem.assetId,
// currentItem.type!,
// currentItem.csgposition!,
// currentItem.csgscale!,
// currentItem.position,
// currentItem.quaternion,
// currentItem.scale!,
// )
// // await setWallItem(
// // organization,
// // currentItem?.model?.uuid,
// // currentItem.modelName,
// // currentItem.assetId,
// // currentItem.type!,
// // currentItem.csgposition!,
// // currentItem.csgscale!,
// // currentItem.position,
// // currentItem.quaternion,
// // currentItem.scale!,
// // )
//SOCKET
// //SOCKET
const data = {
organization,
modelUuid: currentItem.model?.uuid!,
assetId: currentItem.assetId,
modelName: currentItem.modelName!,
type: currentItem.type!,
csgposition: currentItem.csgposition!,
csgscale: currentItem.csgscale!,
position: currentItem.position!,
quaternion: currentItem.quaternion,
scale: currentItem.scale!,
socketId: socket.id,
versionId: selectedVersion?.versionId || '',
projectId,
userId
};
// const data = {
// organization,
// modelUuid: currentItem.model?.uuid!,
// assetId: currentItem.assetId,
// modelName: currentItem.modelName!,
// type: currentItem.type!,
// csgposition: currentItem.csgposition!,
// csgscale: currentItem.csgscale!,
// position: currentItem.position!,
// quaternion: currentItem.quaternion,
// scale: currentItem.scale!,
// socketId: socket.id,
// versionId: selectedVersion?.versionId || '',
// projectId,
// userId
// };
// console.log('data: ', data);
socket.emit("v1:wallItems:set", data);
}, 0);
(state.controls as any)!.enabled = true;
}
}
}
// // console.log('data: ', data);
// socket.emit("v1:wallItems:set", data);
// }, 0);
// (state.controls as any)!.enabled = true;
// }
// }
// }
canvasElement.addEventListener("pointermove", handlePointerMove);
canvasElement.addEventListener("pointerup", handlePointerUp);
// canvasElement.addEventListener("pointermove", handlePointerMove);
// canvasElement.addEventListener("pointerup", handlePointerUp);
return () => {
canvasElement.removeEventListener("pointermove", handlePointerMove);
canvasElement.removeEventListener("pointerup", handlePointerUp);
};
}, [selectedItemsIndex, selectedVersion?.versionId]);
// return () => {
// canvasElement.removeEventListener("pointermove", handlePointerMove);
// canvasElement.removeEventListener("pointerup", handlePointerUp);
// };
// }, [selectedItemsIndex, selectedVersion?.versionId]);
useEffect(() => {
const canvasElement = state.gl.domElement;
let drag = false;
let isLeftMouseDown = false;
// useEffect(() => {
// const canvasElement = state.gl.domElement;
// let drag = false;
// let isLeftMouseDown = false;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
}
};
// const onMouseDown = (evt: any) => {
// if (evt.button === 0) {
// isLeftMouseDown = true;
// drag = false;
// }
// };
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
if (!drag && toolMode === '3D-Delete' && activeModule === "builder") {
DeleteWallItems(
hoveredDeletableWallItem,
setWallItems,
wallItems,
socket,
projectId,
selectedVersion?.versionId || '',
);
}
}
};
// const onMouseUp = (evt: any) => {
// if (evt.button === 0) {
// isLeftMouseDown = false;
// if (!drag && toolMode === '3D-Delete' && activeModule === "builder") {
// DeleteWallItems(
// hoveredDeletableWallItem,
// setWallItems,
// wallItems,
// socket,
// projectId,
// selectedVersion?.versionId || '',
// );
// }
// }
// };
const onMouseMove = () => {
if (isLeftMouseDown) {
drag = true;
}
};
// const onMouseMove = () => {
// if (isLeftMouseDown) {
// drag = true;
// }
// };
const onDrop = (event: any) => {
if (selectedItem.category !== 'Fenestration') return;
// const onDrop = (event: any) => {
// if (selectedItem.category !== 'Fenestration') return;
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
// pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
// pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
// raycaster.setFromCamera(pointer, camera);
if (selectedItem.id && selectedVersion && projectId) {
if (selectedItem.subCategory) {
AddWallItems(
selectedItem,
raycaster,
CSGGroup,
setWallItems,
socket,
projectId,
selectedVersion?.versionId || '',
);
}
event.preventDefault();
}
};
// if (selectedItem.id && selectedVersion && projectId) {
// if (selectedItem.subCategory) {
// AddWallItems(
// selectedItem,
// raycaster,
// CSGGroup,
// setWallItems,
// socket,
// projectId,
// selectedVersion?.versionId || '',
// );
// }
// event.preventDefault();
// }
// };
const onDragOver = (event: any) => {
event.preventDefault();
};
// const onDragOver = (event: any) => {
// event.preventDefault();
// };
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);
// canvasElement.addEventListener("mousedown", onMouseDown);
// canvasElement.addEventListener("mouseup", onMouseUp);
// canvasElement.addEventListener("mousemove", onMouseMove);
// canvasElement.addEventListener("drop", onDrop);
// canvasElement.addEventListener("dragover", onDragOver);
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [toolMode, wallItems, selectedItem, camera, selectedVersion?.versionId]);
// return () => {
// canvasElement.removeEventListener("mousedown", onMouseDown);
// canvasElement.removeEventListener("mouseup", onMouseUp);
// canvasElement.removeEventListener("mousemove", onMouseMove);
// canvasElement.removeEventListener("drop", onDrop);
// canvasElement.removeEventListener("dragover", onDragOver);
// };
// }, [toolMode, wallItems, selectedItem, camera, selectedVersion?.versionId]);
useEffect(() => {
if (toolMode && activeModule === "builder") {
handleMeshMissed(
currentWallItem,
setSelectedWallItem,
setSelectedItemsIndex
);
setSelectedWallItem(null);
setSelectedItemsIndex(null);
}
}, [toolMode]);
// useEffect(() => {
// if (toolMode && activeModule === "builder") {
// handleMeshMissed(
// currentWallItem,
// setSelectedWallItem,
// setSelectedItemsIndex
// );
// setSelectedWallItem(null);
// setSelectedItemsIndex(null);
// }
// }, [toolMode]);
return (
<>
{wallItems.map((item: Types.WallItem, index: number) => (
<group
key={index}
position={item.position}
quaternion={item.quaternion}
scale={item.scale}
>
<Csg
position={item.csgposition!}
scale={item.csgscale!}
model={item.model!}
hoveredDeletableWallItem={hoveredDeletableWallItem}
/>
</group>
))}
</>
);
};
// return (
// <>
// {wallItems.map((item: Types.WallItem, index: number) => (
// <group
// key={index}
// position={item.position}
// quaternion={item.quaternion}
// scale={item.scale}
// >
// <Csg
// position={item.csgposition!}
// scale={item.csgscale!}
// model={item.model!}
// hoveredDeletableWallItem={hoveredDeletableWallItem}
// />
// </group>
// ))}
// </>
// );
// };
export default WallItemsGroup;
// export default WallItemsGroup;

View File

@@ -1,74 +1,74 @@
import { Geometry } from "@react-three/csg";
import {
useSelectedWallItem,
useToggleView,
useToolMode,
useWallItems,
useWalls,
} from "../../../store/builder/store";
import handleMeshDown from "../eventFunctions/handleMeshDown";
import handleMeshMissed from "../eventFunctions/handleMeshMissed";
import WallsMesh from "./wallsMesh";
import WallItemsGroup from "./wallItemsGroup";
// import { Geometry } from "@react-three/csg";
// import {
// useSelectedWallItem,
// useToggleView,
// useToolMode,
// useWallItems,
// useWalls,
// } from "../../../store/builder/store";
// import handleMeshDown from "../eventFunctions/handleMeshDown";
// import handleMeshMissed from "../eventFunctions/handleMeshMissed";
// import WallsMesh from "./wallsMesh";
// import WallItemsGroup from "./wallItemsGroup";
const WallsAndWallItems = ({
CSGGroup,
setSelectedItemsIndex,
selectedItemsIndex,
currentWallItem,
csg,
lines,
hoveredDeletableWallItem,
}: any) => {
const { walls } = useWalls();
const { wallItems } = useWallItems();
const { toggleView } = useToggleView();
const { toolMode } = useToolMode();
const { setSelectedWallItem } = useSelectedWallItem();
// const WallsAndWallItems = ({
// CSGGroup,
// setSelectedItemsIndex,
// selectedItemsIndex,
// currentWallItem,
// csg,
// lines,
// hoveredDeletableWallItem,
// }: any) => {
// const { walls } = useWalls();
// const { wallItems } = useWallItems();
// const { toggleView } = useToggleView();
// const { toolMode } = useToolMode();
// const { setSelectedWallItem } = useSelectedWallItem();
return (
<mesh
ref={CSGGroup as any}
name="Walls"
key={walls.length}
receiveShadow
visible={!toggleView}
onClick={(event) => {
if (toolMode === "cursor") {
handleMeshDown(
event,
currentWallItem,
setSelectedWallItem,
setSelectedItemsIndex,
wallItems,
toggleView
);
}
}}
onPointerMissed={() => {
if (toolMode === "cursor") {
handleMeshMissed(
currentWallItem,
setSelectedWallItem,
setSelectedItemsIndex
);
setSelectedWallItem(null);
setSelectedItemsIndex(null);
}
}}
>
<Geometry ref={csg as any} computeVertexNormals useGroups>
<WallsMesh lines={lines} />
<WallItemsGroup
currentWallItem={currentWallItem}
hoveredDeletableWallItem={hoveredDeletableWallItem}
selectedItemsIndex={selectedItemsIndex}
setSelectedItemsIndex={setSelectedItemsIndex}
CSGGroup={CSGGroup}
/>
</Geometry>
</mesh>
);
};
// return (
// <mesh
// ref={CSGGroup as any}
// name="Walls"
// key={walls.length}
// receiveShadow
// visible={!toggleView}
// onClick={(event) => {
// if (toolMode === "cursor") {
// handleMeshDown(
// event,
// currentWallItem,
// setSelectedWallItem,
// setSelectedItemsIndex,
// wallItems,
// toggleView
// );
// }
// }}
// onPointerMissed={() => {
// if (toolMode === "cursor") {
// handleMeshMissed(
// currentWallItem,
// setSelectedWallItem,
// setSelectedItemsIndex
// );
// setSelectedWallItem(null);
// setSelectedItemsIndex(null);
// }
// }}
// >
// <Geometry ref={csg as any} computeVertexNormals useGroups>
// <WallsMesh lines={lines} />
// <WallItemsGroup
// currentWallItem={currentWallItem}
// hoveredDeletableWallItem={hoveredDeletableWallItem}
// selectedItemsIndex={selectedItemsIndex}
// setSelectedItemsIndex={setSelectedItemsIndex}
// CSGGroup={CSGGroup}
// />
// </Geometry>
// </mesh>
// );
// };
export default WallsAndWallItems;
// export default WallsAndWallItems;

View File

@@ -1,82 +1,82 @@
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
import { Base } from "@react-three/csg";
import { MeshDiscardMaterial } from "@react-three/drei";
import { useUpdateScene, useWalls } from "../../../store/builder/store";
import React, { useEffect } from "react";
import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi";
import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
import loadWalls from "../geomentries/walls/loadWalls";
import texturePath from "../../../assets/textures/floor/wall-tex.png";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
// import * as THREE from "three";
// import * as Types from "../../../types/world/worldTypes";
// import * as CONSTANTS from "../../../types/world/worldConstants";
// import { Base } from "@react-three/csg";
// import { MeshDiscardMaterial } from "@react-three/drei";
// import { useUpdateScene, useWalls } from "../../../store/builder/store";
// import React, { useEffect } from "react";
// import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi";
// import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
// import loadWalls from "../geomentries/walls/loadWalls";
// import texturePath from "../../../assets/textures/floor/wall-tex.png";
// import { useParams } from "react-router-dom";
// import { getUserData } from "../../../functions/getUserData";
// import { useVersionContext } from "../version/versionContext";
const WallsMeshComponent = ({ lines }: any) => {
const { walls, setWalls } = useWalls();
const { updateScene, setUpdateScene } = useUpdateScene();
const { projectId } = useParams();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { organization } = getUserData();
// const WallsMeshComponent = ({ lines }: any) => {
// const { walls, setWalls } = useWalls();
// const { updateScene, setUpdateScene } = useUpdateScene();
// const { projectId } = useParams();
// const { selectedVersionStore } = useVersionContext();
// const { selectedVersion } = selectedVersionStore();
// const { organization } = getUserData();
useEffect(() => {
if (updateScene) {
if (!selectedVersion) {
setUpdateScene(false);
return;
};
getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
const Lines: Types.Lines = objectLinesToArray(data);
localStorage.setItem("Lines", JSON.stringify(Lines));
// useEffect(() => {
// if (updateScene) {
// if (!selectedVersion) {
// setUpdateScene(false);
// return;
// };
// getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
// const Lines: Types.Lines = objectLinesToArray(data);
// localStorage.setItem("Lines", JSON.stringify(Lines));
if (Lines) {
loadWalls(lines, setWalls);
}
});
setUpdateScene(false);
}
}, [updateScene, selectedVersion?.versionId]);
// if (Lines) {
// loadWalls(lines, setWalls);
// }
// });
// setUpdateScene(false);
// }
// }, [updateScene, selectedVersion?.versionId]);
const textureLoader = new THREE.TextureLoader();
const wallTexture = textureLoader.load(texturePath);
// const textureLoader = new THREE.TextureLoader();
// const wallTexture = textureLoader.load(texturePath);
wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping;
wallTexture.repeat.set(0.1, 0.1);
wallTexture.colorSpace = THREE.SRGBColorSpace;
// wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping;
// wallTexture.repeat.set(0.1, 0.1);
// wallTexture.colorSpace = THREE.SRGBColorSpace;
return (
<>
{walls.map((wall: Types.Wall, index: number) => (
<mesh key={index} renderOrder={1}>
<Base
name={`Wall${index + 1}`}
geometry={wall[0]}
rotation={wall[1]}
position={wall[2]}
userData={{ WallType: wall[3], Layer: wall[4] }}
>
<meshStandardMaterial
side={THREE.DoubleSide}
color={CONSTANTS.wallConfig.defaultColor}
map={wallTexture}
/>
</Base>
<mesh
castShadow
geometry={wall[0]}
rotation={wall[1]}
position={wall[2]}
name={`WallRaycastReference_${index + 1}`}
>
<MeshDiscardMaterial />
</mesh>
</mesh>
))}
</>
);
};
// return (
// <>
// {walls.map((wall: Types.Wall, index: number) => (
// <mesh key={index} renderOrder={1}>
// <Base
// name={`Wall${index + 1}`}
// geometry={wall[0]}
// rotation={wall[1]}
// position={wall[2]}
// userData={{ WallType: wall[3], Layer: wall[4] }}
// >
// <meshStandardMaterial
// side={THREE.DoubleSide}
// color={CONSTANTS.wallConfig.defaultColor}
// map={wallTexture}
// />
// </Base>
// <mesh
// castShadow
// geometry={wall[0]}
// rotation={wall[1]}
// position={wall[2]}
// name={`WallRaycastReference_${index + 1}`}
// >
// <MeshDiscardMaterial />
// </mesh>
// </mesh>
// ))}
// </>
// );
// };
const WallsMesh = React.memo(WallsMeshComponent);
export default WallsMesh;
// const WallsMesh = React.memo(WallsMeshComponent);
// export default WallsMesh;

View File

@@ -1,651 +0,0 @@
import React, { useState, useEffect, useMemo, useRef } from "react";
import { Html, Line, Sphere } from "@react-three/drei";
import { useThree, useFrame } from "@react-three/fiber";
import * as THREE from "three";
import {
useActiveLayer,
useSocketStore,
useToggleView,
useToolMode,
useRemovedLayer,
useZones,
useZonePoints,
} from "../../../store/builder/store";
import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi";
import * as CONSTANTS from "../../../types/world/worldConstants";
import * as turf from "@turf/turf";
import { computeArea } from "../functions/computeArea";
import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
const ZoneGroup: React.FC = () => {
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
const [startPoint, setStartPoint] = useState<THREE.Vector3 | null>(null);
const [endPoint, setEndPoint] = useState<THREE.Vector3 | null>(null);
const { zones, setZones } = useZones();
const { zonePoints, setZonePoints } = useZonePoints();
const [isDragging, setIsDragging] = useState(false);
const { selectedZone } = useSelectedZoneStore();
const [draggedSphere, setDraggedSphere] = useState<THREE.Vector3 | null>(null);
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { removedLayer, setRemovedLayer } = useRemovedLayer();
const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { userId, organization } = getUserData();
const groupsRef = useRef<any>();
const zoneMaterial = useMemo(
() =>
new THREE.ShaderMaterial({
side: THREE.DoubleSide,
vertexShader: `
varying vec2 vUv;
void main(){
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vUv = uv;
}
`,
fragmentShader: `
varying vec2 vUv;
uniform vec3 uOuterColor;
void main(){
float alpha = 1.0 - vUv.y;
gl_FragColor = vec4(uOuterColor, alpha);
}
`,
uniforms: {
uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) },
},
transparent: true,
depthWrite: false,
}),
[]
);
useEffect(() => {
if (!selectedVersion) return;
getZonesApi(organization, projectId, selectedVersion?.versionId || '').then((data) => {
if (data && data.length > 0) {
const fetchedZones = data.map((zone: any) => ({
zoneUuid: zone.zoneUuid,
zoneName: zone.zoneName,
points: zone.points,
viewPortCenter: zone.viewPortCenter,
viewPortposition: zone.viewPortposition,
layer: zone.layer,
}));
setZones(fetchedZones);
const fetchedPoints = data.flatMap((zone: any) =>
zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point))
);
setZonePoints(fetchedPoints);
} else {
setZones([]);
}
}).catch((err) => {
console.error(err);
})
}, [selectedVersion?.versionId]);
useEffect(() => {
localStorage.setItem("zones", JSON.stringify(zones));
}, [zones]);
useEffect(() => {
if (removedLayer) {
const updatedZones = zones.filter((zone: any) => zone.layer !== removedLayer);
setZones(updatedZones);
const updatedzonePoints = zonePoints.filter((_: any, index: any) => {
const zoneIndex = Math.floor(index / 4);
return zones[zoneIndex]?.layer !== removedLayer;
});
setZonePoints(updatedzonePoints);
zones.filter((zone: any) => zone.layer === removedLayer).forEach((zone: any) => { deleteZoneFromBackend(zone.zoneUuid); });
setRemovedLayer(null);
}
}, [removedLayer]);
useEffect(() => {
if (toolMode !== "Zone") {
setStartPoint(null);
setEndPoint(null);
}
if (!toggleView) {
setStartPoint(null);
setEndPoint(null);
}
}, [toolMode, toggleView]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const addZoneToBackend = async (zone: {
zoneUuid: string;
zoneName: string;
points: [number, number, number][];
layer: string;
}) => {
const calculateCenter = (points: number[][]) => {
if (!points || points.length === 0) return null;
let sumX = 0, sumY = 0, sumZ = 0;
const numPoints = points.length;
points.forEach(([x, y, z]) => {
sumX += x;
sumY += y;
sumZ += z;
});
return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [
number,
number,
number
];
};
const target: [number, number, number] | null = calculateCenter(zone.points);
if (!target || zone.points.length < 4) return;
const position = [target[0], 10, target[2]];
const input = {
userId: userId,
versionId: selectedVersion?.versionId || '',
projectId,
organization,
zoneData: {
zoneName: zone.zoneName,
zoneUuid: zone.zoneUuid,
points: zone.points,
viewPortCenter: target,
viewPortposition: position,
layer: zone.layer,
},
};
socket.emit("v1:zone:set", input);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const updateZoneToBackend = async (zone: {
zoneUuid: string;
zoneName: string;
points: [number, number, number][];
layer: string;
}) => {
const calculateCenter = (points: number[][]) => {
if (!points || points.length === 0) return null;
let sumX = 0, sumY = 0, sumZ = 0;
const numPoints = points.length;
points.forEach(([x, y, z]) => {
sumX += x;
sumY += y;
sumZ += z;
});
return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [
number,
number,
number
];
};
const target: [number, number, number] | null = calculateCenter(zone.points);
if (!target || zone.points.length < 4) return;
const position = [target[0], 10, target[2]];
const input = {
userId: userId,
versionId: selectedVersion?.versionId || '',
projectId,
organization,
zoneData: {
zoneName: zone.zoneName,
zoneUuid: zone.zoneUuid,
points: zone.points,
viewPortCenter: target,
viewPortposition: position,
layer: zone.layer,
},
};
socket.emit("v1:zone:set", input);
};
const deleteZoneFromBackend = async (zoneUuid: string) => {
const input = {
userId: userId,
versionId: selectedVersion?.versionId || '',
projectId,
organization,
zoneUuid: zoneUuid,
};
socket.emit("v1:zone:delete", input);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const handleDeleteZone = (zoneUuid: string) => {
const updatedZones = zones.filter((zone: any) => zone.zoneUuid !== zoneUuid);
setZones(updatedZones);
const zoneIndex = zones.findIndex((zone: any) => zone.zoneUuid === zoneUuid);
if (zoneIndex !== -1) {
const zonePointsToRemove = zonePoints.slice(zoneIndex * 4, zoneIndex * 4 + 4);
zonePointsToRemove.forEach((point: any) => groupsRef.current.remove(point));
const updatedzonePoints = zonePoints.filter((_: any, index: any) => index < zoneIndex * 4 || index >= zoneIndex * 4 + 4);
setZonePoints(updatedzonePoints);
}
deleteZoneFromBackend(zoneUuid);
};
useEffect(() => {
if (!camera || !toggleView) return;
const canvasElement = gl.domElement;
let drag = false;
let isLeftMouseDown = false;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(groupsRef.current.children, true);
if (intersects.length > 0 && toolMode === "move") {
const clickedObject = intersects[0].object;
const sphereIndex = zonePoints.findIndex((point: any) =>
point.equals(clickedObject.position)
);
if (sphereIndex !== -1) {
(controls as any).enabled = false;
setDraggedSphere(zonePoints[sphereIndex]);
setIsDragging(true);
}
}
}
};
const onMouseUp = (evt: any) => {
if (evt.button === 0 && !drag && !isDragging && toolMode === 'Zone') {
isLeftMouseDown = false;
if (!startPoint) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
setStartPoint(point);
setEndPoint(null);
}
} else if (startPoint) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (!point) return;
const points = [
[startPoint.x, 0.15, startPoint.z],
[point.x, 0.15, startPoint.z],
[point.x, 0.15, point.z],
[startPoint.x, 0.15, point.z],
[startPoint.x, 0.15, startPoint.z],
] as [number, number, number][];
const zoneName = `Zone ${zones.length + 1}`;
const zoneUuid = THREE.MathUtils.generateUUID();
const newZone = {
zoneUuid,
zoneName,
points: points,
layer: activeLayer,
};
const newZones = [...zones, newZone];
setZones(newZones);
const newzonePoints = [
new THREE.Vector3(startPoint.x, 0.15, startPoint.z),
new THREE.Vector3(point.x, 0.15, startPoint.z),
new THREE.Vector3(point.x, 0.15, point.z),
new THREE.Vector3(startPoint.x, 0.15, point.z),
];
const updatedZonePoints = [...zonePoints, ...newzonePoints];
setZonePoints(updatedZonePoints);
addZoneToBackend(newZone);
setStartPoint(null);
setEndPoint(null);
}
} else if (evt.button === 0 && !drag && !isDragging && toolMode === '2D-Delete') {
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(
groupsRef.current.children,
true
);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
const sphereIndex = zonePoints.findIndex((point: any) =>
point.equals(clickedObject.position)
);
if (sphereIndex !== -1) {
const zoneIndex = Math.floor(sphereIndex / 4);
const zoneUuid = zones[zoneIndex].zoneUuid;
handleDeleteZone(zoneUuid);
return;
}
}
}
if (evt.button === 0) {
if (isDragging && draggedSphere) {
setIsDragging(false);
setDraggedSphere(null);
const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere);
if (sphereIndex !== -1) {
const zoneIndex = Math.floor(sphereIndex / 4);
if (zoneIndex !== -1 && zones[zoneIndex]) {
updateZoneToBackend(zones[zoneIndex]);
}
}
}
}
};
const onMouseMove = () => {
if (!groupsRef.current) return;
if (isLeftMouseDown) {
drag = true;
}
raycaster.setFromCamera(pointer, camera);
if (isDragging && draggedSphere) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
draggedSphere.set(point.x, 0.15, point.z);
const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere);
if (sphereIndex !== -1) {
const zoneIndex = Math.floor(sphereIndex / 4);
const cornerIndex = sphereIndex % 4;
const updatedZones = zones.map((zone: any, index: number) => {
if (index === zoneIndex) {
const updatedPoints = [...zone.points];
updatedPoints[cornerIndex] = [point.x, 0.15, point.z];
updatedPoints[4] = updatedPoints[0];
return { ...zone, points: updatedPoints };
}
return zone;
});
setZones(updatedZones);
}
}
}
};
const onContext = (event: any) => {
event.preventDefault();
setStartPoint(null);
setEndPoint(null);
};
if (toolMode === "Zone" || toolMode === '2D-Delete' || toolMode === "move") {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("contextmenu", onContext);
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("contextmenu", onContext);
};
}, [gl, camera, startPoint, toggleView, scene, toolMode, zones, isDragging, zonePoints, draggedSphere, activeLayer, raycaster, pointer, controls, plane, setZones, setZonePoints, addZoneToBackend, handleDeleteZone, updateZoneToBackend, selectedVersion?.versionId]);
useFrame(() => {
if (!startPoint) return;
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
setEndPoint(point);
}
});
return (
<group ref={groupsRef} name="zoneGroup">
<group name="zones" visible={!toggleView}>
{zones.map((zone: any) => (
<group
key={zone.zoneUuid}
name={zone.zoneName}
visible={zone.zoneUuid === selectedZone.zoneUuid}
>
{zone.points
.slice(0, -1)
.map((point: [number, number, number], index: number) => {
const nextPoint = zone.points[index + 1];
const point1 = new THREE.Vector3(point[0], point[1], point[2]);
const point2 = new THREE.Vector3(
nextPoint[0],
nextPoint[1],
nextPoint[2]
);
const planeWidth = point1.distanceTo(point2);
const planeHeight = CONSTANTS.zoneConfig.height;
const midpoint = new THREE.Vector3(
(point1.x + point2.x) / 2,
CONSTANTS.zoneConfig.height / 2 +
(zone.layer - 1) * CONSTANTS.zoneConfig.height,
(point1.z + point2.z) / 2
);
const angle = Math.atan2(
point2.z - point1.z,
point2.x - point1.x
);
return (
<mesh
key={index}
position={midpoint}
rotation={[0, -angle, 0]}
>
<planeGeometry args={[planeWidth, planeHeight]} />
<primitive
object={zoneMaterial.clone()}
attach="material"
/>
</mesh>
);
})}
{!toggleView &&
(() => {
const points3D = zone.points || [];
const coords2D = points3D.map((p: any) => [p[0], p[2]]);
// Ensure the polygon is closed
if (
coords2D.length >= 4 &&
(coords2D[0][0] !== coords2D[coords2D.length - 1][0] ||
coords2D[0][1] !== coords2D[coords2D.length - 1][1])
) {
coords2D.push(coords2D[0]);
}
if (coords2D.length < 4) return null;
const polygon = turf.polygon([coords2D]);
const center2D = turf.center(polygon).geometry.coordinates;
// Calculate the average Y value
const sumY = points3D.reduce(
(sum: number, p: any) => sum + p[1],
0
);
const avgY = points3D.length > 0 ? sumY / points3D.length : 0;
const htmlPosition: [number, number, number] = [
center2D[0],
avgY + (CONSTANTS.zoneConfig.height || 0) + 1.5,
center2D[1],
];
return (
<Html
// data
key={zone.zoneUuid}
position={htmlPosition}
// class
className="zone-name-wrapper"
// others
center
>
<div className="zone-name">{zone.zoneName}</div>
</Html>
);
})()}
</group>
))}
</group>
<group name="zoneLines" visible={toggleView}>
{zones
.filter((zone: any) => zone.layer === activeLayer)
.map((zone: any) => (
<Line
key={zone.zoneUuid}
points={zone.points}
color="#007BFF"
lineWidth={3}
onClick={(e) => {
e.stopPropagation();
if (toolMode === '2D-Delete') {
handleDeleteZone(zone.zoneUuid);
}
}}
/>
))}
</group>
<group name="zoneArea" visible={toggleView}>
{zones.map((zone: any, index: any) => {
if (!toggleView) return null;
const points3D = zone.points;
const coords2D = points3D.map((p: any) => [p[0], p[2]]);
if (
coords2D.length < 4 ||
coords2D[0][0] !== coords2D[coords2D.length - 1][0] ||
coords2D[0][1] !== coords2D[coords2D.length - 1][1]
) {
coords2D.push(coords2D[0]);
}
if (coords2D.length < 4) return null;
const polygon = turf.polygon([coords2D]);
const center2D = turf.center(polygon).geometry.coordinates;
const sumY = points3D.reduce((sum: number, p: any) => sum + p[1], 0);
const avgY = sumY / points3D.length;
const area = computeArea(points3D, "zone");
const formattedArea = `${area.toFixed(2)}`;
const htmlPosition: [number, number, number] = [
center2D[0],
avgY + CONSTANTS.zoneConfig.height,
center2D[1],
];
return (
<Html
// data
key={`${index}-${zone}`}
position={htmlPosition}
// class
wrapperClass="distance-text-wrapper"
className="distance-text"
// other
zIndexRange={[1, 0]}
prepend
center
sprite
>
<div
className={`distance area line-${zone}`}
key={`${index}-${zone}`}
>
{zone.zoneName} ({formattedArea})
</div>
</Html>
);
})}
</group>
<group name="zonePoints" visible={toggleView}>
{zones
.filter((zone: any) => zone.layer === activeLayer)
.flatMap((zone: any) =>
zone.points.slice(0, 4).map((point: any, pointIndex: number) => (
<Sphere
key={`${zone.zoneUuid}-point-${pointIndex}`}
position={new THREE.Vector3(...point)}
args={[0.3, 16, 16]}
name={`point-${zone.zoneUuid}-${pointIndex}`}
>
<meshBasicMaterial color="red" />
</Sphere>
))
)}
</group>
<group name="tempGroup" visible={toggleView}>
{startPoint && endPoint && (
<Line
points={[
[startPoint.x, 0.15, startPoint.z],
[endPoint.x, 0.15, startPoint.z],
[endPoint.x, 0.15, endPoint.z],
[startPoint.x, 0.15, endPoint.z],
[startPoint.x, 0.15, startPoint.z],
]}
color="#C164FF"
lineWidth={3}
/>
)}
</group>
</group>
);
};
export default ZoneGroup;

View File

@@ -1,26 +1,26 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function getClosestIntersection(
intersects: Types.Vector3Array,
point: Types.Vector3
): Types.Vector3 {
////////// A function that finds which point is closest from the intersects points that is given, Used in finding which point in a line is closest when clicked on a line during drawing //////////
let closestNewPoint: THREE.Vector3 = point;
let minDistance = Infinity;
for (const intersect of intersects) {
const distance = point.distanceTo(intersect);
if (distance < minDistance) {
minDistance = distance;
closestNewPoint = intersect;
}
}
return closestNewPoint;
}
export default getClosestIntersection;
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function getClosestIntersection(
intersects: Types.Vector3Array,
point: Types.Vector3
): Types.Vector3 {
////////// A function that finds which point is closest from the intersects points that is given, Used in finding which point in a line is closest when clicked on a line during drawing //////////
let closestNewPoint: THREE.Vector3 = point;
let minDistance = Infinity;
for (const intersect of intersects) {
const distance = point.distanceTo(intersect);
if (distance < minDistance) {
minDistance = distance;
closestNewPoint = intersect;
}
}
return closestNewPoint;
}
export default getClosestIntersection;

View File

@@ -0,0 +1,12 @@
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));
}

View File

@@ -2,10 +2,20 @@ import * as THREE from 'three';
import { useThree } from '@react-three/fiber';
import { useEffect, useMemo, useState } from "react";
import { DragControls, Tube } from '@react-three/drei';
import { useToolMode } from '../../../store/builder/store';
import { useSocketStore, useToolMode } from '../../../store/builder/store';
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import { useSceneContext } from '../../scene/sceneContext';
import * as Constants from '../../../types/world/worldConstants';
import { useVersionContext } from '../version/versionContext';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi';
// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi';
// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi';
// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi';
interface LineProps {
points: [Point, Point];
@@ -16,9 +26,16 @@ function Line({ points }: Readonly<LineProps>) {
const { raycaster, camera, pointer, gl } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const [isDeletable, setIsDeletable] = useState(false);
const { socket } = useSocketStore();
const { toolMode } = useToolMode();
const { wallStore } = useSceneContext();
const { removeWallByPoints, setPosition } = wallStore();
const { wallStore, floorStore, zoneStore } = useSceneContext();
const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore();
const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId } = floorStore();
const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId } = zoneStore();
const { userId, organization } = getUserData();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();
@@ -79,9 +96,123 @@ function Line({ points }: Readonly<LineProps>) {
const handlePointClick = (points: [Point, Point]) => {
if (toolMode === '2D-Delete') {
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
removeWallByPoints(points);
const removedWall = removeWallByPoints(points);
if (removedWall && projectId) {
// API
// deleteWallApi(projectId, selectedVersion?.versionId || '', removedWall.wallUuid);
// SOCKET
const data = {
wallUuid: removedWall.wallUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:delete', data);
}
setHoveredLine(null);
}
if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
const { removedFloors, updatedFloors } = removeFloorByPoints(points);
if (removedFloors.length > 0) {
removedFloors.forEach(floor => {
if (projectId) {
// API
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floor.floorUuid);
// SOCKET
const data = {
floorUuid: floor.floorUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:delete', data);
}
});
}
if (updatedFloors.length > 0) {
updatedFloors.forEach(floor => {
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: floor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
});
}
setHoveredLine(null);
}
if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') {
const { removedZones, updatedZones } = removeZoneByPoints(points);
if (removedZones.length > 0) {
removedZones.forEach(zone => {
if (projectId) {
// API
// deleteZoneApi(projectId, selectedVersion?.versionId || '', zone.zoneUuid);
// SOCKET
const data = {
zoneUuid: zone.zoneUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:delete', data);
}
});
}
if (updatedZones.length > 0) {
updatedZones.forEach(zone => {
if (projectId) {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', zone);
// SOCKET
const data = {
zoneData: zone,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:add', data);
}
});
}
}
gl.domElement.style.cursor = 'default';
}
}
@@ -105,8 +236,18 @@ function Line({ points }: Readonly<LineProps>) {
const newStart = new THREE.Vector3().addVectors(start, delta);
const newEnd = new THREE.Vector3().addVectors(end, delta);
setPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
setPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
setWallPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
setWallPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
}
if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
setFloorPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
setFloorPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
}
if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') {
setZonePosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
setZonePosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
}
}
}
};
@@ -127,11 +268,90 @@ function Line({ points }: Readonly<LineProps>) {
};
const handleDragEnd = (points: [Point, Point]) => {
if (toolMode !== 'move' || !dragOffset) return;
gl.domElement.style.cursor = 'default';
setDragOffset(null);
if (toolMode !== 'move') return;
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
// console.log('Wall after drag: ', points);
const updatedWalls1 = getWallsByPointId(points[0].pointUuid);
const updatedWalls2 = getWallsByPointId(points[1].pointUuid);
const updatedWalls = [...updatedWalls1, ...updatedWalls2].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid));
if (updatedWalls.length > 0 && projectId) {
updatedWalls.forEach(updatedWall => {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall).catch((error) => {
// console.error('Error updating wall:', error);
// });
// SOCKET
const data = {
wallData: updatedWall,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
})
}
} else if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
const updatedFloors1 = getFloorsByPointId(points[0].pointUuid);
const updatedFloors2 = getFloorsByPointId(points[1].pointUuid);
const updatedFloors = [...updatedFloors1, ...updatedFloors2].filter((floor, index, self) => index === self.findIndex((f) => f.floorUuid === floor.floorUuid));
if (updatedFloors.length > 0 && projectId) {
updatedFloors.forEach(updatedFloor => {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor).catch((error) => {
// console.error('Error updating floor:', error);
// });
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
})
}
} else if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') {
const updatedZones1 = getZonesByPointId(points[0].pointUuid);
const updatedZones2 = getZonesByPointId(points[1].pointUuid);
const updatedZones = [...updatedZones1, ...updatedZones2].filter((zone, index, self) => index === self.findIndex((z) => z.zoneUuid === zone.zoneUuid));
if (updatedZones.length > 0 && projectId) {
updatedZones.forEach(updatedZone => {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone).catch((error) => {
// console.error('Error updating zone:', error);
// });
// SOCKET
const data = {
zoneData: updatedZone,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:add', data);
})
}
}
}

View File

@@ -0,0 +1,54 @@
// import * as THREE from 'three';
// import updateReferencePolesheight from './updateReferencePolesheight';
// import * as Types from "../../../../types/world/worldTypes";
// function addAndUpdateReferencePillar(
// raycaster: THREE.Raycaster,
// floorGroup: Types.RefGroup,
// referencePole: Types.RefMesh
// ): void {
// ////////// Find Pillars position and scale based on the pointer interaction //////////
// let Roofs = raycaster.intersectObjects(floorGroup.current.children, true);
// const intersected = Roofs.find(intersect => intersect.object.name.includes("Roof") || intersect.object.name.includes("Floor"));
// if (intersected) {
// const intersectionPoint = intersected.point;
// raycaster.ray.origin.copy(intersectionPoint);
// raycaster.ray.direction.set(0, -1, 0);
// const belowIntersections = raycaster.intersectObjects(floorGroup.current.children, true);
// const validIntersections = belowIntersections.filter(intersect => intersect.object.name.includes("Floor"));
// let distance: Types.Number;
// if (validIntersections.length > 1) {
// let valid = validIntersections.find(intersectedBelow => intersected.point.distanceTo(intersectedBelow.point) > 3);
// if (valid) {
// updateReferencePolesheight(intersectionPoint, valid.distance, referencePole, floorGroup);
// } else {
// const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z);
// distance = intersected.point.distanceTo(belowPoint);
// if (distance > 3) {
// updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup);
// }
// }
// } else {
// const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z);
// distance = intersected.point.distanceTo(belowPoint);
// if (distance > 3) {
// updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup);
// }
// }
// } else {
// if (referencePole.current) {
// (<any>referencePole.current.material).dispose();
// (<any>referencePole.current.geometry).dispose();
// floorGroup.current.remove(referencePole.current);
// referencePole.current = null;
// }
// }
// }
// export default addAndUpdateReferencePillar;

View File

@@ -0,0 +1,24 @@
// import * as THREE from 'three';
// import * as CONSTANTS from '../../../../types/world/worldConstants';
// import * as Types from "../../../../types/world/worldTypes";
// function addPillar(
// referencePole: Types.RefMesh,
// floorGroup: Types.RefGroup
// ): void {
// ////////// Add Pillars to the scene based on the reference. current poles position and scale //////////
// if (referencePole.current) {
// let pole: THREE.Mesh;
// const geometry = referencePole.current.userData.geometry.clone();
// const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.columnConfig.defaultColor });
// pole = new THREE.Mesh(geometry, material);
// pole.rotateX(Math.PI / 2);
// pole.name = "Pole";
// pole.position.set(referencePole.current.userData.position.x, referencePole.current.userData.position.y, referencePole.current.userData.position.z);
// floorGroup.current.add(pole);
// }
// }
// export default addPillar;

View File

@@ -0,0 +1,34 @@
// import * as THREE from 'three';
// import * as Types from "../../../../types/world/worldTypes";
// function DeletableHoveredPillar(
// state: Types.ThreeState,
// floorGroup: Types.RefGroup,
// hoveredDeletablePillar: Types.RefMesh
// ): void {
// ////////// Altering the color of the hovered Pillar during the Deletion time //////////
// const intersects = state.raycaster.intersectObjects(floorGroup.current.children, true);
// const poleIntersect = intersects.find(intersect => intersect.object.name === "Pole");
// if (poleIntersect) {
// if (poleIntersect.object.name !== "Pole") {
// return;
// }
// if (hoveredDeletablePillar.current) {
// (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black");
// hoveredDeletablePillar.current = undefined;
// }
// hoveredDeletablePillar.current = poleIntersect.object as THREE.Mesh; // Type assertion
// (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("red");
// } else {
// if (hoveredDeletablePillar.current) {
// (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black");
// hoveredDeletablePillar.current = undefined;
// }
// }
// }
// export default DeletableHoveredPillar;

View File

@@ -0,0 +1,21 @@
// import { toast } from 'react-toastify';
// import * as Types from "../../../../types/world/worldTypes";
// function DeletePillar(
// hoveredDeletablePillar: Types.RefMesh,
// floorGroup: Types.RefGroup
// ): void {
// ////////// Deleting the hovered Pillar from the itemsGroup //////////
// if (hoveredDeletablePillar.current) {
// (<any>hoveredDeletablePillar.current.material).dispose();
// (<any>hoveredDeletablePillar.current.geometry).dispose();
// floorGroup.current.remove(hoveredDeletablePillar.current);
// echo.success("Pillar Removed!");
// hoveredDeletablePillar.current = undefined;
// }
// }
// export default DeletePillar;

View File

@@ -0,0 +1,40 @@
// import * as THREE from 'three';
// import * as Types from "../../../../types/world/worldTypes";
// function updateReferencePolesheight(
// intersectionPoint: Types.Vector3,
// distance: Types.Number,
// referencePole: Types.RefMesh,
// floorGroup: Types.RefGroup
// ): void {
// ////////// Add a Reference Pillar and update its position and scale based on the pointer interaction //////////
// if (referencePole.current) {
// (<any>referencePole.current.material).dispose();
// (<any>referencePole.current.geometry).dispose();
// floorGroup.current.remove(referencePole.current);
// referencePole.current.geometry.dispose();
// }
// const shape = new THREE.Shape();
// shape.moveTo(0.5, 0);
// shape.absarc(0, 0, 0.5, 0, 2 * Math.PI, false);
// const extrudeSettings = {
// depth: distance,
// bevelEnabled: false,
// };
// const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
// const material = new THREE.MeshBasicMaterial({ color: "green", transparent: true, opacity: 0.5 });
// referencePole.current = new THREE.Mesh(geometry, material);
// referencePole.current.rotateX(Math.PI / 2);
// referencePole.current.position.set(intersectionPoint.x, intersectionPoint.y - 0.01, intersectionPoint.z);
// referencePole.current.userData = { geometry: geometry, distance: distance, position: { x: intersectionPoint.x, y: intersectionPoint.y - 0.01, z: intersectionPoint.z } };
// floorGroup.current.add(referencePole.current);
// }
// export default updateReferencePolesheight;

View File

@@ -11,9 +11,11 @@ const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping i
const CAN_ANGLE_SNAP = true; // Whether snapping is enabled or not
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
const { aisleStore, wallStore } = useSceneContext();
const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext();
const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore();
const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore();
const { floors, getConnectedPoints: getConnectedFloorPoints } = floorStore();
const { zones, getConnectedPoints: getConnectedZonePoints } = zoneStore();
// Wall Snapping
@@ -24,10 +26,13 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
);
}, [walls, currentPoint]);
const snapWallPoint = useCallback((position: [number, number, number]) => {
const snapWallPoint = useCallback((position: [number, number, number], tempPoints?: Point) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const otherPoints = getAllOtherWallPoints();
if (tempPoints) {
otherPoints.push(tempPoints);
}
const currentVec = new THREE.Vector3(...position);
for (const point of otherPoints) {
const pointVec = new THREE.Vector3(...point.position);
@@ -187,10 +192,188 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
};
}, [currentPoint, getConnectedAislePoints]);
// Floor Snapping
const getAllOtherFloorPoints = useCallback(() => {
if (!currentPoint) return [];
return floors.flatMap(floor =>
floor.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [floors, currentPoint]);
const snapFloorPoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
let otherPoints = getAllOtherFloorPoints();
if (tempPoints) {
otherPoints = [...otherPoints, ...tempPoints];
}
const currentVec = new THREE.Vector3(...position);
for (const point of otherPoints) {
const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Floor') {
return { position: point.position, isSnapped: true, snappedPoint: point };
}
}
return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherFloorPoints]);
const snapFloorAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
} => {
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
const connectedPoints = getConnectedFloorPoints(currentPoint.uuid);
if (connectedPoints.length === 0) {
return { position: newPosition, isSnapped: false, snapSources: [] };
}
const newPos = new THREE.Vector3(...newPosition);
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
for (const connectedPoint of connectedPoints) {
const cPos = new THREE.Vector3(...connectedPoint.position);
const xDist = Math.abs(newPos.x - cPos.x);
const zDist = Math.abs(newPos.z - cPos.z);
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestX || xDist < closestX.dist) {
closestX = { pos: cPos, dist: xDist };
}
}
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestZ || zDist < closestZ.dist) {
closestZ = { pos: cPos, dist: zDist };
}
}
}
const snappedPos = newPos.clone();
const snapSources: THREE.Vector3[] = [];
if (closestX) {
snappedPos.x = closestX.pos.x;
snapSources.push(closestX.pos.clone());
}
if (closestZ) {
snappedPos.z = closestZ.pos.z;
snapSources.push(closestZ.pos.clone());
}
const isSnapped = snapSources.length > 0;
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
};
}, [currentPoint, getConnectedFloorPoints]);
// Zone Snapping
const getAllOtherZonePoints = useCallback(() => {
if (!currentPoint) return [];
return zones.flatMap(zone =>
zone.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [zones, currentPoint]);
const snapZonePoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
let otherPoints = getAllOtherZonePoints();
if (tempPoints) {
otherPoints = [...otherPoints, ...tempPoints];
}
const currentVec = new THREE.Vector3(...position);
for (const point of otherPoints) {
const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Zone') {
return { position: point.position, isSnapped: true, snappedPoint: point };
}
}
return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherZonePoints]);
const snapZoneAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
} => {
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
const connectedPoints = getConnectedZonePoints(currentPoint.uuid);
if (connectedPoints.length === 0) {
return { position: newPosition, isSnapped: false, snapSources: [] };
}
const newPos = new THREE.Vector3(...newPosition);
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
for (const connectedPoint of connectedPoints) {
const cPos = new THREE.Vector3(...connectedPoint.position);
const xDist = Math.abs(newPos.x - cPos.x);
const zDist = Math.abs(newPos.z - cPos.z);
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestX || xDist < closestX.dist) {
closestX = { pos: cPos, dist: xDist };
}
}
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestZ || zDist < closestZ.dist) {
closestZ = { pos: cPos, dist: zDist };
}
}
}
const snappedPos = newPos.clone();
const snapSources: THREE.Vector3[] = [];
if (closestX) {
snappedPos.x = closestX.pos.x;
snapSources.push(closestX.pos.clone());
}
if (closestZ) {
snappedPos.z = closestZ.pos.z;
snapSources.push(closestZ.pos.clone());
}
const isSnapped = snapSources.length > 0;
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
};
}, [currentPoint, getConnectedZonePoints]);
return {
snapAislePoint,
snapAisleAngle,
snapWallPoint,
snapWallAngle,
snapFloorPoint,
snapFloorAngle,
snapZonePoint,
snapZoneAngle,
};
};

View File

@@ -1,29 +1,42 @@
import * as THREE from 'three';
import * as Constants from '../../../types/world/worldConstants';
import { useRef, useState, useEffect, useMemo } from 'react';
import { useToolMode } from '../../../store/builder/store';
import { useSocketStore, useToolMode } from '../../../store/builder/store';
import { DragControls } from '@react-three/drei';
import { useThree } from '@react-three/fiber';
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import { usePointSnapping } from './helpers/usePointSnapping';
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
import { useParams } from 'react-router-dom';
import { createAisleApi } from '../../../services/factoryBuilder/aisle/createAisleApi';
import { useVersionContext } from '../version/versionContext';
import { useSceneContext } from '../../scene/sceneContext';
import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi';
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi';
// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi';
// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi';
// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi';
import { getUserData } from '../../../functions/getUserData';
function Point({ point }: { readonly point: Point }) {
const materialRef = useRef<THREE.ShaderMaterial>(null);
const { raycaster, camera, pointer, gl } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const [isHovered, setIsHovered] = useState(false);
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const { socket } = useSocketStore();
const { toolMode } = useToolMode();
const { aisleStore, wallStore } = useSceneContext();
const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext();
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
const { setPosition: setWallPosition, removePoint: removeWallPoint } = wallStore();
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore();
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
const { userId, organization } = getUserData();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
@@ -113,6 +126,14 @@ function Point({ point }: { readonly point: Point }) {
const wallSnapped = snapWallAngle(newPosition);
const finalSnapped = snapWallPoint(wallSnapped.position);
setWallPosition(point.pointUuid, finalSnapped.position);
} else if (point.pointType === 'Floor') {
const floorSnapped = snapFloorAngle(newPosition);
const finalSnapped = snapFloorPoint(floorSnapped.position);
setFloorPosition(point.pointUuid, finalSnapped.position);
} else if (point.pointType === 'Zone') {
const zoneSnapped = snapZoneAngle(newPosition);
const finalSnapped = snapZonePoint(zoneSnapped.position);
setZonePosition(point.pointUuid, finalSnapped.position);
}
}
}
@@ -138,11 +159,75 @@ function Point({ point }: { readonly point: Point }) {
const updatedAisles = getAislesByPointId(point.pointUuid);
if (updatedAisles.length > 0 && projectId) {
updatedAisles.forEach((updatedAisle) => {
createAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '')
upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '')
})
}
} else if (point.pointType === 'Wall') {
// console.log('Wall after drag: ', point);
const updatedWalls = getWallsByPointId(point.pointUuid);
if (updatedWalls && updatedWalls.length > 0 && projectId) {
updatedWalls.forEach((updatedWall) => {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
// SOCKET
const data = {
wallData: updatedWall,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
});
}
} else if (point.pointType === 'Floor') {
const updatedFloors = getFloorsByPointId(point.pointUuid);
if (updatedFloors && updatedFloors.length > 0 && projectId) {
updatedFloors.forEach((updatedFloor) => {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
});
}
} else if (point.pointType === 'Zone') {
const updatedZones = getZonesByPointId(point.pointUuid);
if (updatedZones && updatedZones.length > 0 && projectId) {
updatedZones.forEach((updatedZone) => {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone);
// SOCKET
const data = {
zoneData: updatedZone,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:add', data);
});
}
}
}
@@ -152,16 +237,133 @@ function Point({ point }: { readonly point: Point }) {
const removedAisles = removeAislePoint(point.pointUuid);
if (removedAisles.length > 0) {
removedAisles.forEach(aisle => {
if (projectId)
if (projectId) {
deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '')
}
});
setHoveredPoint(null);
}
}
if (point.pointType === 'Wall') {
const removedAisles = removeWallPoint(point.pointUuid);
if (removedAisles.length > 0) {
const removedWalls = removeWallPoint(point.pointUuid);
if (removedWalls.length > 0) {
setHoveredPoint(null);
removedWalls.forEach(wall => {
if (projectId) {
// API
// deleteWallApi(projectId, selectedVersion?.versionId || '', wall.wallUuid);
// SOCKET
const data = {
wallUuid: wall.wallUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:delete', data);
}
});
}
}
if (point.pointType === 'Floor') {
const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid);
setHoveredPoint(null);
if (removedFloors.length > 0) {
removedFloors.forEach(floor => {
if (projectId) {
// API
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floor.floorUuid);
// SOCKET
const data = {
floorUuid: floor.floorUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:delete', data);
}
});
}
if (updatedFloors.length > 0) {
updatedFloors.forEach(floor => {
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
// SOCKET
const data = {
floorData: floor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
});
}
}
if (point.pointType === 'Zone') {
const { removedZones, updatedZones } = removeZonePoint(point.pointUuid);
setHoveredPoint(null);
if (removedZones.length > 0) {
removedZones.forEach(zone => {
if (projectId) {
// API
// deleteZoneApi(projectId, selectedVersion?.versionId || '', zone.zoneUuid);
// SOCKET
const data = {
zoneUuid: zone.zoneUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:delete', data);
}
});
}
if (updatedZones.length > 0) {
updatedZones.forEach(zone => {
if (projectId) {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', zone);
// SOCKET
const data = {
zoneData: zone,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:add', data);
}
});
}
}
gl.domElement.style.cursor = 'default';

View File

@@ -21,9 +21,17 @@ function ReferencePoint({ point }: { readonly point: Point }) {
return null;
}
const pointName = point.pointType === 'Wall' ? 'Wall-Point' :
point.pointType === 'Floor' ? 'Floor-Point' :
point.pointType === 'Aisle' ? 'Aisle-Point' :
point.pointType === 'Zone' ? 'Zone-Point' : 'Point';
return (
<mesh
position={new THREE.Vector3(...point.position)}
name={pointName}
uuid={point.pointUuid}
userData={point}
>
<boxGeometry args={boxScale} />
<shaderMaterial

View File

@@ -85,9 +85,12 @@ export function useWallClassification(walls: Walls) {
}));
}
const allCoords = mergedLineStrings.flatMap(ls => ls.geometry.coordinates);
const uniqueCoords = Array.from(new Set(allCoords.map(coord => coord.join(','))));
if (uniqueCoords.length < 4) return [];
const lineStrings = turf.featureCollection(mergedLineStrings);
// Now polygonize merged line strings
const polygons = turf.polygonize(lineStrings);
const rooms: Point[][] = [];

View File

@@ -1,20 +1,21 @@
import * as THREE from 'three';
import { Base } from '@react-three/csg';
import { useMemo, useRef, useState } from 'react';
import { Decal, MeshDiscardMaterial } from '@react-three/drei';
import { MeshDiscardMaterial } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
import useModuleStore from '../../../../../store/useModuleStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useWallClassification } from './helpers/useWallClassification';
import { useToggleView, useWallVisibility } from '../../../../../store/builder/store';
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
import * as Constants from '../../../../../types/world/worldConstants';
import DecalInstance from '../../../Decal/decalInstance';
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
function Wall({ wall }: { readonly wall: Wall }) {
const { wallStore } = useSceneContext();
const { walls, addDecal } = wallStore();
@@ -116,14 +117,16 @@ function Wall({ wall }: { readonly wall: Wall }) {
</Base>
<mesh
castShadow
receiveShadow
geometry={geometry}
position={[centerX, centerY, centerZ]}
rotation={[0, -angle, 0]}
userData={wall}
name={`WallReference_${wall.wallUuid}`}
onClick={(e) => {
onDoubleClick={(e) => {
if (visible && !togglView && activeModule === 'builder') {
if (e.object.userData.wallUuid) {
e.stopPropagation();
setSelectedWall(e.object);
setSelectedDecal(null);
@@ -151,7 +154,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
<MeshDiscardMaterial />
{wall.decals.map((decal) => (
<DecalInstance visible={visible} key={decal.decalUuid} decal={decal} />
<DecalInstance zPosition={wall.wallThickness / 2 + 0.001} visible={visible} key={decal.decalUuid} decal={decal} />
))}
</mesh>
</mesh>

Some files were not shown because too many files have changed in this diff Show More