added ui fro and iintegerated ui for decal modification

This commit is contained in:
2025-08-26 15:50:02 +05:30
parent 547fd1af12
commit 165325468a
9 changed files with 204 additions and 45 deletions

View File

@@ -1,23 +1,29 @@
import React from "react"; import React from "react";
interface RotationInputProps { interface RotationInputProps {
heading?: string; // Optional label for the input heading?: string;
label?: string; // Optional label for the input label?: string;
onChange: (value: string) => void; // Callback for value change onChange: (value: string) => void;
placeholder?: string; // Optional placeholder placeholder?: string;
type?: string; // Input type (e.g., text, number, email) type?: string;
value?: number; value?: number;
disabled?: boolean; // Disable the input if true disabled?: boolean;
min?: number;
max?: number;
step?: number;
} }
const RotationInput: React.FC<RotationInputProps> = ({ const RotationInput: React.FC<RotationInputProps> = ({
label = "Rotate :", // Default label label = "Rotate :",
heading = "Rotation", // Default heading heading = "Rotation",
onChange, onChange,
placeholder = "Enter value", // Default placeholder placeholder = "Enter value",
type = "number", // Default type type = "number",
value = "number", value,
disabled = false, disabled = false,
min,
max,
step,
}) => { }) => {
return ( return (
<div className="custom-input-container"> <div className="custom-input-container">
@@ -32,6 +38,9 @@ const RotationInput: React.FC<RotationInputProps> = ({
placeholder={placeholder} placeholder={placeholder}
value={value} value={value}
disabled={disabled} disabled={disabled}
min={min}
max={max}
step={step}
/> />
</div> </div>
</div> </div>

View File

@@ -1,42 +1,178 @@
import { useParams } from "react-router-dom";
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { LayeringBottomIcon, LayeringTopIcon } from "../../../icons/ExportCommonIcons"; import { LayeringBottomIcon, LayeringTopIcon } from "../../../icons/ExportCommonIcons";
import { useSocketStore } from "../../../../store/builder/store";
import InputRange from "../../../ui/inputs/InputRange"; import InputRange from "../../../ui/inputs/InputRange";
import RotationInput from "../customInput/RotationInput"; import RotationInput from "../customInput/RotationInput";
import { getUserData } from "../../../../functions/getUserData";
// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
// import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
const SelectedDecalProperties = () => { const SelectedDecalProperties = () => {
const { selectedDecal, setSelectedDecal } = useBuilderStore();
const { wallStore, floorStore } = useSceneContext();
const { updateDecal: updateDecalFromWall } = wallStore();
const { updateDecal: updateDecalFromFloor } = floorStore();
const { userId, organization } = getUserData();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { socket } = useSocketStore();
const updateBackend = (updatedData: Wall | Floor) => {
if ('wallUuid' in updatedData) {
if (projectId && updatedData) {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
wallData: updatedData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
} else if ('floorUuid' in updatedData) {
if (projectId && updatedData) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
floorData: updatedData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
}
}
const handleRotationChange = (value: number) => {
if (!selectedDecal) return;
const updatedDecal = { ...selectedDecal.decalData, decalRotation: value };
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
if ('wallUuid' in selectedDecal.decalData.decalType) {
const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal);
if (updatedWall) updateBackend(updatedWall);
} else if ('floorUuid' in selectedDecal.decalData.decalType) {
const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal);
if (updatedFloor) updateBackend(updatedFloor);
}
}
const handleScaleChange = (value: number) => {
if (!selectedDecal) return;
const updatedDecal = { ...selectedDecal.decalData, decalScale: value };
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
if ('wallUuid' in selectedDecal.decalData.decalType) {
const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal);
if (updatedWall) updateBackend(updatedWall);
} else if ('floorUuid' in selectedDecal.decalData.decalType) {
const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal);
if (updatedFloor) updateBackend(updatedFloor);
}
}
const handleOpacityChange = (value: number) => {
if (!selectedDecal) return;
const updatedDecal = { ...selectedDecal.decalData, decalOpacity: value };
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
if ('wallUuid' in selectedDecal.decalData.decalType) {
const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal);
if (updatedWall) updateBackend(updatedWall);
} else if ('floorUuid' in selectedDecal.decalData.decalType) {
const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal);
if (updatedFloor) updateBackend(updatedFloor);
}
}
const handleLayerChange = (direction: "up" | "down") => {
if (!selectedDecal) return;
const position: [number, number, number] = [...(selectedDecal.decalData.decalPosition || [0, 0, 0]),];
if (direction === "up") {
position[2] = Math.abs(position[2]);
} else {
position[2] = -Math.abs(position[2]);
}
const updatedDecal: Decal = { ...selectedDecal.decalData, decalPosition: position, };
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
if ("wallUuid" in selectedDecal.decalData.decalType) {
const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal);
if (updatedWall) updateBackend(updatedWall);
} else if ("floorUuid" in selectedDecal.decalData.decalType) {
const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal);
if (updatedFloor) updateBackend(updatedFloor);
}
};
if (!selectedDecal) return null;
return ( return (
<div className="decal-transformation-container"> <div className="decal-transformation-container">
<div className="header">Decal Properties</div> <div className="header">Decal Properties</div>
<section> <section>
<RotationInput <RotationInput
onChange={() => { }} onChange={(e) => { handleRotationChange(parseFloat(e)) }}
value={10} value={selectedDecal.decalData.decalRotation || 0}
/> />
<RotationInput <RotationInput
min={0.1}
max={10}
step={0.1}
heading="Scaling" heading="Scaling"
label="Scale :" label="Scale :"
onChange={() => { }} onChange={(e) => { handleScaleChange(parseFloat(e)) }}
value={10} value={selectedDecal.decalData.decalScale || 1}
/> />
</section> </section>
<section> <section>
<InputRange <InputRange
label="Opacity" label="Opacity"
value={1} value={selectedDecal.decalData.decalOpacity || 1}
min={0} min={0.1}
step={0.1} step={0.1}
max={1} max={1}
onChange={(value: number) => console.log(value)} onChange={(value: number) => handleOpacityChange(value)}
/> />
<div className="transformation-wrapper opacity"> <div className="transformation-wrapper opacity">
<div className="transformation-header">Layering</div> <div className="transformation-header">Layering</div>
<div className="layers-list"> <div className="layers-list">
<button className="layer-move-btn"> <button
className="layer-move-btn"
onClick={() => handleLayerChange("down")}
>
<LayeringBottomIcon /> <LayeringBottomIcon />
</button> </button>
<button className="layer-move-btn"> <button
className="layer-move-btn"
onClick={() => handleLayerChange("up")}
>
<LayeringTopIcon /> <LayeringTopIcon />
</button> </button>
</div> </div>

View File

@@ -93,7 +93,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
// debug // debug
visible={visible} visible={visible}
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]} position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
rotation={[0, 0, 0]} rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
scale={[decal.decalScale, decal.decalScale, 0.01]} scale={[decal.decalScale, decal.decalScale, 0.01]}
userData={decal} userData={decal}
onClick={(e) => { onClick={(e) => {
@@ -101,7 +101,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
if (e.object.userData.decalUuid) { if (e.object.userData.decalUuid) {
e.stopPropagation(); e.stopPropagation();
if (toolMode === 'cursor') { if (toolMode === 'cursor') {
setSelectedDecal(e.object); setSelectedDecal({ decalMesh: e.object, decalData: decal });
setSelectedWall(null); setSelectedWall(null);
setSelectedFloor(null); setSelectedFloor(null);
} else if (toolMode === '3D-Delete') { } else if (toolMode === '3D-Delete') {
@@ -131,7 +131,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
} }
}} }}
onPointerMissed={() => { onPointerMissed={() => {
if (selectedDecal && selectedDecal.userData.decalUuid === decal.decalUuid) { if (selectedDecal && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) {
setSelectedDecal(null); setSelectedDecal(null);
} }
}} }}
@@ -141,6 +141,8 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP
side={THREE.DoubleSide} side={THREE.DoubleSide}
polygonOffset polygonOffset
polygonOffsetFactor={-1} polygonOffsetFactor={-1}
transparent
opacity={decal.decalOpacity}
/> />
</Decal> </Decal>
) )

View File

@@ -167,6 +167,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
decalId: 'Default Decal', decalId: 'Default Decal',
decalPosition: [0, 0, wall.wallThickness / 2 + 0.001], decalPosition: [0, 0, wall.wallThickness / 2 + 0.001],
decalRotation: 0, decalRotation: 0,
decalOpacity: 1,
decalScale: 1, decalScale: 1,
decalType: { type: 'Wall', wallUuid: wall.wallUuid } decalType: { type: 'Wall', wallUuid: wall.wallUuid }
} }
@@ -183,7 +184,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
<MeshDiscardMaterial /> <MeshDiscardMaterial />
{wall.decals.map((decal) => ( {wall.decals.map((decal) => (
<DecalInstance parent={wall} zPosition={wall.wallThickness / 2 + 0.001} visible={visible} key={decal.decalUuid} decal={decal} /> <DecalInstance parent={wall} visible={visible} key={decal.decalUuid} decal={decal} />
))} ))}
</mesh> </mesh>
</mesh> </mesh>

