completed floor

This commit is contained in:
2025-06-27 15:44:31 +05:30
parent 64f0cdb148
commit c73bdf4556
22 changed files with 1422 additions and 1159 deletions

View File

@@ -22,6 +22,7 @@ import {
useSelectedEventData, useSelectedEventData,
useSelectedEventSphere, useSelectedEventSphere,
} from "../../../store/simulation/useSimulationStore"; } from "../../../store/simulation/useSimulationStore";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import GlobalProperties from "./properties/GlobalProperties"; import GlobalProperties from "./properties/GlobalProperties";
import AssetProperties from "./properties/AssetProperties"; import AssetProperties from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties"; import ZoneProperties from "./properties/ZoneProperties";
@@ -29,8 +30,9 @@ import EventProperties from "./properties/eventProperties/EventProperties";
import VersionHistory from "./versionHisory/VersionHistory"; import VersionHistory from "./versionHisory/VersionHistory";
import AisleProperties from "./properties/AisleProperties"; import AisleProperties from "./properties/AisleProperties";
import WallProperties from "./properties/WallProperties"; import WallProperties from "./properties/WallProperties";
import { useBuilderStore } from "../../../store/builder/useBuilderStore"; import FloorProperties from "./properties/FloorProperties";
import SelectedWallProperties from "./properties/SelectedWallProperties"; import SelectedWallProperties from "./properties/SelectedWallProperties";
import SelectedFloorProperties from "./properties/SelectedFloorProperties";
const SideBarRight: React.FC = () => { const SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
@@ -38,7 +40,7 @@ const SideBarRight: React.FC = () => {
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { subModule, setSubModule } = useSubModuleStore(); const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem(); const { selectedFloorItem } = useSelectedFloorItem();
const { selectedWall } = useBuilderStore(); const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore();
const { selectedEventData } = useSelectedEventData(); const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore(); const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
@@ -147,6 +149,7 @@ const SideBarRight: React.FC = () => {
subModule === "properties" && subModule === "properties" &&
activeModule !== "visualization" && activeModule !== "visualization" &&
!selectedFloorItem && !selectedFloorItem &&
!selectedFloor &&
!selectedWall && ( !selectedWall && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
@@ -155,6 +158,8 @@ const SideBarRight: React.FC = () => {
return <AisleProperties />; return <AisleProperties />;
} else if (toolMode === "Wall") { } else if (toolMode === "Wall") {
return <WallProperties />; return <WallProperties />;
} else if (toolMode === "Floor") {
return <FloorProperties />;
} else { } else {
return <GlobalProperties />; return <GlobalProperties />;
} }
@@ -178,6 +183,8 @@ const SideBarRight: React.FC = () => {
subModule === "properties" && subModule === "properties" &&
activeModule !== "visualization" && activeModule !== "visualization" &&
!selectedFloorItem && !selectedFloorItem &&
!selectedFloor &&
!selectedAisle &&
selectedWall && ( selectedWall && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
@@ -186,6 +193,20 @@ const SideBarRight: React.FC = () => {
</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 && {!viewVersionHistory &&
subModule === "zoneProperties" && subModule === "zoneProperties" &&
(activeModule === "builder" || activeModule === "simulation") && ( (activeModule === "builder" || activeModule === "simulation") && (

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 InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import defaultTexture from '../../../../assets/textures/floor/wall-tex.png'; import defaultTexture from '../../../../assets/textures/floor/wall-tex.png';
@@ -8,11 +8,14 @@ import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { useSceneContext } from "../../../../modules/scene/sceneContext"; import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useVersionContext } from "../../../../modules/builder/version/versionContext"; import { useVersionContext } from "../../../../modules/builder/version/versionContext";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
import { getUserData } from "../../../../functions/getUserData"; import { getUserData } from "../../../../functions/getUserData";
import { useSocketStore } from "../../../../store/builder/store"; import { useSocketStore } from "../../../../store/builder/store";
// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
const SelectedWallProperties = () => { const SelectedWallProperties = () => {
const [height, setHeight] = useState("");
const [thickness, setThickness] = useState("");
const { selectedWall } = useBuilderStore(); const { selectedWall } = useBuilderStore();
const { wallStore } = useSceneContext(); const { wallStore } = useSceneContext();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -31,7 +34,15 @@ const SelectedWallProperties = () => {
const wall = selectedWall ? getWallById(selectedWall.userData.wallUuid) : null; 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) => { const handleHeightChange = (val: string) => {
setHeight(val);
const height = parseFloat(val); const height = parseFloat(val);
if (!isNaN(height) && wall) { if (!isNaN(height) && wall) {
const updatedWall = updateWall(wall.wallUuid, { wallHeight: height }); const updatedWall = updateWall(wall.wallUuid, { wallHeight: height });
@@ -57,6 +68,7 @@ const SelectedWallProperties = () => {
}; };
const handleThicknessChange = (val: string) => { const handleThicknessChange = (val: string) => {
setThickness(val);
const thickness = parseFloat(val); const thickness = parseFloat(val);
if (!isNaN(thickness) && wall) { if (!isNaN(thickness) && wall) {
const updatedWall = updateWall(wall.wallUuid, { wallThickness: thickness }); const updatedWall = updateWall(wall.wallUuid, { wallThickness: thickness });
@@ -109,16 +121,8 @@ const SelectedWallProperties = () => {
if (!wall) return null; if (!wall) return null;
const selectedMaterials = { const selectedMaterials = {
side1: { side1: materials.find((m) => m.textureId === wall.insideMaterial) ?? materials[0],
texture: materials.find((material) => material.textureId === wall.insideMaterial)?.texture || 'Unknown', side2: materials.find((m) => m.textureId === wall.outsideMaterial) ?? materials[0]
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'
}
}; };
return ( return (
@@ -128,12 +132,12 @@ const SelectedWallProperties = () => {
<div className="wall-properties"> <div className="wall-properties">
<InputWithDropDown <InputWithDropDown
label="Height" label="Height"
value={`${wall.wallHeight}`} value={height}
onChange={handleHeightChange} onChange={handleHeightChange}
/> />
<InputWithDropDown <InputWithDropDown
label="Thickness" label="Thickness"
value={`${wall.wallThickness}`} value={thickness}
onChange={handleThicknessChange} onChange={handleThicknessChange}
/> />
</div> </div>

View File

@@ -11,30 +11,20 @@ import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
// Define Material type // Define Material type
type Material = { type Material = {
texture: string; texture: string;
textureId: string;
textureName: string; textureName: string;
}; };
// Default and initial materials const materials = [
const defaultMaterial: Material = { { texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" },
texture: defaultTexture, { texture: wallTexture1, textureId: "Material 1", textureName: "Grunge Concrete Wall" }
textureName: "Default Material", ];
};
const initialMaterial: Material = {
texture: wallTexture1,
textureName: "Grunge Concrete Wall",
};
const WallProperties = () => { 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<{ const [selectedMaterials, setSelectedMaterials] = useState<{
side1: Material | null; side1: Material | null;
side2: Material | null; side2: Material | null;
@@ -43,11 +33,10 @@ const WallProperties = () => {
side2: null, side2: null,
}); });
// Set default material initially for both sides
useEffect(() => { useEffect(() => {
setSelectedMaterials({ setSelectedMaterials({
side1: defaultMaterial, side1: materials.find((mat) => mat.textureId === outsideMaterial) || null,
side2: defaultMaterial, side2: materials.find((mat) => mat.textureId === insideMaterial) || null,
}); });
}, []); }, []);

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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); 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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); 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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); 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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); 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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); 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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); 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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); 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]} position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
userData={aisle} userData={aisle}
onClick={handleClick} onDoubleClick={handleClick}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedAisle(null); setSelectedAisle(null);
}} }}

View File

@@ -26,7 +26,7 @@ const gltfLoaderWorker = new Worker(
function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, readonly plane: RefMesh }) { function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, readonly plane: RefMesh }) {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { controls, gl, pointer, camera, raycaster } = useThree(); const { controls, gl, pointer, camera, raycaster, scene } = useThree();
const { setLoadingProgress } = useLoadingProgress(); const { setLoadingProgress } = useLoadingProgress();
const { assetStore, eventStore } = useSceneContext(); const { assetStore, eventStore } = useSceneContext();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -277,7 +277,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 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

@@ -10,10 +10,10 @@ import PointsCalculator from "../../../simulation/events/points/functions/points
import { getUserData } from "../../../../functions/getUserData"; import { getUserData } from "../../../../functions/getUserData";
async function addAssetModel( async function addAssetModel(
scene: THREE.Scene,
raycaster: THREE.Raycaster, raycaster: THREE.Raycaster,
camera: THREE.Camera, camera: THREE.Camera,
pointer: THREE.Vector2, pointer: THREE.Vector2,
floorGroup: Types.RefGroup,
socket: Socket<any>, socket: Socket<any>,
selectedItem: any, selectedItem: any,
setSelectedItem: any, setSelectedItem: any,
@@ -32,19 +32,16 @@ async function addAssetModel(
const loader = new GLTFLoader(); const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader(); const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/");
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
loader.setDRACOLoader(dracoLoader); loader.setDRACOLoader(dracoLoader);
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
const floorIntersections = raycaster.intersectObjects( const wallFloorsGroup = scene.getObjectByName("Walls-Floors-Group") as Types.Group | null;
floorGroup.current.children, const floorsGroup = scene.getObjectByName("Floors-Group") as Types.Group | null;
true const floorChildren = floorsGroup?.children ?? [];
); const wallFloorChildren = wallFloorsGroup?.children ?? [];
const intersectedFloor = floorIntersections.find((intersect) => const floorIntersections = raycaster.intersectObjects([...floorChildren, ...wallFloorChildren], true);
intersect.object.name.includes("Floor") const intersectedFloor = floorIntersections.find((intersect) => intersect.object.name.includes("Floor"));
);
const planeIntersections = raycaster.intersectObject(plane.current!, true); const planeIntersections = raycaster.intersectObject(plane.current!, true);
const intersectedPlane = planeIntersections[0]; const intersectedPlane = planeIntersections[0];
@@ -52,53 +49,32 @@ async function addAssetModel(
let intersectPoint: THREE.Vector3 | null = null; let intersectPoint: THREE.Vector3 | null = null;
if (intersectedFloor && intersectedPlane) { if (intersectedFloor && intersectedPlane) {
intersectPoint = // intersectPoint = intersectedFloor.distance < intersectedPlane.distance ?
intersectedFloor.distance < intersectedPlane.distance // new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z)
? new THREE.Vector3( // : new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
intersectedFloor.point.x, if (intersectedFloor.distance < intersectedPlane.distance) {
Math.round(intersectedFloor.point.y), if (intersectedFloor.object.userData.floorUuid) {
intersectedFloor.point.z intersectPoint = new THREE.Vector3(intersectedFloor.point.x, intersectedFloor.object.userData.floorDepth, intersectedFloor.point.z);
) } else {
: new THREE.Vector3( intersectPoint = new THREE.Vector3(intersectedFloor.point.x, 0, intersectedFloor.point.z);
intersectedPlane.point.x, }
0, } else {
intersectedPlane.point.z intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
); }
} else if (intersectedFloor) { } else if (intersectedFloor) {
intersectPoint = new THREE.Vector3( intersectPoint = new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z);
intersectedFloor.point.x,
Math.round(intersectedFloor.point.y),
intersectedFloor.point.z
);
} else if (intersectedPlane) { } else if (intersectedPlane) {
intersectPoint = new THREE.Vector3( intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
intersectedPlane.point.x,
0,
intersectedPlane.point.z
);
} }
if (intersectPoint) { if (intersectPoint) {
if (intersectPoint.y < 0) { if (intersectPoint.y < 0) {
intersectPoint = new THREE.Vector3( intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z);
intersectPoint.x,
0,
intersectPoint.z
);
} }
const cachedModel = THREE.Cache.get(selectedItem.id); const cachedModel = THREE.Cache.get(selectedItem.id);
if (cachedModel) { if (cachedModel) {
handleModelLoad( handleModelLoad(cachedModel, intersectPoint!, selectedItem, addEvent, addAsset, socket, selectedVersion?.versionId || '', projectId, userId);
cachedModel,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
return; return;
} else { } else {
const cachedModelBlob = await retrieveGLTF(selectedItem.id); const cachedModelBlob = await retrieveGLTF(selectedItem.id);
@@ -108,38 +84,15 @@ async function addAssetModel(
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl); THREE.Cache.remove(blobUrl);
THREE.Cache.add(selectedItem.id, gltf); THREE.Cache.add(selectedItem.id, gltf);
handleModelLoad( handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket, selectedVersion?.versionId || '', projectId, userId);
gltf,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
}); });
} else { } else {
loader.load( loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`,
`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`,
async (gltf) => { async (gltf) => {
const modelBlob = await fetch( const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`).then((res) => res.blob());
`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`
).then((res) => res.blob());
await storeGLTF(selectedItem.id, modelBlob); await storeGLTF(selectedItem.id, modelBlob);
THREE.Cache.add(selectedItem.id, gltf); THREE.Cache.add(selectedItem.id, gltf);
await handleModelLoad( await handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket, selectedVersion?.versionId || '', projectId, userId);
gltf,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
} }
); );
} }
@@ -217,9 +170,7 @@ async function handleModelLoad(
if (!data || !data.points) return; if (!data || !data.points) return;
const eventData: any = { const eventData: any = { type: selectedItem.type, };
type: selectedItem.type,
};
if (selectedItem.type === "Conveyor") { if (selectedItem.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = { const ConveyorEvent: ConveyorEventSchema = {
@@ -279,10 +230,8 @@ async function handleModelLoad(
const nextPoint = ConveyorEvent.points[i + 1]; const nextPoint = ConveyorEvent.points[i + 1];
if (currentPoint.action.triggers.length > 0) { if (currentPoint.action.triggers.length > 0) {
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid;
nextPoint.uuid; currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid;
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid =
nextPoint.action.actionUuid;
} }
} }
addEvent(ConveyorEvent); addEvent(ConveyorEvent);

View File

@@ -59,9 +59,9 @@ function FloorInstance({ floor }: { floor: Floor }) {
receiveShadow receiveShadow
name={`Floor-${floor.floorUuid}`} name={`Floor-${floor.floorUuid}`}
rotation={[Math.PI / 2, 0, 0]} rotation={[Math.PI / 2, 0, 0]}
position={[0, floor.floorDepth, 0]} position={[0, !floor.isBeveled ? floor.floorDepth : (floor.floorDepth - 0.1), 0]}
userData={floor} userData={floor}
onClick={(e) => { onDoubleClick={(e) => {
if (!togglView && activeModule === 'builder') { if (!togglView && activeModule === 'builder') {
if (e.object.userData.floorUuid) { if (e.object.userData.floorUuid) {
setSelectedFloor(e.object); setSelectedFloor(e.object);
@@ -76,10 +76,14 @@ function FloorInstance({ floor }: { floor: Floor }) {
}} }}
> >
<Extrude <Extrude
name={`Floor-${floor.floorUuid}`}
args={[shape, { args={[shape, {
depth: floor.floorDepth, depth: !floor.isBeveled ? floor.floorDepth : (floor.floorDepth - 0.1),
bevelEnabled: floor.isBeveled, bevelEnabled: floor.isBeveled,
bevelThickness: floor.bevelStrength bevelSegments: floor.bevelStrength,
bevelOffset: -0.1,
bevelSize: 0.1,
bevelThickness: 0.1,
}]} }]}
userData={floor} userData={floor}
> >

View File

@@ -123,7 +123,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
rotation={[0, -angle, 0]} rotation={[0, -angle, 0]}
userData={wall} userData={wall}
name={`WallReference_${wall.wallUuid}`} name={`WallReference_${wall.wallUuid}`}
onClick={(e) => { onDoubleClick={(e) => {
if (visible && !togglView && activeModule === 'builder') { if (visible && !togglView && activeModule === 'builder') {
if (e.object.userData.wallUuid) { if (e.object.userData.wallUuid) {
setSelectedWall(e.object); setSelectedWall(e.object);

View File

@@ -134,14 +134,15 @@ function Floor({ room }: { room: Point[] }) {
if (!shape) return null; if (!shape) return null;
return ( return (
<group name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}> <mesh name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
<Extrude <Extrude
receiveShadow
name="Wall-Floor"
args={[shape, { depth: Constants.floorConfig.height, bevelEnabled: false }]} args={[shape, { depth: Constants.floorConfig.height, bevelEnabled: false }]}
position={[0, 0, 0]} position={[0, 0, 0]}
receiveShadow
> >
<meshStandardMaterial color={Constants.floorConfig.defaultColor} map={floorTexture} side={DoubleSide} /> <meshStandardMaterial color={Constants.floorConfig.defaultColor} map={floorTexture} side={DoubleSide} />
</Extrude> </Extrude>
</group> </mesh>
); );
} }

View File

@@ -12,8 +12,8 @@ import getClosestIntersection from '../../geomentries/lines/getClosestIntersecti
import ReferencePoint from '../../point/reference/referencePoint'; import ReferencePoint from '../../point/reference/referencePoint';
import ReferenceWall from './referenceWall'; import ReferenceWall from './referenceWall';
import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi'; // import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
import { deleteWallApi } from '../../../../services/factoryBuilder/wall/deleteWallApi'; // import { deleteWallApi } from '../../../../services/factoryBuilder/wall/deleteWallApi';
function WallCreator() { function WallCreator() {
const { scene, camera, raycaster, gl, pointer } = useThree(); const { scene, camera, raycaster, gl, pointer } = useThree();

View File

@@ -16,38 +16,43 @@ interface DistanceFindingControlsProps {
object: number; object: number;
} }
const material = new LineBasicMaterial({ color: "#d2baff" });
const DIRECTION_LABEL_MAP = {
textPosX: "textPosX",
textNegX: "textNegX",
textPosZ: "textPosZ",
textNegZ: "textNegZ",
} as const;
const DistanceFindingControls = ({ const DistanceFindingControls = ({
boundingBoxRef, boundingBoxRef,
object, object,
}: DistanceFindingControlsProps) => { }: DistanceFindingControlsProps) => {
const { camera, scene } = useThree(); const { camera, scene } = useThree();
const [labelValues, setLabelValues] = useState<{ const [labelValues, setLabelValues] = useState<Record<string, string>>({
textPosX: any;
textNegX: any;
textPosZ: any;
textNegZ: any;
}>({
textPosX: "", textPosX: "",
textNegX: "", textNegX: "",
textPosZ: "", textPosZ: "",
textNegZ: "", textNegZ: "",
}); });
// Refs for measurement lines const lineRefs = {
const line1 = useRef<Line>(null); posX: useRef<Line>(null),
const line2 = useRef<Line>(null); negX: useRef<Line>(null),
const line3 = useRef<Line>(null); posZ: useRef<Line>(null),
const line4 = useRef<Line>(null); negZ: useRef<Line>(null),
const line5 = useRef<Line>(null); posY: useRef<Line>(null),
};
// Refs for measurement text labels const textRefs = {
const textPosX = useRef<Group>(null); textPosX: useRef<Group>(null),
const textNegX = useRef<Group>(null); textNegX: useRef<Group>(null),
const textPosZ = useRef<Group>(null); textPosZ: useRef<Group>(null),
const textNegZ = useRef<Group>(null); textNegZ: useRef<Group>(null),
const textPosY = useRef<Group>(null); textPosY: useRef<Group>(null),
};
// Store line geometries to avoid recreation
const lineGeometries = useRef({ const lineGeometries = useRef({
posX: new BufferGeometry(), posX: new BufferGeometry(),
negX: new BufferGeometry(), negX: new BufferGeometry(),
@@ -57,11 +62,11 @@ const DistanceFindingControls = ({
}); });
useFrame(() => { useFrame(() => {
if (!boundingBoxRef?.current) return; const bboxMesh = boundingBoxRef?.current;
if (!bboxMesh) return;
boundingBoxRef.current.geometry.computeBoundingBox();
const bbox = boundingBoxRef.current.geometry.boundingBox;
bboxMesh.geometry.computeBoundingBox();
const bbox = bboxMesh.geometry.boundingBox;
if (!bbox) return; if (!bbox) return;
const size = { const size = {
@@ -70,97 +75,45 @@ const DistanceFindingControls = ({
z: bbox.max.z - bbox.min.z, z: bbox.max.z - bbox.min.z,
}; };
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone(); const center = bboxMesh.getWorldPosition(new Vector3()).clone();
if (!center) return;
if (!vec) return; updateLine("posX", new Vector3(1, 0, 0), "pos", size, center);
updateLine({ updateLine("negX", new Vector3(-1, 0, 0), "neg", size, center);
line: line1.current, updateLine("posZ", new Vector3(0, 0, 1), "pos", size, center);
geometry: lineGeometries.current.posX, updateLine("negZ", new Vector3(0, 0, -1), "neg", size, center);
direction: new Vector3(1, 0, 0), // Positive X updateLine("posY", new Vector3(0, -1, 0), "posY", size, center);
angle: "pos",
mesh: textPosX,
vec,
size,
});
updateLine({
line: line2.current,
geometry: lineGeometries.current.negX,
direction: new Vector3(-1, 0, 0), // Negative X
angle: "neg",
mesh: textNegX,
vec,
size,
});
updateLine({
line: line3.current,
geometry: lineGeometries.current.posZ,
direction: new Vector3(0, 0, 1), // Positive Z
angle: "pos",
mesh: textPosZ,
vec,
size,
});
updateLine({
line: line4.current,
geometry: lineGeometries.current.negZ,
direction: new Vector3(0, 0, -1), // Negative Z
angle: "neg",
mesh: textNegZ,
vec,
size,
});
updateLine({
line: line5.current,
geometry: lineGeometries.current.posY,
direction: new Vector3(0, -1, 0), // Down (Y)
angle: "posY",
mesh: textPosY,
vec,
size,
});
}); });
const updateLine = ({ const updateLine = (
line, key: keyof typeof lineRefs,
geometry, direction: Vector3,
direction, angle: string,
angle, size: { x: number; y: number; z: number },
mesh, origin: Vector3
vec, ) => {
size, const line = lineRefs[key].current;
}: { const geometry = lineGeometries.current[key];
line: Line | null; const mesh = textRefs[`text${key[0].toUpperCase() + key.slice(1)}` as keyof typeof textRefs];
geometry: BufferGeometry;
direction: Vector3;
angle: string;
mesh: React.RefObject<Group>;
vec: Vector3;
size: { x: number; y: number; z: number };
}) => {
if (!line) return; if (!line) return;
const points = []; const points: Vector3[] = [];
const halfSize = new Vector3(size.x / 2, size.y / 2, size.z / 2);
if (angle === "pos") { if (angle === "pos") {
points[0] = new Vector3(vec.x, vec.y, vec.z).add( points[0] = origin.clone().add(direction.clone().multiply(halfSize));
new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2)
);
} else if (angle === "neg") { } else if (angle === "neg") {
points[0] = new Vector3(vec.x, vec.y, vec.z).sub( points[0] = origin.clone().sub(direction.clone().multiply(halfSize));
new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2)
);
} else if (angle === "posY") { } else if (angle === "posY") {
points[0] = new Vector3(vec.x, vec.y, vec.z).sub( points[0] = origin.clone().sub(new Vector3(0, size.y / 2, 0));
new Vector3(0, size.y / 2, 0)
);
} }
const ray = new Raycaster(); const ray = new Raycaster();
if (camera) ray.camera = camera; ray.camera = camera;
ray.set(new Vector3(vec.x, vec.y, vec.z), direction); ray.set(origin, direction);
ray.params.Line.threshold = 0.1; ray.params.Line.threshold = 0.1;
// Find intersection points
const wallsGroup = scene.children.find((val) => const wallsGroup = scene.children.find((val) =>
val?.name.includes("Walls") val?.name.includes("Walls")
); );
@@ -168,170 +121,86 @@ const DistanceFindingControls = ({
? ray.intersectObjects([wallsGroup], true) ? ray.intersectObjects([wallsGroup], true)
: []; : [];
// Find intersection point const intersect = intersects.find((i) =>
if (intersects[0]) { i.object.name.includes("Wall")
for (const intersect of intersects) { );
if (intersect.object.name.includes("Wall")) {
points[1] = if (intersect) {
angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor points[1] = angle !== "posY" ? intersect.point : new Vector3(origin.x, 0, origin.z);
break;
}
}
} }
if (points[1]) { if (points[1]) {
geometry.dispose(); geometry.dispose();
geometry.setFromPoints([points[0], points[1]]); geometry.setFromPoints(points);
line.geometry = geometry; line.geometry = geometry;
// Calculate the distance only once
const distance = points[0].distanceTo(points[1]).toFixed(2); const distance = points[0].distanceTo(points[1]).toFixed(2);
// Update measurement text
if (mesh?.current) { if (mesh?.current) {
geometry.computeBoundingSphere(); geometry.computeBoundingSphere();
const center = geometry.boundingSphere?.center; mesh.current.position.copy(geometry.boundingSphere!.center);
if (center) {
mesh.current.position.copy(center);
}
const label = document.getElementById(mesh.current.name); const labelEl = document.getElementById(mesh.current.name);
if (label) { if (labelEl) {
label.innerText = `${distance}m`; labelEl.innerText = `${distance}m`;
// Update specific label state based on the label ID if (DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) {
switch (label.id) { setLabelValues((prev) => ({
case "textPosX": ...prev,
setLabelValues((prevState) => ({ ...prevState, textPosX: distance })); [labelEl.id]: distance,
break; }));
case "textNegX":
setLabelValues((prevState) => ({ ...prevState, textNegX: distance }));
break;
case "textPosZ":
setLabelValues((prevState) => ({ ...prevState, textPosZ: distance }));
break;
case "textNegZ":
setLabelValues((prevState) => ({ ...prevState, textNegZ: distance }));
break;
default:
break;
} }
} }
} }
} else { } else {
// No intersection found - clear the line
geometry.dispose(); geometry.dispose();
geometry.setFromPoints([new Vector3(), new Vector3()]); geometry.setFromPoints([new Vector3(), new Vector3()]);
line.geometry = geometry; line.geometry = geometry;
const label = document.getElementById(mesh?.current?.name ?? ""); const labelEl = document.getElementById(mesh?.current?.name ?? "");
if (label) { if (labelEl && DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) {
label.innerText = ""; labelEl.innerText = "";
setLabelValues((prev) => ({
// Clear the corresponding label value in the state ...prev,
switch (label.id) { [labelEl.id]: "",
case "textPosX": }));
setLabelValues((prevState) => ({ ...prevState, textPosX: "" }));
break;
case "textNegX":
setLabelValues((prevState) => ({ ...prevState, textNegX: "" }));
break;
case "textPosZ":
setLabelValues((prevState) => ({ ...prevState, textPosZ: "" }));
break;
case "textNegZ":
setLabelValues((prevState) => ({ ...prevState, textNegZ: "" }));
break;
default:
break;
} }
} }
}
}; };
const Material = new LineBasicMaterial({ color: "#d2baff" }); const renderLabel = (id: keyof typeof textRefs) => (
<group name={id} ref={textRefs[id]}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues[id] === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id={id}>{labelValues[id]}</div>
</Html>
</group>
);
return ( return (
<> <>
{/* Measurement text labels */}
{boundingBoxRef.current && object > 0 && ( {boundingBoxRef.current && object > 0 && (
<> <group
<group name="textPosX" ref={textPosX}> name="DistanceFindingControls"
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textPosX === "" ? "hidden" : "visible",
}}
> >
<div className="distance-label" id="textPosX">{labelValues.textPosX}</div> {renderLabel("textPosX")}
</Html> {renderLabel("textNegX")}
</group> {renderLabel("textPosZ")}
{renderLabel("textNegZ")}
<group name="textNegX" ref={textNegX}> <primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.posX} />
<Html <primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.negX} />
wrapperClass="distance-text-wrapper" <primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.posZ} />
className="distance-text" <primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.negZ} />
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textNegX === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id="textNegX">{labelValues.textNegX}</div>
</Html>
</group> </group>
)}
<group name="textPosZ" ref={textPosZ}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[2, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textPosZ === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id="textPosZ">{labelValues.textPosZ}</div>
</Html>
</group>
<group name="textNegZ" ref={textNegZ}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textNegZ === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id="textNegZ">{labelValues.textNegZ}</div>
</Html>
</group>
{/* Measurement lines */}
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line1}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line2}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line3}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line4}
/>
</>
)
}
</> </>
); );
}; };

View File

@@ -55,7 +55,7 @@ interface BuilderState {
// Setters - Floor // Setters - Floor
setSelectedFloor: (floor: Object3D | null) => void; setSelectedFloor: (floor: Object3D | null) => void;
setFloorDepth: (depth: number) => void; setFloorDepth: (depth: number) => void;
setBeveled: (isBeveled: boolean) => void; setIsBeveled: (isBeveled: boolean) => void;
setBevelStrength: (strength: number) => void; setBevelStrength: (strength: number) => void;
setFloorMaterial: (material: string, side: 'side' | 'top') => void; setFloorMaterial: (material: string, side: 'side' | 'top') => void;
@@ -99,7 +99,7 @@ export const useBuilderStore = create<BuilderState>()(
selectedFloor: null, selectedFloor: null,
floorDepth: 0.1, floorDepth: 0.1,
isBeveled: false, isBeveled: false,
bevelStrength: 0.05, bevelStrength: 5,
sideMaterial: 'Material 1', sideMaterial: 'Material 1',
topMaterial: 'Default Material', topMaterial: 'Default Material',
@@ -182,7 +182,7 @@ export const useBuilderStore = create<BuilderState>()(
}); });
}, },
setBeveled: (isBeveled: boolean) => { setIsBeveled: (isBeveled: boolean) => {
set((state) => { set((state) => {
state.isBeveled = isBeveled; state.isBeveled = isBeveled;
}); });

View File

@@ -5,7 +5,7 @@ interface FloorStore {
floors: Floor[]; floors: Floor[];
setFloors: (floors: Floor[]) => void; setFloors: (floors: Floor[]) => void;
addFloor: (floor: Floor) => void; addFloor: (floor: Floor) => void;
updateFloor: (uuid: string, updated: Partial<Floor>) => void; updateFloor: (uuid: string, updated: Partial<Floor>) => Floor | undefined;
removeFloor: (uuid: string) => void; removeFloor: (uuid: string) => void;
removePoint: (pointUuid: string) => { removedFloors: Floor[], updatedFloors: Floor[] }; removePoint: (pointUuid: string) => { removedFloors: Floor[], updatedFloors: Floor[] };
removeFloorByPoints: (Points: [Point, Point]) => { removedFloors: Floor[], updatedFloors: Floor[] }; removeFloorByPoints: (Points: [Point, Point]) => { removedFloors: Floor[], updatedFloors: Floor[] };
@@ -45,12 +45,17 @@ export const createFloorStore = () => {
state.floors.push(floor); state.floors.push(floor);
}), }),
updateFloor: (uuid, updated) => set(state => { updateFloor: (uuid, updated) => {
let updatedFloor: Floor | undefined;
set(state => {
const floor = state.floors.find(f => f.floorUuid === uuid); const floor = state.floors.find(f => f.floorUuid === uuid);
if (floor) { if (floor) {
Object.assign(floor, updated); Object.assign(floor, updated);
updatedFloor = JSON.parse(JSON.stringify(floor));
} }
}), });
return updatedFloor;
},
removeFloor: (uuid) => set(state => { removeFloor: (uuid) => set(state => {
state.floors = state.floors.filter(f => f.floorUuid !== uuid); state.floors = state.floors.filter(f => f.floorUuid !== uuid);