completed floor
This commit is contained in:
@@ -1,27 +1,28 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
import useModuleStore, {
|
import useModuleStore, {
|
||||||
useSubModuleStore,
|
useSubModuleStore,
|
||||||
} from "../../../store/useModuleStore";
|
} from "../../../store/useModuleStore";
|
||||||
import {
|
import {
|
||||||
AnalysisIcon,
|
AnalysisIcon,
|
||||||
MechanicsIcon,
|
MechanicsIcon,
|
||||||
PropertiesIcon,
|
PropertiesIcon,
|
||||||
SimulationIcon,
|
SimulationIcon,
|
||||||
} from "../../icons/SimulationIcons";
|
} from "../../icons/SimulationIcons";
|
||||||
import { useToggleStore } from "../../../store/useUIToggleStore";
|
import { useToggleStore } from "../../../store/useUIToggleStore";
|
||||||
import Visualization from "./visualization/Visualization";
|
import Visualization from "./visualization/Visualization";
|
||||||
import Analysis from "./analysis/Analysis";
|
import Analysis from "./analysis/Analysis";
|
||||||
import Simulations from "./simulation/Simulations";
|
import Simulations from "./simulation/Simulations";
|
||||||
import useVersionHistoryVisibleStore, {
|
import useVersionHistoryVisibleStore, {
|
||||||
useSaveVersion,
|
useSaveVersion,
|
||||||
useSelectedFloorItem,
|
useSelectedFloorItem,
|
||||||
useToolMode,
|
useToolMode,
|
||||||
} from "../../../store/builder/store";
|
} from "../../../store/builder/store";
|
||||||
import {
|
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,207 +30,227 @@ 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();
|
||||||
const { toggleUIRight } = useToggleStore();
|
const { toggleUIRight } = useToggleStore();
|
||||||
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();
|
||||||
const { isVersionSaved } = useSaveVersion();
|
const { isVersionSaved } = useSaveVersion();
|
||||||
|
|
||||||
// Reset activeList whenever activeModule changes
|
// Reset activeList whenever activeModule changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeModule !== "simulation") setSubModule("properties");
|
if (activeModule !== "simulation") setSubModule("properties");
|
||||||
if (activeModule === "simulation") setSubModule("simulations");
|
if (activeModule === "simulation") setSubModule("simulations");
|
||||||
}, [activeModule, setSubModule]);
|
}, [activeModule, setSubModule]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
activeModule !== "mechanics" &&
|
activeModule !== "mechanics" &&
|
||||||
selectedEventData &&
|
selectedEventData &&
|
||||||
selectedEventSphere
|
selectedEventSphere
|
||||||
) {
|
) {
|
||||||
setSubModule("mechanics");
|
setSubModule("mechanics");
|
||||||
} else if (!selectedEventData && !selectedEventSphere) {
|
} else if (!selectedEventData && !selectedEventSphere) {
|
||||||
if (activeModule === "simulation") {
|
if (activeModule === "simulation") {
|
||||||
setSubModule("simulations");
|
setSubModule("simulations");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (activeModule !== "simulation") {
|
if (activeModule !== "simulation") {
|
||||||
setSubModule("properties");
|
setSubModule("properties");
|
||||||
}
|
}
|
||||||
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`sidebar-right-wrapper ${toggleUIRight && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"
|
className={`sidebar-right-wrapper ${toggleUIRight && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Header />
|
<Header />
|
||||||
{toggleUIRight && (
|
{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" && (
|
|
||||||
<>
|
<>
|
||||||
<button
|
{!isVersionSaved && (
|
||||||
id="sidebar-action-list-simulation"
|
<div className="sidebar-actions-container">
|
||||||
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
|
{activeModule !== "simulation" && (
|
||||||
}`}
|
<button
|
||||||
onClick={() => {
|
id="sidebar-action-list-properties"
|
||||||
setSubModule("simulations");
|
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
|
||||||
setVersionHistoryVisible(false);
|
}`}
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
setSubModule("properties");
|
||||||
<div className="tooltip">simulations</div>
|
setVersionHistoryVisible(false);
|
||||||
<SimulationIcon isActive={subModule === "simulations"} />
|
}}
|
||||||
</button>
|
>
|
||||||
<button
|
<div className="tooltip">properties</div>
|
||||||
id="sidebar-action-list-mechanics"
|
<PropertiesIcon isActive={subModule === "properties"} />
|
||||||
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
|
</button>
|
||||||
}`}
|
)}
|
||||||
onClick={() => {
|
{activeModule === "simulation" && (
|
||||||
setSubModule("mechanics");
|
<>
|
||||||
setVersionHistoryVisible(false);
|
<button
|
||||||
}}
|
id="sidebar-action-list-simulation"
|
||||||
>
|
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
|
||||||
<div className="tooltip">mechanics</div>
|
}`}
|
||||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
onClick={() => {
|
||||||
</button>
|
setSubModule("simulations");
|
||||||
<button
|
setVersionHistoryVisible(false);
|
||||||
id="sidebar-action-list-analysis"
|
}}
|
||||||
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
|
>
|
||||||
}`}
|
<div className="tooltip">simulations</div>
|
||||||
onClick={() => {
|
<SimulationIcon isActive={subModule === "simulations"} />
|
||||||
setSubModule("analysis");
|
</button>
|
||||||
setVersionHistoryVisible(false);
|
<button
|
||||||
}}
|
id="sidebar-action-list-mechanics"
|
||||||
>
|
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
|
||||||
<div className="tooltip">analysis</div>
|
}`}
|
||||||
<AnalysisIcon isActive={subModule === "analysis"} />
|
onClick={() => {
|
||||||
</button>
|
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>
|
|
||||||
)}
|
)}
|
||||||
|
</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>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SideBarRight;
|
export default SideBarRight;
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -10,163 +10,152 @@ import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
|||||||
|
|
||||||
// Define Material type
|
// Define Material type
|
||||||
type Material = {
|
type Material = {
|
||||||
texture: string;
|
texture: string;
|
||||||
textureName: string;
|
textureId: 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[]>([
|
const [selectedMaterials, setSelectedMaterials] = useState<{
|
||||||
defaultMaterial,
|
side1: Material | null;
|
||||||
initialMaterial,
|
side2: Material | null;
|
||||||
]);
|
}>({
|
||||||
|
side1: null,
|
||||||
const [selectedMaterials, setSelectedMaterials] = useState<{
|
side2: null,
|
||||||
side1: Material | null;
|
|
||||||
side2: Material | null;
|
|
||||||
}>({
|
|
||||||
side1: null,
|
|
||||||
side2: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set default material initially for both sides
|
|
||||||
useEffect(() => {
|
|
||||||
setSelectedMaterials({
|
|
||||||
side1: defaultMaterial,
|
|
||||||
side2: defaultMaterial,
|
|
||||||
});
|
});
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleHeightChange = (newValue: string) => {
|
useEffect(() => {
|
||||||
setWallHeight(parseFloat(newValue));
|
setSelectedMaterials({
|
||||||
};
|
side1: materials.find((mat) => mat.textureId === outsideMaterial) || null,
|
||||||
|
side2: materials.find((mat) => mat.textureId === insideMaterial) || null,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleThicknessChange = (newValue: string) => {
|
const handleHeightChange = (newValue: string) => {
|
||||||
setWallThickness(parseFloat(newValue));
|
setWallHeight(parseFloat(newValue));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectMaterial = (material: Material) => {
|
const handleThicknessChange = (newValue: string) => {
|
||||||
setSelectedMaterials((prev) => ({
|
setWallThickness(parseFloat(newValue));
|
||||||
...prev,
|
};
|
||||||
[activeSide]: material,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const handleSelectMaterial = (material: Material) => {
|
||||||
<div className="wall-properties-container">
|
setSelectedMaterials((prev) => ({
|
||||||
<section className="wall-properties-section">
|
...prev,
|
||||||
<div className="header">Wall</div>
|
[activeSide]: material,
|
||||||
<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>
|
|
||||||
|
|
||||||
<div className="material-preview">
|
return (
|
||||||
<div className="sides-wrapper">
|
<div className="wall-properties-container">
|
||||||
<button
|
<section className="wall-properties-section">
|
||||||
className={`side-wrapper ${activeSide === "side1" ? "active" : ""}`}
|
<div className="header">Wall</div>
|
||||||
onClick={() => setActiveSide("side1")}
|
<div className="wall-properties">
|
||||||
>
|
<InputWithDropDown
|
||||||
<div className="label">Side 1</div>
|
label="Height"
|
||||||
<div className="texture-image">
|
value={`${wallHeight}`}
|
||||||
{selectedMaterials.side1 && (
|
onChange={(val) => handleHeightChange(val)}
|
||||||
<img
|
/>
|
||||||
draggable={false}
|
<InputWithDropDown
|
||||||
src={selectedMaterials.side1.texture}
|
label="Thickness"
|
||||||
alt={selectedMaterials.side1.textureName}
|
value={`${wallThickness}`}
|
||||||
/>
|
onChange={(val) => handleThicknessChange(val)}
|
||||||
)}
|
/>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</section>
|
||||||
|
<section>
|
||||||
|
<div className="header-wrapper">
|
||||||
|
<div className="header">Materials</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<div className="material-preview">
|
||||||
className={`side-wrapper ${activeSide === "side2" ? "active" : ""}`}
|
<div className="sides-wrapper">
|
||||||
onClick={() => setActiveSide("side2")}
|
<button
|
||||||
>
|
className={`side-wrapper ${activeSide === "side1" ? "active" : ""}`}
|
||||||
<div className="label">Side 2</div>
|
onClick={() => setActiveSide("side1")}
|
||||||
<div className="texture-image">
|
>
|
||||||
{selectedMaterials.side2 && (
|
<div className="label">Side 1</div>
|
||||||
<img
|
<div className="texture-image">
|
||||||
draggable={false}
|
{selectedMaterials.side1 && (
|
||||||
src={selectedMaterials.side2.texture}
|
<img
|
||||||
alt={selectedMaterials.side2.textureName}
|
draggable={false}
|
||||||
/>
|
src={selectedMaterials.side1.texture}
|
||||||
)}
|
alt={selectedMaterials.side1.textureName}
|
||||||
</div>
|
/>
|
||||||
</button>
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className="preview">
|
<button
|
||||||
{selectedMaterials[activeSide] && (
|
className={`side-wrapper ${activeSide === "side2" ? "active" : ""}`}
|
||||||
<img
|
onClick={() => setActiveSide("side2")}
|
||||||
draggable={false}
|
>
|
||||||
src={selectedMaterials[activeSide]!.texture}
|
<div className="label">Side 2</div>
|
||||||
alt={selectedMaterials[activeSide]!.textureName}
|
<div className="texture-image">
|
||||||
/>
|
{selectedMaterials.side2 && (
|
||||||
)}
|
<img
|
||||||
</div>
|
draggable={false}
|
||||||
</div>
|
src={selectedMaterials.side2.texture}
|
||||||
|
alt={selectedMaterials.side2.textureName}
|
||||||
<div className="materials">
|
/>
|
||||||
{materials.length === 0 ? (
|
)}
|
||||||
<div className="no-materials">No materials added yet.</div>
|
</div>
|
||||||
) : (
|
</button>
|
||||||
<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>
|
</div>
|
||||||
</button>
|
|
||||||
);
|
<div className="preview">
|
||||||
})}
|
{selectedMaterials[activeSide] && (
|
||||||
</div>
|
<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>
|
</div>
|
||||||
</section>
|
);
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WallProperties;
|
export default WallProperties;
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,489 +10,438 @@ import PointsCalculator from "../../../simulation/events/points/functions/points
|
|||||||
import { getUserData } from "../../../../functions/getUserData";
|
import { getUserData } from "../../../../functions/getUserData";
|
||||||
|
|
||||||
async function addAssetModel(
|
async function addAssetModel(
|
||||||
raycaster: THREE.Raycaster,
|
scene: THREE.Scene,
|
||||||
camera: THREE.Camera,
|
raycaster: THREE.Raycaster,
|
||||||
pointer: THREE.Vector2,
|
camera: THREE.Camera,
|
||||||
floorGroup: Types.RefGroup,
|
pointer: THREE.Vector2,
|
||||||
socket: Socket<any>,
|
socket: Socket<any>,
|
||||||
selectedItem: any,
|
selectedItem: any,
|
||||||
setSelectedItem: any,
|
setSelectedItem: any,
|
||||||
addEvent: (event: EventsSchema) => void,
|
addEvent: (event: EventsSchema) => void,
|
||||||
addAsset: (asset: Asset) => void,
|
addAsset: (asset: Asset) => void,
|
||||||
plane: Types.RefMesh,
|
plane: Types.RefMesh,
|
||||||
selectedVersion?: Version | null,
|
selectedVersion?: Version | null,
|
||||||
projectId?: string,
|
projectId?: string,
|
||||||
userId?: string
|
userId?: string
|
||||||
): Promise<void> {
|
): 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 {
|
try {
|
||||||
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];
|
||||||
|
|
||||||
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) {
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
} 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(
|
async function handleModelLoad(
|
||||||
gltf: any,
|
gltf: any,
|
||||||
intersectPoint: THREE.Vector3,
|
intersectPoint: THREE.Vector3,
|
||||||
selectedItem: any,
|
selectedItem: any,
|
||||||
addEvent: (event: EventsSchema) => void,
|
addEvent: (event: EventsSchema) => void,
|
||||||
addAsset: (asset: Asset) => void,
|
addAsset: (asset: Asset) => void,
|
||||||
socket: Socket<any>,
|
socket: Socket<any>,
|
||||||
versionId: string,
|
versionId: string,
|
||||||
projectId?: string,
|
projectId?: string,
|
||||||
userId?: string
|
userId?: string
|
||||||
) {
|
) {
|
||||||
const { organization } = getUserData();
|
const { organization } = getUserData();
|
||||||
const model = gltf.scene.clone();
|
const model = gltf.scene.clone();
|
||||||
model.userData = {
|
model.userData = {
|
||||||
name: selectedItem.name,
|
name: selectedItem.name,
|
||||||
modelId: selectedItem.id,
|
modelId: selectedItem.id,
|
||||||
modelUuid: model.uuid,
|
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,
|
|
||||||
};
|
};
|
||||||
|
model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z);
|
||||||
|
model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap);
|
||||||
|
|
||||||
if (selectedItem.type === "Conveyor") {
|
model.traverse((child: any) => {
|
||||||
const ConveyorEvent: ConveyorEventSchema = {
|
if (child) {
|
||||||
modelUuid: newFloorItem.modelUuid,
|
child.castShadow = true;
|
||||||
modelName: newFloorItem.modelName,
|
child.receiveShadow = true;
|
||||||
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) => ({
|
const newFloorItem: Asset = {
|
||||||
uuid: point.uuid,
|
modelUuid: model.uuid,
|
||||||
position: point.position,
|
modelName: selectedItem.name,
|
||||||
rotation: point.rotation,
|
assetId: selectedItem.id,
|
||||||
}));
|
position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z],
|
||||||
} else if (selectedItem.type === "Vehicle") {
|
rotation: [0, 0, 0],
|
||||||
const vehicleEvent: VehicleEventSchema = {
|
isLocked: false,
|
||||||
modelUuid: newFloorItem.modelUuid,
|
isVisible: true,
|
||||||
modelName: newFloorItem.modelName,
|
isCollidable: false,
|
||||||
position: newFloorItem.position,
|
opacity: 1,
|
||||||
rotation: newFloorItem.rotation,
|
};
|
||||||
state: "idle",
|
|
||||||
type: "vehicle",
|
|
||||||
speed: 1,
|
// API
|
||||||
point: {
|
|
||||||
uuid: THREE.MathUtils.generateUUID(),
|
// await setAssetsApi(
|
||||||
position: [data.points[0].x, data.points[0].y, data.points[0].z],
|
// organization,
|
||||||
rotation: [0, 0, 0],
|
// newFloorItem.modelUuid,
|
||||||
action: {
|
// newFloorItem.modelName,
|
||||||
actionUuid: THREE.MathUtils.generateUUID(),
|
// newFloorItem.assetId,
|
||||||
actionName: "Action 1",
|
// newFloorItem.position,
|
||||||
actionType: "travel",
|
// { x: 0, y: 0, z: 0 },
|
||||||
unLoadDuration: 5,
|
// false,
|
||||||
loadCapacity: 1,
|
// true,
|
||||||
steeringAngle: 0,
|
// );
|
||||||
pickUpPoint: null,
|
|
||||||
unLoadPoint: null,
|
// SOCKET
|
||||||
triggers: [],
|
|
||||||
},
|
if (selectedItem.type) {
|
||||||
},
|
const data = PointsCalculator(
|
||||||
};
|
selectedItem.type,
|
||||||
addEvent(vehicleEvent);
|
gltf.scene.clone(),
|
||||||
eventData.point = {
|
new THREE.Vector3(...model.rotation)
|
||||||
uuid: vehicleEvent.point.uuid,
|
);
|
||||||
position: vehicleEvent.point.position,
|
|
||||||
rotation: vehicleEvent.point.rotation,
|
if (!data || !data.points) return;
|
||||||
};
|
|
||||||
} else if (selectedItem.type === "ArmBot") {
|
const eventData: any = { type: selectedItem.type, };
|
||||||
const roboticArmEvent: RoboticArmEventSchema = {
|
|
||||||
modelUuid: newFloorItem.modelUuid,
|
if (selectedItem.type === "Conveyor") {
|
||||||
modelName: newFloorItem.modelName,
|
const ConveyorEvent: ConveyorEventSchema = {
|
||||||
position: newFloorItem.position,
|
modelUuid: newFloorItem.modelUuid,
|
||||||
rotation: newFloorItem.rotation,
|
modelName: newFloorItem.modelName,
|
||||||
state: "idle",
|
position: newFloorItem.position,
|
||||||
type: "roboticArm",
|
rotation: newFloorItem.rotation,
|
||||||
speed: 1,
|
state: "idle",
|
||||||
point: {
|
type: "transfer",
|
||||||
uuid: THREE.MathUtils.generateUUID(),
|
speed: 1,
|
||||||
position: [data.points[0].x, data.points[0].y, data.points[0].z],
|
points: data.points.map((point: THREE.Vector3, index: number) => {
|
||||||
rotation: [0, 0, 0],
|
const triggers: TriggerSchema[] = [];
|
||||||
actions: [
|
|
||||||
{
|
if (data.points && index < data.points.length - 1) {
|
||||||
actionUuid: THREE.MathUtils.generateUUID(),
|
triggers.push({
|
||||||
actionName: "Action 1",
|
triggerUuid: THREE.MathUtils.generateUUID(),
|
||||||
actionType: "pickAndPlace",
|
triggerName: `Trigger 1`,
|
||||||
process: {
|
triggerType: "onComplete",
|
||||||
startPoint: null,
|
delay: 0,
|
||||||
endPoint: null,
|
triggeredAsset: {
|
||||||
},
|
triggeredModel: {
|
||||||
triggers: [],
|
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,
|
||||||
},
|
},
|
||||||
],
|
isLocked: false,
|
||||||
},
|
isVisible: true,
|
||||||
};
|
socketId: socket.id,
|
||||||
addEvent(roboticArmEvent);
|
eventData: eventData,
|
||||||
eventData.point = {
|
versionId: versionId,
|
||||||
uuid: roboticArmEvent.point.uuid,
|
projectId: projectId,
|
||||||
position: roboticArmEvent.point.position,
|
userId: userId,
|
||||||
rotation: roboticArmEvent.point.rotation,
|
};
|
||||||
};
|
|
||||||
} else if (selectedItem.type === "StaticMachine") {
|
socket.emit("v1:model-asset:add", completeData);
|
||||||
const machineEvent: MachineEventSchema = {
|
|
||||||
modelUuid: newFloorItem.modelUuid,
|
const asset: Asset = {
|
||||||
modelName: newFloorItem.modelName,
|
modelUuid: completeData.modelUuid,
|
||||||
position: newFloorItem.position,
|
modelName: completeData.modelName,
|
||||||
rotation: newFloorItem.rotation,
|
assetId: completeData.assetId,
|
||||||
state: "idle",
|
position: completeData.position,
|
||||||
type: "machine",
|
rotation: [
|
||||||
point: {
|
completeData.rotation.x,
|
||||||
uuid: THREE.MathUtils.generateUUID(),
|
completeData.rotation.y,
|
||||||
position: [data.points[0].x, data.points[0].y, data.points[0].z],
|
completeData.rotation.z,
|
||||||
rotation: [0, 0, 0],
|
] as [number, number, number],
|
||||||
action: {
|
isLocked: completeData.isLocked,
|
||||||
actionUuid: THREE.MathUtils.generateUUID(),
|
isCollidable: false,
|
||||||
actionName: "Action 1",
|
isVisible: completeData.isVisible,
|
||||||
actionType: "process",
|
opacity: 1,
|
||||||
processTime: 10,
|
eventData: completeData.eventData,
|
||||||
swapMaterial: "Default Material",
|
};
|
||||||
triggers: [],
|
|
||||||
},
|
addAsset(asset);
|
||||||
},
|
} else {
|
||||||
};
|
const data = {
|
||||||
addEvent(machineEvent);
|
organization,
|
||||||
eventData.point = {
|
modelUuid: newFloorItem.modelUuid,
|
||||||
uuid: machineEvent.point.uuid,
|
modelName: newFloorItem.modelName,
|
||||||
position: machineEvent.point.position,
|
assetId: newFloorItem.assetId,
|
||||||
rotation: machineEvent.point.rotation,
|
position: newFloorItem.position,
|
||||||
};
|
rotation: {
|
||||||
} else if (selectedItem.type === "Storage") {
|
x: model.rotation.x,
|
||||||
const storageEvent: StorageEventSchema = {
|
y: model.rotation.y,
|
||||||
modelUuid: newFloorItem.modelUuid,
|
z: model.rotation.z,
|
||||||
modelName: newFloorItem.modelName,
|
},
|
||||||
position: newFloorItem.position,
|
isLocked: false,
|
||||||
rotation: newFloorItem.rotation,
|
isVisible: true,
|
||||||
state: "idle",
|
socketId: socket.id,
|
||||||
type: "storageUnit",
|
versionId: versionId,
|
||||||
point: {
|
projectId: projectId,
|
||||||
uuid: THREE.MathUtils.generateUUID(),
|
userId: userId,
|
||||||
position: [data.points[0].x, data.points[0].y, data.points[0].z],
|
};
|
||||||
rotation: [0, 0, 0],
|
|
||||||
action: {
|
socket.emit("v1:model-asset:add", data);
|
||||||
actionUuid: THREE.MathUtils.generateUUID(),
|
|
||||||
actionName: "Action 1",
|
const asset = {
|
||||||
actionType: "store",
|
modelUuid: data.modelUuid,
|
||||||
storageCapacity: 10,
|
modelName: data.modelName,
|
||||||
triggers: [],
|
assetId: data.assetId,
|
||||||
},
|
position: data.position,
|
||||||
},
|
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [
|
||||||
};
|
number,
|
||||||
addEvent(storageEvent);
|
number,
|
||||||
eventData.point = {
|
number
|
||||||
uuid: storageEvent.point.uuid,
|
],
|
||||||
position: storageEvent.point.position,
|
isLocked: data.isLocked,
|
||||||
rotation: storageEvent.point.rotation,
|
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;
|
export default addAssetModel;
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -1,339 +1,208 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Vector3,
|
Vector3,
|
||||||
Raycaster,
|
Raycaster,
|
||||||
BufferGeometry,
|
BufferGeometry,
|
||||||
LineBasicMaterial,
|
LineBasicMaterial,
|
||||||
Line,
|
Line,
|
||||||
Mesh,
|
Mesh,
|
||||||
Group,
|
Group,
|
||||||
} from "three";
|
} from "three";
|
||||||
import { useThree, useFrame } from "@react-three/fiber";
|
import { useThree, useFrame } from "@react-three/fiber";
|
||||||
import { Html } from "@react-three/drei";
|
import { Html } from "@react-three/drei";
|
||||||
|
|
||||||
interface DistanceFindingControlsProps {
|
interface DistanceFindingControlsProps {
|
||||||
boundingBoxRef: React.RefObject<Mesh>;
|
boundingBoxRef: React.RefObject<Mesh>;
|
||||||
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;
|
textPosX: "",
|
||||||
textNegX: any;
|
textNegX: "",
|
||||||
textPosZ: any;
|
textPosZ: "",
|
||||||
textNegZ: any;
|
textNegZ: "",
|
||||||
}>({
|
});
|
||||||
textPosX: "",
|
|
||||||
textNegX: "",
|
|
||||||
textPosZ: "",
|
|
||||||
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 textPosX = useRef<Group>(null);
|
|
||||||
const textNegX = useRef<Group>(null);
|
|
||||||
const textPosZ = useRef<Group>(null);
|
|
||||||
const textNegZ = useRef<Group>(null);
|
|
||||||
const textPosY = useRef<Group>(null);
|
|
||||||
|
|
||||||
// Store line geometries to avoid recreation
|
|
||||||
const lineGeometries = useRef({
|
|
||||||
posX: new BufferGeometry(),
|
|
||||||
negX: new BufferGeometry(),
|
|
||||||
posZ: new BufferGeometry(),
|
|
||||||
negZ: new BufferGeometry(),
|
|
||||||
posY: new BufferGeometry(),
|
|
||||||
});
|
|
||||||
|
|
||||||
useFrame(() => {
|
|
||||||
if (!boundingBoxRef?.current) return;
|
|
||||||
|
|
||||||
boundingBoxRef.current.geometry.computeBoundingBox();
|
|
||||||
const bbox = boundingBoxRef.current.geometry.boundingBox;
|
|
||||||
|
|
||||||
if (!bbox) return;
|
|
||||||
|
|
||||||
const size = {
|
|
||||||
x: bbox.max.x - bbox.min.x,
|
|
||||||
y: bbox.max.y - bbox.min.y,
|
|
||||||
z: bbox.max.z - bbox.min.z,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone();
|
const textRefs = {
|
||||||
|
textPosX: useRef<Group>(null),
|
||||||
|
textNegX: useRef<Group>(null),
|
||||||
|
textPosZ: useRef<Group>(null),
|
||||||
|
textNegZ: useRef<Group>(null),
|
||||||
|
textPosY: useRef<Group>(null),
|
||||||
|
};
|
||||||
|
|
||||||
if (!vec) return;
|
const lineGeometries = useRef({
|
||||||
updateLine({
|
posX: new BufferGeometry(),
|
||||||
line: line1.current,
|
negX: new BufferGeometry(),
|
||||||
geometry: lineGeometries.current.posX,
|
posZ: new BufferGeometry(),
|
||||||
direction: new Vector3(1, 0, 0), // Positive X
|
negZ: new BufferGeometry(),
|
||||||
angle: "pos",
|
posY: new BufferGeometry(),
|
||||||
mesh: textPosX,
|
|
||||||
vec,
|
|
||||||
size,
|
|
||||||
});
|
});
|
||||||
updateLine({
|
|
||||||
line: line2.current,
|
useFrame(() => {
|
||||||
geometry: lineGeometries.current.negX,
|
const bboxMesh = boundingBoxRef?.current;
|
||||||
direction: new Vector3(-1, 0, 0), // Negative X
|
if (!bboxMesh) return;
|
||||||
angle: "neg",
|
|
||||||
mesh: textNegX,
|
bboxMesh.geometry.computeBoundingBox();
|
||||||
vec,
|
const bbox = bboxMesh.geometry.boundingBox;
|
||||||
size,
|
if (!bbox) return;
|
||||||
|
|
||||||
|
const size = {
|
||||||
|
x: bbox.max.x - bbox.min.x,
|
||||||
|
y: bbox.max.y - bbox.min.y,
|
||||||
|
z: bbox.max.z - bbox.min.z,
|
||||||
|
};
|
||||||
|
|
||||||
|
const center = bboxMesh.getWorldPosition(new Vector3()).clone();
|
||||||
|
if (!center) return;
|
||||||
|
|
||||||
|
updateLine("posX", new Vector3(1, 0, 0), "pos", size, center);
|
||||||
|
updateLine("negX", new Vector3(-1, 0, 0), "neg", size, center);
|
||||||
|
updateLine("posZ", new Vector3(0, 0, 1), "pos", size, center);
|
||||||
|
updateLine("negZ", new Vector3(0, 0, -1), "neg", size, center);
|
||||||
|
updateLine("posY", new Vector3(0, -1, 0), "posY", size, center);
|
||||||
});
|
});
|
||||||
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;
|
|
||||||
|
|
||||||
const points = [];
|
if (!line) return;
|
||||||
|
|
||||||
if (angle === "pos") {
|
const points: Vector3[] = [];
|
||||||
points[0] = new Vector3(vec.x, vec.y, vec.z).add(
|
const halfSize = new Vector3(size.x / 2, size.y / 2, size.z / 2);
|
||||||
new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2)
|
|
||||||
);
|
|
||||||
} else if (angle === "neg") {
|
|
||||||
points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
|
|
||||||
new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2)
|
|
||||||
);
|
|
||||||
} else if (angle === "posY") {
|
|
||||||
points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
|
|
||||||
new Vector3(0, size.y / 2, 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ray = new Raycaster();
|
if (angle === "pos") {
|
||||||
if (camera) ray.camera = camera;
|
points[0] = origin.clone().add(direction.clone().multiply(halfSize));
|
||||||
ray.set(new Vector3(vec.x, vec.y, vec.z), direction);
|
} else if (angle === "neg") {
|
||||||
ray.params.Line.threshold = 0.1;
|
points[0] = origin.clone().sub(direction.clone().multiply(halfSize));
|
||||||
|
} else if (angle === "posY") {
|
||||||
|
points[0] = origin.clone().sub(new Vector3(0, size.y / 2, 0));
|
||||||
|
}
|
||||||
|
|
||||||
// Find intersection points
|
const ray = new Raycaster();
|
||||||
const wallsGroup = scene.children.find((val) =>
|
ray.camera = camera;
|
||||||
val?.name.includes("Walls")
|
ray.set(origin, direction);
|
||||||
|
ray.params.Line.threshold = 0.1;
|
||||||
|
|
||||||
|
const wallsGroup = scene.children.find((val) =>
|
||||||
|
val?.name.includes("Walls")
|
||||||
|
);
|
||||||
|
const intersects = wallsGroup
|
||||||
|
? ray.intersectObjects([wallsGroup], true)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const intersect = intersects.find((i) =>
|
||||||
|
i.object.name.includes("Wall")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (intersect) {
|
||||||
|
points[1] = angle !== "posY" ? intersect.point : new Vector3(origin.x, 0, origin.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points[1]) {
|
||||||
|
geometry.dispose();
|
||||||
|
geometry.setFromPoints(points);
|
||||||
|
line.geometry = geometry;
|
||||||
|
|
||||||
|
const distance = points[0].distanceTo(points[1]).toFixed(2);
|
||||||
|
|
||||||
|
if (mesh?.current) {
|
||||||
|
geometry.computeBoundingSphere();
|
||||||
|
mesh.current.position.copy(geometry.boundingSphere!.center);
|
||||||
|
|
||||||
|
const labelEl = document.getElementById(mesh.current.name);
|
||||||
|
if (labelEl) {
|
||||||
|
labelEl.innerText = `${distance}m`;
|
||||||
|
|
||||||
|
if (DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) {
|
||||||
|
setLabelValues((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[labelEl.id]: distance,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
geometry.dispose();
|
||||||
|
geometry.setFromPoints([new Vector3(), new Vector3()]);
|
||||||
|
line.geometry = geometry;
|
||||||
|
|
||||||
|
const labelEl = document.getElementById(mesh?.current?.name ?? "");
|
||||||
|
if (labelEl && DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) {
|
||||||
|
labelEl.innerText = "";
|
||||||
|
setLabelValues((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[labelEl.id]: "",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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>
|
||||||
);
|
);
|
||||||
const intersects = wallsGroup
|
|
||||||
? ray.intersectObjects([wallsGroup], true)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
// Find intersection point
|
return (
|
||||||
if (intersects[0]) {
|
|
||||||
for (const intersect of intersects) {
|
|
||||||
if (intersect.object.name.includes("Wall")) {
|
|
||||||
points[1] =
|
|
||||||
angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (points[1]) {
|
|
||||||
geometry.dispose();
|
|
||||||
geometry.setFromPoints([points[0], points[1]]);
|
|
||||||
line.geometry = geometry;
|
|
||||||
|
|
||||||
// Calculate the distance only once
|
|
||||||
const distance = points[0].distanceTo(points[1]).toFixed(2);
|
|
||||||
|
|
||||||
// Update measurement text
|
|
||||||
if (mesh?.current) {
|
|
||||||
geometry.computeBoundingSphere();
|
|
||||||
const center = geometry.boundingSphere?.center;
|
|
||||||
if (center) {
|
|
||||||
mesh.current.position.copy(center);
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = document.getElementById(mesh.current.name);
|
|
||||||
if (label) {
|
|
||||||
label.innerText = `${distance}m`;
|
|
||||||
|
|
||||||
// Update specific label state based on the label ID
|
|
||||||
switch (label.id) {
|
|
||||||
case "textPosX":
|
|
||||||
setLabelValues((prevState) => ({ ...prevState, textPosX: 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 {
|
|
||||||
// No intersection found - clear the line
|
|
||||||
geometry.dispose();
|
|
||||||
geometry.setFromPoints([new Vector3(), new Vector3()]);
|
|
||||||
line.geometry = geometry;
|
|
||||||
|
|
||||||
const label = document.getElementById(mesh?.current?.name ?? "");
|
|
||||||
if (label) {
|
|
||||||
label.innerText = "";
|
|
||||||
|
|
||||||
// Clear the corresponding label value in the state
|
|
||||||
switch (label.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" });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Measurement text labels */}
|
|
||||||
{boundingBoxRef.current && object > 0 && (
|
|
||||||
<>
|
<>
|
||||||
<group name="textPosX" ref={textPosX}>
|
{boundingBoxRef.current && object > 0 && (
|
||||||
<Html
|
<group
|
||||||
wrapperClass="distance-text-wrapper"
|
name="DistanceFindingControls"
|
||||||
className="distance-text"
|
>
|
||||||
zIndexRange={[1, 0]}
|
{renderLabel("textPosX")}
|
||||||
style={{
|
{renderLabel("textNegX")}
|
||||||
pointerEvents: "none",
|
{renderLabel("textPosZ")}
|
||||||
visibility: labelValues.textPosX === "" ? "hidden" : "visible",
|
{renderLabel("textNegZ")}
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="distance-label" id="textPosX">{labelValues.textPosX}</div>
|
|
||||||
</Html>
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<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]}
|
</group>
|
||||||
style={{
|
)}
|
||||||
pointerEvents: "none",
|
|
||||||
visibility: labelValues.textNegX === "" ? "hidden" : "visible",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="distance-label" id="textNegX">{labelValues.textNegX}</div>
|
|
||||||
</Html>
|
|
||||||
</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}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DistanceFindingControls;
|
export default DistanceFindingControls;
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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) => {
|
||||||
const floor = state.floors.find(f => f.floorUuid === uuid);
|
let updatedFloor: Floor | undefined;
|
||||||
if (floor) {
|
set(state => {
|
||||||
Object.assign(floor, updated);
|
const floor = state.floors.find(f => f.floorUuid === uuid);
|
||||||
}
|
if (floor) {
|
||||||
}),
|
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);
|
||||||
|
|||||||
Reference in New Issue
Block a user