View File

@@ -165,7 +165,7 @@ export default function PostProcessing() {
)} )}
{selectedDecal && ( {selectedDecal && (
<Outline <Outline
selection={selectedDecal} selection={selectedDecal.decalMesh}
selectionLayer={10} selectionLayer={10}
width={2000} width={2000}
blendFunction={BlendFunction.ALPHA} blendFunction={BlendFunction.ALPHA}

View File

@@ -38,7 +38,7 @@ interface BuilderState {
zoneColor: string; zoneColor: string;
// Decal Settings // Decal Settings
selectedDecal: Object3D | null; selectedDecal: { decalMesh: Object3D, decalData: Decal } | null;
deletableDecal: Object3D | null; deletableDecal: Object3D | null;
// Aisle General // Aisle General
@@ -87,7 +87,7 @@ interface BuilderState {
setZoneColor: (color: string) => void; setZoneColor: (color: string) => void;
// Setters - Decal // Setters - Decal
setSelectedDecal: (decal: Object3D | null) => void; setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => void;
setDeletableDecal: (decal: Object3D | null) => void; setDeletableDecal: (decal: Object3D | null) => void;
// Setters - Aisle General // Setters - Aisle General
@@ -290,7 +290,7 @@ export const useBuilderStore = create<BuilderState>()(
// === Setters: Decal === // === Setters: Decal ===
setSelectedDecal: (decal: Object3D | null) => { setSelectedDecal: (decal: { decalMesh: Object3D, decalData: Decal } | null) => {
set((state) => { set((state) => {
state.selectedDecal = decal; state.selectedDecal = decal;
}) })

View File

@@ -20,7 +20,7 @@ interface FloorStore {
setDepth: (uuid: string, depth: number) => void; setDepth: (uuid: string, depth: number) => void;
setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void; setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void;
addDecal: (floors: string, decal: Decal) => void; addDecal: (floors: string, decal: Decal) => void;
updateDecal: (decalUuid: string, decal: Decal) => void; updateDecal: (decalUuid: string, decal: Decal) => Floor | undefined;
removeDecal: (decalUuid: string) => Floor | undefined; removeDecal: (decalUuid: string) => Floor | undefined;
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void;
updateDecalRotation: (decalUuid: string, rotation: number) => void; updateDecalRotation: (decalUuid: string, rotation: number) => void;
@@ -203,15 +203,20 @@ export const createFloorStore = () => {
} }
}), }),
updateDecal: (decalUuid, updatedDecal) => set(state => { updateDecal: (decalUuid, updatedDecal) => {
for (const floor of state.floors) { let affectedFloor: Floor | undefined;
const index = floor.decals.findIndex(d => d.decalUuid === decalUuid); set(state => {
if (index !== -1) { for (const floor of state.floors) {
floor.decals[index] = updatedDecal; const index = floor.decals.findIndex(d => d.decalUuid === decalUuid);
break; if (index !== -1) {
floor.decals[index] = updatedDecal;
affectedFloor = JSON.parse(JSON.stringify(floor));
break;
}
} }
} })
}), return affectedFloor;
},
removeDecal: (decalUuid) => { removeDecal: (decalUuid) => {
let affectedFloor: Floor | undefined; let affectedFloor: Floor | undefined;

View File

@@ -10,7 +10,7 @@ interface WallStore {
clearWalls: () => void; clearWalls: () => void;
removeWallByPoints: (Points: [Point, Point]) => Wall | undefined; removeWallByPoints: (Points: [Point, Point]) => Wall | undefined;
addDecal: (wallUuid: string, decal: Decal) => void; addDecal: (wallUuid: string, decal: Decal) => void;
updateDecal: (decalUuid: string, decal: Decal) => void; updateDecal: (decalUuid: string, decal: Decal) => Wall | undefined;
removeDecal: (decalUuid: string) => Wall | undefined; removeDecal: (decalUuid: string) => Wall | undefined;
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void;
updateDecalRotation: (decalUuid: string, rotation: number) => void; updateDecalRotation: (decalUuid: string, rotation: number) => void;
@@ -90,14 +90,19 @@ export const createWallStore = () => {
} }
}), }),
updateDecal: (decalUuid, decal) => set((state) => { updateDecal: (decalUuid, decal) => {
for (const wall of state.walls) { let affectedWall: Wall | undefined;
const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid); set((state) => {
if (decalToUpdate) { for (const wall of state.walls) {
Object.assign(decalToUpdate, decal); const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid);
if (decalToUpdate) {
Object.assign(decalToUpdate, decal);
affectedWall = JSON.parse(JSON.stringify(wall));
}
} }
} });
}), return affectedWall;
},
removeDecal: (decalUuid) => { removeDecal: (decalUuid) => {
let affectedWall: Wall | undefined; let affectedWall: Wall | undefined;

View File

@@ -88,6 +88,7 @@ interface Decal {
decalType: WallDecal | FloorDecal; decalType: WallDecal | FloorDecal;
decalPosition: [number, number, number]; decalPosition: [number, number, number];
decalRotation: number; decalRotation: number;
decalOpacity: number;
decalScale: number; decalScale: number;
} }