feat: Implement wall creation and management features
- Added WallGroup component to manage wall creation and instances. - Introduced WallCreator for handling wall creation logic, including mouse events and snapping. - Created ReferenceWall component to visualize temporary wall during creation. - Implemented WallInstances to render existing walls in the scene. - Added useWallStore for state management of walls, including adding, updating, and removing walls. - Enhanced Point and Line components to support wall-related functionalities. - Updated builder store to include wall properties such as thickness and height. - Refactored point snapping logic to accommodate wall snapping. - Removed unused ReferencePoint component and adjusted imports accordingly. - Updated world constants to include new wall-related configurations.
This commit is contained in:
parent
49ac226078
commit
20b6f84b38
|
@ -8,11 +8,9 @@ import { upsertProductOrEventApi } from "../../../../../../services/simulation/p
|
|||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore";
|
||||
import * as THREE from 'three';
|
||||
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
||||
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
|
||||
|
||||
function StorageMechanics() {
|
||||
const { storageUnitStore } = useSceneContext();
|
||||
const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default");
|
||||
const [selectedPointData, setSelectedPointData] = useState<StoragePointSchema | undefined>();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
|
@ -20,7 +18,6 @@ function StorageMechanics() {
|
|||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
|
||||
const { setCurrentMaterials, clearCurrentMaterials, updateCurrentLoad, getStorageUnitById } = storageUnitStore();
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
@ -92,22 +89,6 @@ function StorageMechanics() {
|
|||
);
|
||||
updateSelectedPointData();
|
||||
}
|
||||
|
||||
if (option === "spawn") {
|
||||
const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid);
|
||||
if (storageUnit) {
|
||||
const materialType = selectedPointData.action.materialType || "Default material";
|
||||
const materials = Array.from({ length: selectedPointData.action.storageCapacity }, () =>
|
||||
createNewMaterial(materialType)
|
||||
).filter(Boolean) as { materialType: string; materialId: string }[];
|
||||
|
||||
setCurrentMaterials(selectedEventData.data.modelUuid, materials);
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, selectedPointData.action.storageCapacity);
|
||||
}
|
||||
} else {
|
||||
clearCurrentMaterials(selectedEventData.data.modelUuid);
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRenameAction = (newName: string) => {
|
||||
|
@ -141,21 +122,6 @@ function StorageMechanics() {
|
|||
event
|
||||
);
|
||||
updateSelectedPointData();
|
||||
|
||||
if (activeOption === "spawn") {
|
||||
const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid);
|
||||
if (storageUnit) {
|
||||
const materialType = selectedPointData.action.materialType || "Default material";
|
||||
const materials = Array.from({ length: selectedPointData.action.storageCapacity }, () =>
|
||||
createNewMaterial(materialType)
|
||||
).filter(Boolean) as { materialType: string; materialId: string }[];
|
||||
|
||||
setCurrentMaterials(selectedEventData.data.modelUuid, materials);
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, selectedPointData.action.storageCapacity);
|
||||
}
|
||||
} else {
|
||||
updateCurrentLoad(selectedEventData.data.modelUuid, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -183,17 +149,6 @@ function StorageMechanics() {
|
|||
event
|
||||
);
|
||||
updateSelectedPointData();
|
||||
|
||||
if (activeOption === "spawn") {
|
||||
const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid);
|
||||
if (storageUnit) {
|
||||
const materials = Array.from({ length: storageUnit.currentMaterials.length }, () =>
|
||||
createNewMaterial(value)
|
||||
).filter(Boolean) as { materialType: string; materialId: string }[];
|
||||
|
||||
setCurrentMaterials(selectedEventData.data.modelUuid, materials);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,316 +4,293 @@ import RenameInput from "../../../../../ui/inputs/RenameInput";
|
|||
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
|
||||
import Trigger from "../trigger/Trigger";
|
||||
import {
|
||||
useSelectedAction,
|
||||
useSelectedEventData,
|
||||
useSelectedEventSphere,
|
||||
useSelectedAction,
|
||||
useSelectedEventData,
|
||||
} from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||
import TravelAction from "../actions/TravelAction";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
||||
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
|
||||
|
||||
function VehicleMechanics() {
|
||||
const { vehicleStore } = useSceneContext();
|
||||
const [activeOption, setActiveOption] = useState<"default" | "travel">("default");
|
||||
const [selectedPointData, setSelectedPointData] = useState<VehiclePointSchema | undefined>();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
|
||||
const { getVehicleById } = vehicleStore();
|
||||
const [activeOption, setActiveOption] = useState<"default" | "travel">("default");
|
||||
const [selectedPointData, setSelectedPointData] = useState<VehiclePointSchema | undefined>();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedEventData && selectedEventData.data.type === "vehicle") {
|
||||
const point = getPointByUuid(
|
||||
selectedProduct.productId,
|
||||
selectedEventData.data.modelUuid,
|
||||
selectedEventData.selectedPoint
|
||||
) as VehiclePointSchema | undefined;
|
||||
useEffect(() => {
|
||||
if (selectedEventData && selectedEventData.data.type === "vehicle") {
|
||||
const point = getPointByUuid(
|
||||
selectedProduct.productId,
|
||||
selectedEventData.data.modelUuid,
|
||||
selectedEventData.selectedPoint
|
||||
) as VehiclePointSchema | undefined;
|
||||
|
||||
if (point) {
|
||||
setSelectedPointData(point);
|
||||
setActiveOption(point.action.actionType as "travel");
|
||||
setSelectedAction(point.action.actionUuid, point.action.actionName);
|
||||
}
|
||||
} else {
|
||||
clearSelectedAction();
|
||||
}
|
||||
}, [selectedProduct, selectedEventData]);
|
||||
if (point) {
|
||||
setSelectedPointData(point);
|
||||
setActiveOption(point.action.actionType as "travel");
|
||||
setSelectedAction(point.action.actionUuid, point.action.actionName);
|
||||
}
|
||||
} else {
|
||||
clearSelectedAction();
|
||||
}
|
||||
}, [selectedProduct, selectedEventData]);
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
productId: string,
|
||||
organization: string,
|
||||
eventData: EventsSchema
|
||||
) => {
|
||||
upsertProductOrEventApi({
|
||||
productName: productName,
|
||||
productId: productId,
|
||||
organization: organization,
|
||||
eventDatas: eventData,
|
||||
});
|
||||
};
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
productId: string,
|
||||
organization: string,
|
||||
eventData: EventsSchema
|
||||
) => {
|
||||
upsertProductOrEventApi({
|
||||
productName: productName,
|
||||
productId: productId,
|
||||
organization: organization,
|
||||
eventDatas: eventData,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSpeedChange = (value: string) => {
|
||||
if (!selectedEventData) return;
|
||||
const event = updateEvent(
|
||||
selectedProduct.productId,
|
||||
selectedEventData.data.modelUuid,
|
||||
{
|
||||
speed: parseFloat(value),
|
||||
}
|
||||
);
|
||||
const handleSpeedChange = (value: string) => {
|
||||
if (!selectedEventData) return;
|
||||
const event = updateEvent(
|
||||
selectedProduct.productId,
|
||||
selectedEventData.data.modelUuid,
|
||||
{
|
||||
speed: parseFloat(value),
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleActionTypeChange = (option: string) => {
|
||||
if (!selectedEventData || !selectedPointData) return;
|
||||
const validOption = option as "travel";
|
||||
setActiveOption(validOption);
|
||||
const handleActionTypeChange = (option: string) => {
|
||||
if (!selectedEventData || !selectedPointData) return;
|
||||
const validOption = option as "travel";
|
||||
setActiveOption(validOption);
|
||||
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{
|
||||
actionType: validOption,
|
||||
}
|
||||
);
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{
|
||||
actionType: validOption,
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRenameAction = (newName: string) => {
|
||||
if (!selectedPointData) return;
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{ actionName: newName }
|
||||
);
|
||||
const handleRenameAction = (newName: string) => {
|
||||
if (!selectedPointData) return;
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{ actionName: newName }
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoadCapacityChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{
|
||||
loadCapacity: parseFloat(value),
|
||||
}
|
||||
);
|
||||
const handleLoadCapacityChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{
|
||||
loadCapacity: parseFloat(value),
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnloadDurationChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{
|
||||
unLoadDuration: parseFloat(value),
|
||||
}
|
||||
);
|
||||
const handleUnloadDurationChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
const event = updateAction(
|
||||
selectedProduct.productId,
|
||||
selectedPointData.action.actionUuid,
|
||||
{
|
||||
unLoadDuration: parseFloat(value),
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePickPointChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
};
|
||||
const handlePickPointChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
};
|
||||
|
||||
const handleUnloadPointChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
};
|
||||
const handleUnloadPointChange = (value: string) => {
|
||||
if (!selectedPointData) return;
|
||||
};
|
||||
|
||||
// Get current values from store
|
||||
// Get current values from store
|
||||
|
||||
const currentSpeed =
|
||||
(
|
||||
getEventByModelUuid(
|
||||
selectedProduct.productId,
|
||||
selectedEventData?.data.modelUuid || ""
|
||||
) as VehicleEventSchema | undefined
|
||||
)?.speed?.toString() || "0.5";
|
||||
const currentSpeed =
|
||||
(
|
||||
getEventByModelUuid(
|
||||
selectedProduct.productId,
|
||||
selectedEventData?.data.modelUuid || ""
|
||||
) as VehicleEventSchema | undefined
|
||||
)?.speed?.toString() || "0.5";
|
||||
|
||||
const currentActionName = selectedPointData
|
||||
? selectedPointData.action.actionName
|
||||
: "Action Name";
|
||||
const currentActionName = selectedPointData
|
||||
? selectedPointData.action.actionName
|
||||
: "Action Name";
|
||||
|
||||
const currentLoadCapacity = selectedPointData
|
||||
? selectedPointData.action.loadCapacity.toString()
|
||||
: "1";
|
||||
const currentLoadCapacity = selectedPointData
|
||||
? selectedPointData.action.loadCapacity.toString()
|
||||
: "1";
|
||||
|
||||
const currentUnloadDuration = selectedPointData
|
||||
? selectedPointData.action.unLoadDuration.toString()
|
||||
: "1";
|
||||
const currentUnloadDuration = selectedPointData
|
||||
? selectedPointData.action.unLoadDuration.toString()
|
||||
: "1";
|
||||
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
function handleClearPoints() {
|
||||
|
||||
function handleClearPoints() {
|
||||
const updatedVehicle = getVehicleById(
|
||||
selectedEventSphere?.userData.modelUuid
|
||||
);
|
||||
if (!selectedEventData || !selectedPointData?.action.actionUuid) return;
|
||||
|
||||
if (
|
||||
!selectedProduct?.productId ||
|
||||
!selectedEventSphere?.userData?.modelUuid ||
|
||||
!updatedVehicle?.point
|
||||
)
|
||||
return;
|
||||
|
||||
const event = updateEvent(
|
||||
selectedProduct.productId,
|
||||
selectedEventSphere.userData.modelUuid,
|
||||
{
|
||||
point: {
|
||||
...updatedVehicle.point,
|
||||
action: {
|
||||
...updatedVehicle.point?.action,
|
||||
const event = updateAction(
|
||||
selectedProduct.productId, selectedPointData.action.actionUuid, {
|
||||
pickUpPoint: null,
|
||||
unLoadPoint: null,
|
||||
steeringAngle: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
})
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productId,
|
||||
organization,
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const availableActions = {
|
||||
defaultOption: "travel",
|
||||
options: ["travel"],
|
||||
};
|
||||
const availableActions = {
|
||||
defaultOption: "travel",
|
||||
options: ["travel"],
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedEventData && (
|
||||
return (
|
||||
<>
|
||||
<div className="global-props section">
|
||||
<div className="property-list-container">
|
||||
<div className="property-item">
|
||||
<InputWithDropDown
|
||||
label="Speed"
|
||||
value={currentSpeed}
|
||||
min={0}
|
||||
step={0.1}
|
||||
defaultValue={"0.5"}
|
||||
max={10}
|
||||
activeOption="m/s"
|
||||
onClick={() => { }}
|
||||
onChange={handleSpeedChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section>
|
||||
<ActionsList selectedPointData={selectedPointData} />
|
||||
<div className="selected-actions-details">
|
||||
<div className="selected-actions-header">
|
||||
<RenameInput
|
||||
value={currentActionName}
|
||||
onRename={handleRenameAction}
|
||||
/>
|
||||
</div>
|
||||
<div className="selected-actions-list">
|
||||
<LabledDropdown
|
||||
defaultOption="travel"
|
||||
options={availableActions.options}
|
||||
onSelect={handleActionTypeChange}
|
||||
/>
|
||||
{selectedEventData && (
|
||||
<>
|
||||
<div className="global-props section">
|
||||
<div className="property-list-container">
|
||||
<div className="property-item">
|
||||
<InputWithDropDown
|
||||
label="Speed"
|
||||
value={currentSpeed}
|
||||
min={0}
|
||||
step={0.1}
|
||||
defaultValue={"0.5"}
|
||||
max={10}
|
||||
activeOption="m/s"
|
||||
onClick={() => { }}
|
||||
onChange={handleSpeedChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section>
|
||||
<ActionsList selectedPointData={selectedPointData} />
|
||||
<div className="selected-actions-details">
|
||||
<div className="selected-actions-header">
|
||||
<RenameInput
|
||||
value={currentActionName}
|
||||
onRename={handleRenameAction}
|
||||
/>
|
||||
</div>
|
||||
<div className="selected-actions-list">
|
||||
<LabledDropdown
|
||||
defaultOption="travel"
|
||||
options={availableActions.options}
|
||||
onSelect={handleActionTypeChange}
|
||||
/>
|
||||
|
||||
{activeOption === "travel" && (
|
||||
<TravelAction
|
||||
loadCapacity={{
|
||||
value: currentLoadCapacity,
|
||||
min: 1,
|
||||
max: 100,
|
||||
defaultValue: "1",
|
||||
onChange: handleLoadCapacityChange,
|
||||
}}
|
||||
unloadDuration={{
|
||||
value: currentUnloadDuration,
|
||||
min: 1,
|
||||
max: 60,
|
||||
defaultValue: "1",
|
||||
onChange: handleUnloadDurationChange,
|
||||
}}
|
||||
clearPoints={handleClearPoints}
|
||||
// pickPoint={{
|
||||
// value: currentPickPoint,
|
||||
// onChange: handlePickPointChange,
|
||||
// }}
|
||||
// unloadPoint={{
|
||||
// value: currentUnloadPoint,
|
||||
// onChange: handleUnloadPointChange,
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tirgger">
|
||||
<Trigger
|
||||
selectedPointData={selectedPointData as any}
|
||||
type={"Vehicle"}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{activeOption === "travel" && (
|
||||
<TravelAction
|
||||
loadCapacity={{
|
||||
value: currentLoadCapacity,
|
||||
min: 1,
|
||||
max: 100,
|
||||
defaultValue: "1",
|
||||
onChange: handleLoadCapacityChange,
|
||||
}}
|
||||
unloadDuration={{
|
||||
value: currentUnloadDuration,
|
||||
min: 1,
|
||||
max: 60,
|
||||
defaultValue: "1",
|
||||
onChange: handleUnloadDurationChange,
|
||||
}}
|
||||
clearPoints={handleClearPoints}
|
||||
// pickPoint={{
|
||||
// value: currentPickPoint,
|
||||
// onChange: handlePickPointChange,
|
||||
// }}
|
||||
// unloadPoint={{
|
||||
// value: currentUnloadPoint,
|
||||
// onChange: handleUnloadPointChange,
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tirgger">
|
||||
<Trigger
|
||||
selectedPointData={selectedPointData as any}
|
||||
type={"Vehicle"}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export default VehicleMechanics;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../.
|
|||
import { useAisleStore } from '../../../../store/builder/useAisleStore';
|
||||
import ReferenceAisle from './referenceAisle';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import ReferencePoint from '../../point/referencePoint';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
|
||||
function AisleCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
|
@ -249,8 +249,10 @@ function AisleCreator() {
|
|||
canvasElement.addEventListener("click", onMouseClick);
|
||||
canvasElement.addEventListener("contextmenu", onContext);
|
||||
} else {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
if (tempPoints.length > 0 || isCreating) {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
|
@ -265,7 +267,7 @@ function AisleCreator() {
|
|||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint]);
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, getAislePointById, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -24,10 +24,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
const [tempAisle, setTempAisle] = useState<Aisle | null>(null);
|
||||
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
|
||||
|
||||
const directionalSnap = useDirectionalSnapping(
|
||||
currentPosition,
|
||||
tempPoints[0]?.position || null
|
||||
);
|
||||
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null);
|
||||
const { checkSnapForAisle } = usePointSnapping({ uuid: 'temp-aisle', pointType: 'Aisle', position: directionalSnap.position || [0, 0, 0] });
|
||||
|
||||
useFrame(() => {
|
||||
|
@ -39,7 +36,6 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (intersectionPoint) {
|
||||
|
||||
const snapped = checkSnapForAisle([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
|
@ -216,14 +212,11 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
|||
<>
|
||||
{toggleView &&
|
||||
<Html
|
||||
// data
|
||||
key={tempAisle.aisleUuid}
|
||||
userData={tempAisle}
|
||||
position={[textPosition.x, 1, textPosition.z]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
|
|
|
@ -48,6 +48,7 @@ import LayoutImage from "./layout/layoutImage";
|
|||
import AssetsGroup from "./asset/assetsGroup";
|
||||
import { Bvh } from "@react-three/drei";
|
||||
import AislesGroup from "./aisle/aislesGroup";
|
||||
import WallGroup from "./wall/wallGroup";
|
||||
|
||||
export default function Builder() {
|
||||
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
|
||||
|
@ -279,6 +280,8 @@ export default function Builder() {
|
|||
plane={plane}
|
||||
/>
|
||||
|
||||
<WallGroup />
|
||||
|
||||
<AislesGroup />
|
||||
|
||||
<MeasurementTool />
|
||||
|
|
|
@ -157,7 +157,7 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
|||
}
|
||||
|
||||
if (toolMode === "Wall") {
|
||||
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket);
|
||||
// drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket);
|
||||
}
|
||||
|
||||
if (toolMode === "Floor") {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo } from "react";
|
||||
import * as Constants from '../../../types/world/worldConstants';
|
||||
import { Tube } from '@react-three/drei';
|
||||
|
||||
interface LineProps {
|
||||
points: [Point, Point];
|
||||
}
|
||||
|
||||
function Line({ points }: Readonly<LineProps>) {
|
||||
const path = useMemo(() => {
|
||||
const [start, end] = points.map(p => new THREE.Vector3(...p.position));
|
||||
return new THREE.LineCurve3(start, end);
|
||||
}, [points]);
|
||||
|
||||
const colors = getColor(points[0]);
|
||||
|
||||
function getColor(point: Point) {
|
||||
if (point.pointType === 'Aisle') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.aisleColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Floor') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.floorColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.wallColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Zone') {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.zoneColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
defaultLineColor: Constants.lineConfig.defaultColor,
|
||||
defaultDeleteColor: Constants.lineConfig.deleteColor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Tube
|
||||
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
|
||||
>
|
||||
<meshStandardMaterial color={colors.defaultLineColor} />
|
||||
</Tube>
|
||||
);
|
||||
}
|
||||
|
||||
export default Line;
|
|
@ -0,0 +1,25 @@
|
|||
import * as THREE from 'three';
|
||||
import { useMemo } from "react";
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import { Tube } from '@react-three/drei';
|
||||
|
||||
interface ReferenceLineProps {
|
||||
points: [Point, Point];
|
||||
}
|
||||
|
||||
function ReferenceLine({ points }: Readonly<ReferenceLineProps>) {
|
||||
const path = useMemo(() => {
|
||||
const [start, end] = points.map(p => new THREE.Vector3(...p.position));
|
||||
return new THREE.LineCurve3(start, end);
|
||||
}, [points]);
|
||||
|
||||
return (
|
||||
<Tube
|
||||
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
|
||||
>
|
||||
<meshStandardMaterial color={Constants.lineConfig.helperColor} />
|
||||
</Tube>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReferenceLine;
|
|
@ -1,6 +1,7 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useAisleStore } from '../../../../store/builder/useAisleStore';
|
||||
import * as THREE from 'three';
|
||||
import { useWallStore } from '../../../../store/builder/useWallStore';
|
||||
|
||||
const SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
||||
|
||||
|
@ -8,8 +9,9 @@ const CAN_SNAP = true; // Whether snapping is enabled or not
|
|||
|
||||
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
|
||||
const { aisles } = useAisleStore();
|
||||
const { walls } = useWallStore();
|
||||
|
||||
const getAllOtherPoints = useCallback(() => {
|
||||
const getAllOtherAislePoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
|
||||
return aisles.flatMap(aisle =>
|
||||
|
@ -17,10 +19,17 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
|
|||
);
|
||||
}, [aisles, currentPoint]);
|
||||
|
||||
const getAllOtherWallPoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
return walls.flatMap(wall =>
|
||||
wall.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
}, [walls, currentPoint]);
|
||||
|
||||
const checkSnapForAisle = useCallback((position: [number, number, number]) => {
|
||||
if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = getAllOtherPoints();
|
||||
const otherPoints = getAllOtherAislePoints();
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
|
||||
for (const point of otherPoints) {
|
||||
|
@ -33,9 +42,25 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
|
|||
}
|
||||
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherPoints]);
|
||||
}, [currentPoint, getAllOtherAislePoints]);
|
||||
|
||||
const checkSnapForWall = useCallback((position: [number, number, number]) => {
|
||||
if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = getAllOtherWallPoints();
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
if (distance <= SNAP_THRESHOLD && currentPoint.pointType === 'Wall') {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
}
|
||||
}
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherWallPoints]);
|
||||
|
||||
return {
|
||||
checkSnapForAisle,
|
||||
checkSnapForWall,
|
||||
};
|
||||
};
|
|
@ -22,35 +22,67 @@ function Point({ point }: { readonly point: Point }) {
|
|||
const { deletePointOrLine } = useDeletePointOrLine();
|
||||
|
||||
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
||||
const defaultInnerColor = Constants.pointConfig.defaultInnerColor;
|
||||
const defaultOuterColor = Constants.pointConfig.aisleOuterColor;
|
||||
const defaultDeleteColor = Constants.pointConfig.deleteColor;
|
||||
const colors = getColor(point);
|
||||
|
||||
function getColor(point: Point) {
|
||||
if (point.pointType === 'Aisle') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.aisleOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Floor') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.floorOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.wallOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else if (point.pointType === 'Zone') {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.zoneOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.defaultOuterColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (materialRef.current && (toolMode === 'move' || deletePointOrLine)) {
|
||||
let innerColor;
|
||||
let outerColor;
|
||||
if (isHovered) {
|
||||
innerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor;
|
||||
outerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor;
|
||||
innerColor = deletePointOrLine ? colors.defaultDeleteColor : colors.defaultOuterColor;
|
||||
outerColor = deletePointOrLine ? colors.defaultDeleteColor : colors.defaultOuterColor;
|
||||
} else {
|
||||
innerColor = defaultInnerColor;
|
||||
outerColor = defaultOuterColor;
|
||||
innerColor = colors.defaultInnerColor;
|
||||
outerColor = colors.defaultOuterColor;
|
||||
}
|
||||
materialRef.current.uniforms.uInnerColor.value.set(innerColor);
|
||||
materialRef.current.uniforms.uOuterColor.value.set(outerColor);
|
||||
materialRef.current.uniformsNeedUpdate = true;
|
||||
} else if (materialRef.current && toolMode !== 'move') {
|
||||
materialRef.current.uniforms.uInnerColor.value.set(defaultInnerColor);
|
||||
materialRef.current.uniforms.uOuterColor.value.set(defaultOuterColor);
|
||||
materialRef.current.uniforms.uInnerColor.value.set(colors.defaultInnerColor);
|
||||
materialRef.current.uniforms.uOuterColor.value.set(colors.defaultOuterColor);
|
||||
materialRef.current.uniformsNeedUpdate = true;
|
||||
}
|
||||
}, [isHovered, defaultInnerColor, defaultOuterColor, toolMode, deletePointOrLine, defaultDeleteColor]);
|
||||
}, [isHovered, colors.defaultInnerColor, colors.defaultOuterColor, colors.defaultDeleteColor, toolMode, deletePointOrLine]);
|
||||
|
||||
const uniforms = useMemo(() => ({
|
||||
uOuterColor: { value: new THREE.Color(defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(defaultInnerColor) },
|
||||
}), [defaultInnerColor, defaultInnerColor]);
|
||||
uOuterColor: { value: new THREE.Color(colors.defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(colors.defaultInnerColor) },
|
||||
}), [colors.defaultInnerColor, colors.defaultOuterColor]);
|
||||
|
||||
const handleDrag = (point: Point) => {
|
||||
if (toolMode === 'move' && isHovered) {
|
||||
|
@ -76,10 +108,11 @@ function Point({ point }: { readonly point: Point }) {
|
|||
|
||||
const handlePointClick = (point: Point) => {
|
||||
if (deletePointOrLine) {
|
||||
const removedAisles = removePoint(point.pointUuid);
|
||||
if (removedAisles.length > 0) {
|
||||
setHoveredPoint(null);
|
||||
console.log(removedAisles);
|
||||
if (point.pointType === 'Aisle') {
|
||||
const removedAisles = removePoint(point.pointUuid);
|
||||
if (removedAisles.length > 0) {
|
||||
setHoveredPoint(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import * as THREE from 'three';
|
||||
import * as Constants from '../../../types/world/worldConstants';
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import { useRef, useMemo } from 'react';
|
||||
|
||||
function ReferencePoint({ point }: { readonly point: Point }) {
|
||||
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
||||
|
||||
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
||||
const defaultInnerColor = Constants.pointConfig.defaultInnerColor;
|
||||
const defaultOuterColor = Constants.pointConfig.aisleOuterColor;
|
||||
const colors = {
|
||||
defaultInnerColor: Constants.pointConfig.defaultInnerColor,
|
||||
defaultOuterColor: Constants.pointConfig.helperColor,
|
||||
defaultDeleteColor: Constants.pointConfig.deleteColor,
|
||||
};
|
||||
|
||||
const uniforms = useMemo(() => ({
|
||||
uOuterColor: { value: new THREE.Color(defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(defaultInnerColor) },
|
||||
}), [defaultOuterColor, defaultInnerColor]);
|
||||
uOuterColor: { value: new THREE.Color(colors.defaultOuterColor) },
|
||||
uInnerColor: { value: new THREE.Color(colors.defaultInnerColor) },
|
||||
}), [colors.defaultInnerColor, colors.defaultOuterColor, colors.defaultDeleteColor]);
|
||||
|
||||
if (!point) {
|
||||
return null;
|
|
@ -0,0 +1,21 @@
|
|||
import Line from '../../../line/line'
|
||||
import Point from '../../../point/point';
|
||||
import { useToggleView } from '../../../../../store/builder/store';
|
||||
|
||||
function WallInstance({ wall }: { readonly wall: Wall }) {
|
||||
const { toggleView } = useToggleView();
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView && (
|
||||
<>
|
||||
<Point point={wall.points[0]} />
|
||||
<Line points={wall.points} />
|
||||
<Point point={wall.points[1]} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallInstance
|
|
@ -0,0 +1,21 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useWallStore } from '../../../../store/builder/useWallStore'
|
||||
import WallInstance from './instance/wallInstance';
|
||||
|
||||
function WallInstances() {
|
||||
const { walls } = useWallStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('walls: ', walls);
|
||||
}, [walls])
|
||||
|
||||
return (
|
||||
<>
|
||||
{walls.map((wall) => (
|
||||
<WallInstance key={wall.wallUuid} wall={wall} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallInstances
|
|
@ -0,0 +1,128 @@
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
|
||||
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
||||
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import ReferenceLine from '../../line/reference/referenceLine';
|
||||
|
||||
interface ReferenceWallProps {
|
||||
tempPoints: Point[];
|
||||
}
|
||||
|
||||
function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
const { wallHeight, wallThickness, setSnappedPosition, setSnappedPoint } = useBuilderStore();
|
||||
|
||||
const { pointer, raycaster, camera } = useThree();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
const finalPosition = useRef<[number, number, number] | null>(null);
|
||||
|
||||
const [tempWall, setTempWall] = useState<Wall | null>(null);
|
||||
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
|
||||
|
||||
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null);
|
||||
const { checkSnapForWall } = usePointSnapping({ uuid: 'temp-wall', pointType: 'Wall', position: directionalSnap.position || [0, 0, 0] });
|
||||
|
||||
useFrame(() => {
|
||||
if (toolMode === 'Wall' && toggleView && tempPoints.length === 1) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (intersectionPoint) {
|
||||
const snapped = checkSnapForWall([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
finalPosition.current = snapped.position;
|
||||
setSnappedPosition(snapped.position);
|
||||
setSnappedPoint(snapped.snappedPoint);
|
||||
} else if (directionalSnap.isSnapped) {
|
||||
finalPosition.current = directionalSnap.position;
|
||||
setSnappedPosition(directionalSnap.position);
|
||||
setSnappedPoint(null);
|
||||
} else {
|
||||
finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z];
|
||||
setSnappedPosition(null);
|
||||
setSnappedPoint(null);
|
||||
}
|
||||
|
||||
if (!finalPosition.current) return;
|
||||
|
||||
const wallPoints: [Point, Point] = [
|
||||
tempPoints[0],
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Wall',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer,
|
||||
}
|
||||
];
|
||||
|
||||
setTempWall({
|
||||
wallUuid: 'temp-wall',
|
||||
points: wallPoints,
|
||||
outSideMaterial: 'default',
|
||||
inSideMaterial: 'default',
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
})
|
||||
|
||||
}
|
||||
} else if (tempWall !== null) {
|
||||
setTempWall(null);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTempWall(null);
|
||||
}, [toolMode, toggleView, tempPoints.length, wallHeight, wallThickness]);
|
||||
|
||||
if (!tempWall) return null;
|
||||
|
||||
const renderWall = () => {
|
||||
return (
|
||||
<ReferenceLine points={tempWall.points} />
|
||||
)
|
||||
};
|
||||
|
||||
const textPosition = new THREE.Vector3().addVectors(new THREE.Vector3(...tempWall.points[0].position), new THREE.Vector3(...tempWall.points[1].position)).divideScalar(2);
|
||||
const distance = new THREE.Vector3(...tempWall.points[0].position).distanceTo(new THREE.Vector3(...tempWall.points[1].position));
|
||||
|
||||
const rendertext = () => {
|
||||
return (
|
||||
<>
|
||||
{toggleView && (
|
||||
<Html
|
||||
key={tempWall.wallUuid}
|
||||
userData={tempWall}
|
||||
position={[textPosition.x, 1, textPosition.z]}
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div className={`distance ${tempWall.wallUuid}`}>{distance.toFixed(2)} m</div>
|
||||
</Html>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<group name="Wall-Reference-Group">
|
||||
{renderWall()}
|
||||
{rendertext()}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReferenceWall;
|
|
@ -0,0 +1,155 @@
|
|||
import * as THREE from 'three'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useWallStore } from '../../../../store/builder/useWallStore';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
import ReferenceWall from './referenceWall';
|
||||
|
||||
function WallCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { addWall, getWallPointById } = useWallStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { wallThickness, wallHeight, snappedPosition, snappedPoint } = useBuilderStore();
|
||||
|
||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown.current = true;
|
||||
drag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseClick = () => {
|
||||
if (drag.current || !toggleView) return;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
if (!position) return;
|
||||
|
||||
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point');
|
||||
|
||||
const newPoint: Point = {
|
||||
pointUuid: THREE.MathUtils.generateUUID(),
|
||||
pointType: 'Wall',
|
||||
position: [position.x, position.y, position.z],
|
||||
layer: activeLayer
|
||||
};
|
||||
|
||||
if (snappedPosition && snappedPoint) {
|
||||
newPoint.pointUuid = snappedPoint.pointUuid;
|
||||
newPoint.position = snappedPosition;
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
}
|
||||
|
||||
if (intersects && !snappedPoint) {
|
||||
const point = getWallPointById(intersects.object.uuid);
|
||||
if (point) {
|
||||
newPoint.pointUuid = point.pointUuid;
|
||||
newPoint.position = point.position;
|
||||
newPoint.layer = point.layer;
|
||||
}
|
||||
}
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const wall: Wall = {
|
||||
wallUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
outSideMaterial: 'default',
|
||||
inSideMaterial: 'default',
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight
|
||||
};
|
||||
addWall(wall);
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const onContext = (event: any) => {
|
||||
event.preventDefault();
|
||||
if (isCreating) {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (toolMode === "Wall" && toggleView) {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("click", onMouseClick);
|
||||
canvasElement.addEventListener("contextmenu", onContext);
|
||||
} else {
|
||||
if (tempPoints.length > 0 || isCreating) {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addWall, getWallPointById, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView &&
|
||||
<>
|
||||
<group name='Aisle-Reference-Points-Group'>
|
||||
{tempPoints.map((point) => (
|
||||
<ReferencePoint key={point.pointUuid} point={point} />
|
||||
))}
|
||||
</group>
|
||||
|
||||
{tempPoints.length > 0 &&
|
||||
<ReferenceWall tempPoints={tempPoints} />
|
||||
}
|
||||
</>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallCreator
|
|
@ -0,0 +1,16 @@
|
|||
import WallCreator from './wallCreator/wallCreator'
|
||||
import WallInstances from './Instances/wallInstances'
|
||||
|
||||
function WallGroup() {
|
||||
return (
|
||||
<>
|
||||
|
||||
<WallCreator />
|
||||
|
||||
<WallInstances />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WallGroup
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect } from 'react'
|
||||
import MaterialInstances from './instances/materialInstances'
|
||||
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
|
||||
import MaterialCollisionDetector from './collisionDetection/materialCollitionDetector';
|
||||
// import MaterialCollisionDetector from './collisionDetection/materialCollitionDetector';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
|
||||
function Materials() {
|
||||
|
@ -25,7 +25,7 @@ function Materials() {
|
|||
|
||||
}
|
||||
|
||||
<MaterialCollisionDetector />
|
||||
{/* <MaterialCollisionDetector /> */}
|
||||
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -9,6 +9,16 @@ interface BuilderState {
|
|||
snappedPoint: Point | null;
|
||||
snappedPosition: [number, number, number] | null;
|
||||
|
||||
// Wall
|
||||
|
||||
wallThickness: number;
|
||||
wallHeight: number;
|
||||
|
||||
setWallThickness: (thickness: number) => void;
|
||||
setWallHeight: (height: number) => void;
|
||||
|
||||
// Aisle
|
||||
|
||||
selectedAisle: Object3D | null;
|
||||
|
||||
aisleType: AisleTypes;
|
||||
|
@ -68,6 +78,25 @@ export const useBuilderStore = create<BuilderState>()(
|
|||
snappedPoint: null,
|
||||
snappedPosition: null,
|
||||
|
||||
// Wall
|
||||
|
||||
wallThickness: 2,
|
||||
wallHeight: 7,
|
||||
|
||||
setWallThickness: (thickness: number) => {
|
||||
set((state) => {
|
||||
state.wallThickness = thickness;
|
||||
})
|
||||
},
|
||||
|
||||
setWallHeight: (height: number) => {
|
||||
set((state) => {
|
||||
state.wallHeight = height;
|
||||
})
|
||||
},
|
||||
|
||||
// Aisle
|
||||
|
||||
selectedAisle: null,
|
||||
|
||||
aisleType: 'solid-aisle',
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
import { create } from 'zustand';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
|
||||
interface WallStore {
|
||||
walls: Wall[];
|
||||
setWalls: (walls: Wall[]) => void;
|
||||
addWall: (wall: Wall) => void;
|
||||
updateWall: (uuid: string, updated: Partial<Wall>) => void;
|
||||
removeWall: (uuid: string) => void;
|
||||
|
||||
removePoint: (pointUuid: string) => Wall[];
|
||||
setPosition: (pointUuid: string, position: [number, number, number]) => void;
|
||||
setLayer: (pointUuid: string, layer: number) => void;
|
||||
|
||||
getWallById: (uuid: string) => Wall | undefined;
|
||||
getWallPointById: (uuid: string) => Point | undefined;
|
||||
getConnectedPoints: (uuid: string) => Point[];
|
||||
}
|
||||
|
||||
export const useWallStore = create<WallStore>()(
|
||||
immer((set, get) => ({
|
||||
walls: [],
|
||||
|
||||
setWalls: (walls) => set((state) => {
|
||||
state.walls = walls;
|
||||
}),
|
||||
|
||||
addWall: (wall) => set((state) => {
|
||||
state.walls.push(wall);
|
||||
}),
|
||||
|
||||
updateWall: (uuid, updated) => set((state) => {
|
||||
const wall = state.walls.find(w => w.wallUuid === uuid);
|
||||
if (wall) {
|
||||
Object.assign(wall, updated);
|
||||
}
|
||||
}),
|
||||
|
||||
removeWall: (uuid) => set((state) => {
|
||||
state.walls = state.walls.filter(w => w.wallUuid !== uuid);
|
||||
}),
|
||||
|
||||
removePoint: (pointUuid) => {
|
||||
const removedWalls: Wall[] = [];
|
||||
set((state) => {
|
||||
state.walls = state.walls.filter((wall) => {
|
||||
const hasPoint = wall.points.some(p => p.pointUuid === pointUuid);
|
||||
if (hasPoint) {
|
||||
removedWalls.push(JSON.parse(JSON.stringify(wall)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
return removedWalls;
|
||||
},
|
||||
|
||||
setPosition: (pointUuid, position) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const point = wall.points.find(p => p.pointUuid === pointUuid);
|
||||
if (point) {
|
||||
point.position = position;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
setLayer: (pointUuid, layer) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const point = wall.points.find(p => p.pointUuid === pointUuid);
|
||||
if (point) {
|
||||
point.layer = layer;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
getWallById: (uuid) => {
|
||||
return get().walls.find(w => w.wallUuid === uuid);
|
||||
},
|
||||
|
||||
getWallPointById: (uuid) => {
|
||||
for (const wall of get().walls) {
|
||||
const point = wall.points.find(p => p.pointUuid === uuid);
|
||||
if (point) return point;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
getConnectedPoints: (uuid) => {
|
||||
const connected: Point[] = [];
|
||||
for (const wall of get().walls) {
|
||||
for (const point of wall.points) {
|
||||
if (point.pointUuid === uuid) {
|
||||
connected.push(...wall.points.filter(p => p.pointUuid !== uuid));
|
||||
}
|
||||
}
|
||||
}
|
||||
return connected;
|
||||
},
|
||||
}))
|
||||
);
|
|
@ -52,10 +52,12 @@ interface Wall {
|
|||
points: [Point, Point];
|
||||
outSideMaterial: string;
|
||||
inSideMaterial: string;
|
||||
thickness: number;
|
||||
height: number;
|
||||
wallThickness: number;
|
||||
wallHeight: number;
|
||||
}
|
||||
|
||||
type Walls = Wall[];
|
||||
|
||||
|
||||
// Aisle
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ export type PointConfig = {
|
|||
aisleOuterColor: string;
|
||||
zoneOuterColor: string;
|
||||
snappingThreshold: number;
|
||||
helperColor: string;
|
||||
};
|
||||
|
||||
export type LineConfig = {
|
||||
|
@ -135,6 +136,7 @@ export type LineConfig = {
|
|||
floorColor: string;
|
||||
aisleColor: string;
|
||||
zoneColor: string;
|
||||
deleteColor: string;
|
||||
helperColor: string;
|
||||
};
|
||||
|
||||
|
@ -305,6 +307,7 @@ export const pointConfig: PointConfig = {
|
|||
aisleOuterColor: "#FBBC05", // Outer color of the aisle points
|
||||
zoneOuterColor: "#007BFF", // Outer color of the zone points
|
||||
snappingThreshold: 1, // Threshold for snapping
|
||||
helperColor: "#C164FF", // Color of the helper lines
|
||||
};
|
||||
|
||||
export const lineConfig: LineConfig = {
|
||||
|
@ -322,6 +325,7 @@ export const lineConfig: LineConfig = {
|
|||
floorColor: "#808080", // Color of the floor lines
|
||||
aisleColor: "#FBBC05", // Color of the aisle lines
|
||||
zoneColor: "#007BFF", // Color of the zone lines
|
||||
deleteColor: "#ff0000", // Color of the line when deleting
|
||||
helperColor: "#C164FF", // Color of the helper lines
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue