Refactor MoveControls and TransformControls for improved asset manipulation

- Cleaned up MoveControls component by removing unused variables and optimizing event handling.
- Enhanced asset movement logic with better key event detection and state management.
- Removed deprecated transform mode state from store.
- Updated worldTypes to remove unnecessary positionY property.
- Introduced TransformControls component for handling object transformations (translate/rotate) with proper state management and backend updates.
- Implemented event handling for mouse actions and keyboard shortcuts to toggle transformation modes.
This commit is contained in:
Jerald-Golden-B 2025-05-13 12:58:04 +05:30
parent b1569e64ed
commit ecab03c5f0
16 changed files with 1401 additions and 1278 deletions

View File

@ -234,14 +234,16 @@ const Assets: React.FC = () => {
alt={asset.filename} alt={asset.filename}
className="asset-image" className="asset-image"
onPointerDown={() => { onPointerDown={() => {
setSelectedItem({ if (asset.category !== 'Feneration') {
name: asset.filename, setSelectedItem({
id: asset.AssetID, name: asset.filename,
type: id: asset.AssetID,
asset.type === "undefined" type:
? undefined asset.type === "undefined"
: asset.type, ? undefined
}); : asset.type,
});
}
}} }}
/> />
<div className="asset-name"> <div className="asset-name">

View File

@ -29,7 +29,6 @@ import {
useSocketStore, useSocketStore,
useToggleView, useToggleView,
useToolMode, useToolMode,
useTransformMode,
useActiveSubTool, useActiveSubTool,
} from "../../store/store"; } from "../../store/store";
import useToggleStore from "../../store/useUIToggleStore"; import useToggleStore from "../../store/useUIToggleStore";
@ -61,7 +60,6 @@ const Tools: React.FC = () => {
const { setAddAction } = useAddAction(); const { setAddAction } = useAddAction();
const { setSelectedWallItem } = useSelectedWallItem(); const { setSelectedWallItem } = useSelectedWallItem();
const { setTransformMode } = useTransformMode();
const { setDeletePointOrLine } = useDeletePointOrLine(); const { setDeletePointOrLine } = useDeletePointOrLine();
const { setToolMode } = useToolMode(); const { setToolMode } = useToolMode();
const { activeTool, setActiveTool } = useActiveTool(); const { activeTool, setActiveTool } = useActiveTool();
@ -126,7 +124,6 @@ const Tools: React.FC = () => {
setToolMode(null); setToolMode(null);
setDeleteTool(false); setDeleteTool(false);
setAddAction(null); setAddAction(null);
setTransformMode(null);
setDeletePointOrLine(false); setDeletePointOrLine(false);
setRefTextUpdate((prevUpdate) => prevUpdate - 1); setRefTextUpdate((prevUpdate) => prevUpdate - 1);
@ -134,20 +131,6 @@ const Tools: React.FC = () => {
case "cursor": case "cursor":
if (toggleView) { if (toggleView) {
setToolMode('move'); setToolMode('move');
} else {
setTransformMode("translate");
}
break;
case "Rotate":
if (!toggleView) {
setTransformMode("rotate");
}
break;
case "Scale":
if (!toggleView) {
setTransformMode("scale");
} }
break; break;

View File

@ -54,57 +54,57 @@ import NavMesh from "../simulation/vehicle/navMesh/navMesh";
import CalculateAreaGroup from "./groups/calculateAreaGroup"; import CalculateAreaGroup from "./groups/calculateAreaGroup";
export default function Builder() { export default function Builder() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
const csg = useRef(); // Reference for CSG object, used for 3D modeling. const csg = useRef(); // Reference for CSG object, used for 3D modeling.
const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects.
const scene = useRef() as Types.RefScene; // Reference to the scene. const scene = useRef() as Types.RefScene; // Reference to the scene.
const camera = useRef() as Types.RefCamera; // Reference to the camera object. const camera = useRef() as Types.RefCamera; // Reference to the camera object.
const controls = useRef<any>(); // Reference to the controls object. const controls = useRef<any>(); // Reference to the controls object.
const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene. const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene.
const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control.
// Assigning the scene and camera from the Three.js state to the references. // Assigning the scene and camera from the Three.js state to the references.
scene.current = state.scene; scene.current = state.scene;
camera.current = state.camera; camera.current = state.camera;
controls.current = state.controls; controls.current = state.controls;
raycaster.current = state.raycaster; raycaster.current = state.raycaster;
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference. const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
const grid = useRef() as any; // Reference for a grid object for raycaster reference. const grid = useRef() as any; // Reference for a grid object for raycaster reference.
const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line.
const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end).
const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc...
const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active.
const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point.
const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start).
const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items. const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items.
const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active. const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active.
const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation.
const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn.
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn.
const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn. const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn.
const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw.
const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not.
const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed.
const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf).
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation.
const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group.
const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn.
const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created.
const floorGroupAisle = useRef() as Types.RefGroup; const floorGroupAisle = useRef() as Types.RefGroup;
const zoneGroup = useRef() as Types.RefGroup; const zoneGroup = useRef() as Types.RefGroup;
const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls.
const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted.
const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted.
const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted.
const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted.
const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted.
const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc...
const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position. const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position.
const [selectedItemsIndex, setSelectedItemsIndex] = useState<Types.Number | null>(null); // State for tracking the index of the selected item. const [selectedItemsIndex, setSelectedItemsIndex] = useState<Types.Number | null>(null); // State for tracking the index of the selected item.
const { activeLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. const { activeLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx.
@ -120,42 +120,39 @@ export default function Builder() {
const { setWalls } = useWalls(); const { setWalls } = useWalls();
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
// const loader = new GLTFLoader(); // const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader(); // const dracoLoader = new DRACOLoader();
// dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); // dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
// loader.setDRACOLoader(dracoLoader); // loader.setDRACOLoader(dracoLoader);
////////// Assest Configuration Values ////////// ////////// Assest Configuration Values //////////
const AssetConfigurations: Types.AssetConfigurations = { const AssetConfigurations: Types.AssetConfigurations = {
arch: { arch: {
modelUrl: arch, modelUrl: arch,
scale: [0.75, 0.75, 0.75], scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5], csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0], csgposition: [0, 2, 0],
positionY: () => 0, type: "Fixed-Move",
type: "Fixed-Move", },
}, door: {
door: { modelUrl: door,
modelUrl: door, scale: [0.75, 0.75, 0.75],
scale: [0.75, 0.75, 0.75], csgscale: [2, 4, 0.5],
csgscale: [2, 4, 0.5], csgposition: [0, 2, 0],
csgposition: [0, 2, 0], type: "Fixed-Move",
positionY: () => 0, },
type: "Fixed-Move", window: {
}, modelUrl: Window,
window: { scale: [0.75, 0.75, 0.75],
modelUrl: Window, csgscale: [5, 3, 0.5],
scale: [0.75, 0.75, 0.75], csgposition: [0, 1.5, 0],
csgscale: [5, 3, 0.5], type: "Free-Move",
csgposition: [0, 1.5, 0], },
positionY: (intersectionPoint) => intersectionPoint.point.y, };
type: "Free-Move",
},
};
////////// All Toggle's ////////// ////////// All Toggle's //////////
useEffect(() => { useEffect(() => {
setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); setRefTextUpdate((prevUpdate: number) => prevUpdate - 1);
@ -177,167 +174,167 @@ export default function Builder() {
} }
}, [toggleView]); }, [toggleView]);
useEffect(() => { useEffect(() => {
THREE.Cache.clear(); THREE.Cache.clear();
THREE.Cache.enabled = true; THREE.Cache.enabled = true;
}, []); }, []);
useEffect(() => { useEffect(() => {
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
async function fetchVisibility() { async function fetchVisibility() {
const visibility = await findEnvironment( const visibility = await findEnvironment(
organization, organization,
localStorage.getItem("userId")! localStorage.getItem("userId")!
); );
if (visibility) { if (visibility) {
setRoofVisibility(visibility.roofVisibility); setRoofVisibility(visibility.roofVisibility);
setWallVisibility(visibility.wallVisibility); setWallVisibility(visibility.wallVisibility);
setShadows(visibility.shadowVisibility); setShadows(visibility.shadowVisibility);
setRenderDistance(visibility.renderDistance); setRenderDistance(visibility.renderDistance);
setLimitDistance(visibility.limitDistance); setLimitDistance(visibility.limitDistance);
} }
} }
fetchVisibility(); fetchVisibility();
}, []); }, []);
////////// UseFrame is Here ////////// ////////// UseFrame is Here //////////
useFrame(() => { useFrame(() => {
if (toolMode) { if (toolMode) {
Draw( Draw(
state, state,
plane, plane,
cursorPosition, cursorPosition,
floorPlanGroupPoint, floorPlanGroupPoint,
floorPlanGroupLine, floorPlanGroupLine,
snappedPoint, snappedPoint,
isSnapped, isSnapped,
isSnappedUUID, isSnappedUUID,
line, line,
lines, lines,
ispreSnapped, ispreSnapped,
floorPlanGroup, floorPlanGroup,
ReferenceLineMesh, ReferenceLineMesh,
LineCreated, LineCreated,
setRefTextUpdate, setRefTextUpdate,
Tube, Tube,
anglesnappedPoint, anglesnappedPoint,
isAngleSnapped, isAngleSnapped,
toolMode toolMode
); );
} }
}); });
////////// Return ////////// ////////// Return //////////
return ( return (
<> <>
<Ground grid={grid} plane={plane} /> <Ground grid={grid} plane={plane} />
<DistanceText key={toggleView} /> <DistanceText key={toggleView} />
<ReferenceDistanceText <ReferenceDistanceText
key={refTextupdate} key={refTextupdate}
line={ReferenceLineMesh.current} line={ReferenceLineMesh.current}
/> />
<SocketResponses <SocketResponses
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
lines={lines} lines={lines}
floorGroup={floorGroup} floorGroup={floorGroup}
floorGroupAisle={floorGroupAisle} floorGroupAisle={floorGroupAisle}
scene={scene} scene={scene}
onlyFloorlines={onlyFloorlines} onlyFloorlines={onlyFloorlines}
AssetConfigurations={AssetConfigurations} AssetConfigurations={AssetConfigurations}
itemsGroup={itemsGroup} itemsGroup={itemsGroup}
isTempLoader={isTempLoader} isTempLoader={isTempLoader}
tempLoader={tempLoader} tempLoader={tempLoader}
currentLayerPoint={currentLayerPoint} currentLayerPoint={currentLayerPoint}
floorPlanGroupPoint={floorPlanGroupPoint} floorPlanGroupPoint={floorPlanGroupPoint}
floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupLine={floorPlanGroupLine}
zoneGroup={zoneGroup} zoneGroup={zoneGroup}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
/> />
<WallsAndWallItems <WallsAndWallItems
CSGGroup={CSGGroup} CSGGroup={CSGGroup}
AssetConfigurations={AssetConfigurations} AssetConfigurations={AssetConfigurations}
setSelectedItemsIndex={setSelectedItemsIndex} setSelectedItemsIndex={setSelectedItemsIndex}
selectedItemsIndex={selectedItemsIndex} selectedItemsIndex={selectedItemsIndex}
currentWallItem={currentWallItem} currentWallItem={currentWallItem}
csg={csg} csg={csg}
lines={lines} lines={lines}
hoveredDeletableWallItem={hoveredDeletableWallItem} hoveredDeletableWallItem={hoveredDeletableWallItem}
/> />
<FloorItemsGroup <FloorItemsGroup
itemsGroup={itemsGroup} itemsGroup={itemsGroup}
hoveredDeletableFloorItem={hoveredDeletableFloorItem} hoveredDeletableFloorItem={hoveredDeletableFloorItem}
AttachedObject={AttachedObject} AttachedObject={AttachedObject}
floorGroup={floorGroup} floorGroup={floorGroup}
tempLoader={tempLoader} tempLoader={tempLoader}
isTempLoader={isTempLoader} isTempLoader={isTempLoader}
plane={plane} plane={plane}
/> />
<FloorGroup <FloorGroup
floorGroup={floorGroup} floorGroup={floorGroup}
lines={lines} lines={lines}
referencePole={referencePole} referencePole={referencePole}
hoveredDeletablePillar={hoveredDeletablePillar} hoveredDeletablePillar={hoveredDeletablePillar}
/> />
<FloorPlanGroup <FloorPlanGroup
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint} floorPlanGroupPoint={floorPlanGroupPoint}
floorGroup={floorGroup} floorGroup={floorGroup}
currentLayerPoint={currentLayerPoint} currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
hoveredDeletablePoint={hoveredDeletablePoint} hoveredDeletablePoint={hoveredDeletablePoint}
hoveredDeletableLine={hoveredDeletableLine} hoveredDeletableLine={hoveredDeletableLine}
plane={plane} plane={plane}
line={line} line={line}
lines={lines} lines={lines}
onlyFloorline={onlyFloorline} onlyFloorline={onlyFloorline}
onlyFloorlines={onlyFloorlines} onlyFloorlines={onlyFloorlines}
ReferenceLineMesh={ReferenceLineMesh} ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated} LineCreated={LineCreated}
isSnapped={isSnapped} isSnapped={isSnapped}
ispreSnapped={ispreSnapped} ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint} snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID} isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped} isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint} anglesnappedPoint={anglesnappedPoint}
/> />
<ZoneGroup /> <ZoneGroup />
<FloorGroupAilse <FloorGroupAilse
floorGroupAisle={floorGroupAisle} floorGroupAisle={floorGroupAisle}
plane={plane} plane={plane}
floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint} floorPlanGroupPoint={floorPlanGroupPoint}
line={line} line={line}
lines={lines} lines={lines}
currentLayerPoint={currentLayerPoint} currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
ReferenceLineMesh={ReferenceLineMesh} ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated} LineCreated={LineCreated}
isSnapped={isSnapped} isSnapped={isSnapped}
ispreSnapped={ispreSnapped} ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint} snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID} isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped} isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint} anglesnappedPoint={anglesnappedPoint}
/> />
<MeasurementTool /> <MeasurementTool />
<CalculateAreaGroup /> <CalculateAreaGroup />
<NavMesh lines={lines} /> <NavMesh lines={lines} />
</> </>
); );
} }

View File

@ -4,51 +4,51 @@ import { useDeleteTool } from "../../../store/store";
import { useRef } from "react"; import { useRef } from "react";
export interface CsgProps { export interface CsgProps {
position: THREE.Vector3 | [number, number, number]; position: THREE.Vector3 | [number, number, number];
scale: THREE.Vector3 | [number, number, number]; scale: THREE.Vector3 | [number, number, number];
model: THREE.Object3D; model: THREE.Object3D;
hoveredDeletableWallItem: { current: THREE.Mesh | null }; hoveredDeletableWallItem: { current: THREE.Mesh | null };
} }
export const Csg: React.FC<CsgProps> = (props) => { export const Csg: React.FC<CsgProps> = (props) => {
const { deleteTool } = useDeleteTool(); const { deleteTool } = useDeleteTool();
const modelRef = useRef<THREE.Object3D>(); const modelRef = useRef<THREE.Object3D>();
const originalMaterials = useRef<Map<THREE.Mesh, THREE.Material>>(new Map()); const originalMaterials = useRef<Map<THREE.Mesh, THREE.Material>>(new Map());
const handleHover = (hovered: boolean, object: THREE.Mesh | null) => { const handleHover = (hovered: boolean, object: THREE.Mesh | null) => {
if (modelRef.current && deleteTool) { if (modelRef.current && deleteTool) {
modelRef.current.traverse((child) => { modelRef.current.traverse((child) => {
if (child instanceof THREE.Mesh) { if (child instanceof THREE.Mesh) {
if (!originalMaterials.current.has(child)) { if (!originalMaterials.current.has(child)) {
originalMaterials.current.set(child, child.material); originalMaterials.current.set(child, child.material);
} }
child.material = child.material.clone(); child.material = child.material.clone();
child.material.color.set(hovered && deleteTool ? 0xff0000 : (originalMaterials.current.get(child) as any).color); child.material.color.set(hovered && deleteTool ? 0xff0000 : (originalMaterials.current.get(child) as any).color);
}
});
} }
}); props.hoveredDeletableWallItem.current = hovered ? object : null;
} };
props.hoveredDeletableWallItem.current = hovered ? object : null;
};
return ( return (
<Geometry>
<Subtraction {...props}>
<Geometry> <Geometry>
<Base geometry={new THREE.BoxGeometry()} /> <Subtraction {...props}>
<Geometry>
<Base geometry={new THREE.BoxGeometry()} />
</Geometry>
</Subtraction>
<primitive
object={props.model}
ref={modelRef}
onPointerOver={(e: any) => {
e.stopPropagation();
handleHover(true, e.object.parent);
}}
onPointerOut={(e: any) => {
e.stopPropagation();
handleHover(false, null);
}}
/>
</Geometry> </Geometry>
</Subtraction> );
<primitive
object={props.model}
ref={modelRef}
onPointerOver={(e: any) => {
e.stopPropagation();
handleHover(true, e.object.parent);
}}
onPointerOut={(e: any) => {
e.stopPropagation();
handleHover(false, null);
}}
/>
</Geometry>
);
}; };

View File

@ -26,7 +26,6 @@ export default function addFloorToScene(
if (materialCache.has(materialKey)) { if (materialCache.has(materialKey)) {
material = materialCache.get(materialKey) as THREE.Material; material = materialCache.get(materialKey) as THREE.Material;
// } else {
} else { } else {
const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath); const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath);
// const floorTexture = textureLoader.load(texturePath); // const floorTexture = textureLoader.load(texturePath);

View File

@ -35,7 +35,7 @@ async function AddWallItems(
}); });
const config = AssetConfigurations[selected]; const config = AssetConfigurations[selected];
let positionY = typeof config.positionY === 'function' ? config.positionY(intersectionPoint) : config.positionY; let positionY = config.type === 'Fixed-Move' ? 0 : intersectionPoint.point.y;
if (positionY === 0) { if (positionY === 0) {
positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height; positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height;
} }

View File

@ -11,7 +11,6 @@ import {
useSelectedItem, useSelectedItem,
useSocketStore, useSocketStore,
useToggleView, useToggleView,
useTransformMode,
} from "../../../store/store"; } from "../../../store/store";
import { useEffect } from "react"; import { useEffect } from "react";
import * as THREE from "three"; import * as THREE from "three";
@ -59,7 +58,6 @@ const FloorItemsGroup = ({
const { camMode } = useCamMode(); const { camMode } = useCamMode();
const { deleteTool } = useDeleteTool(); const { deleteTool } = useDeleteTool();
const { setDeletableFloorItem } = useDeletableFloorItem(); const { setDeletableFloorItem } = useDeletableFloorItem();
const { transformMode } = useTransformMode();
const { setSelectedFloorItem } = useSelectedFloorItem(); const { setSelectedFloorItem } = useSelectedFloorItem();
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
const { selectedItem, setSelectedItem } = useSelectedItem(); const { selectedItem, setSelectedItem } = useSelectedItem();
@ -257,9 +255,8 @@ const FloorItemsGroup = ({
socket socket
); );
} }
const Mode = transformMode;
if (Mode !== null || activeTool === "cursor") { if (activeTool === "cursor") {
if (!itemsGroup.current) return; if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects( let intersects = raycaster.intersectObjects(
itemsGroup.current.children, itemsGroup.current.children,
@ -296,9 +293,8 @@ const FloorItemsGroup = ({
isLeftMouseDown = false; isLeftMouseDown = false;
if (drag) return; if (drag) return;
const Mode = transformMode;
if (Mode !== null || activeTool === "cursor") { if (activeTool === "cursor") {
if (!itemsGroup.current) return; if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects( let intersects = raycaster.intersectObjects(
itemsGroup.current.children, itemsGroup.current.children,
@ -412,16 +408,7 @@ const FloorItemsGroup = ({
canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver); canvasElement.removeEventListener("dragover", onDragOver);
}; };
}, [ }, [deleteTool, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);
deleteTool,
transformMode,
controls,
selectedItem,
state.camera,
state.pointer,
activeTool,
activeModule,
]);
useFrame(() => { useFrame(() => {
if (controls) if (controls)

View File

@ -1,12 +1,12 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { import {
useDeleteTool, useDeleteTool,
useDeletePointOrLine, useDeletePointOrLine,
useObjectPosition, useObjectPosition,
useObjectRotation, useObjectRotation,
useSelectedWallItem, useSelectedWallItem,
useSocketStore, useSocketStore,
useWallItems, useWallItems,
} from "../../../store/store"; } from "../../../store/store";
import { Csg } from "../csg/csg"; import { Csg } from "../csg/csg";
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
@ -20,276 +20,276 @@ import AddWallItems from "../geomentries/walls/addWallItems";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
const WallItemsGroup = ({ const WallItemsGroup = ({
currentWallItem, currentWallItem,
AssetConfigurations, AssetConfigurations,
hoveredDeletableWallItem, hoveredDeletableWallItem,
selectedItemsIndex, selectedItemsIndex,
setSelectedItemsIndex, setSelectedItemsIndex,
CSGGroup, CSGGroup,
}: any) => { }: any) => {
const state = useThree(); const state = useThree();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { pointer, camera, raycaster } = state; const { pointer, camera, raycaster } = state;
const { deleteTool } = useDeleteTool(); const { deleteTool } = useDeleteTool();
const { wallItems, setWallItems } = useWallItems(); const { wallItems, setWallItems } = useWallItems();
const { setObjectPosition } = useObjectPosition(); const { setObjectPosition } = useObjectPosition();
const { setObjectRotation } = useObjectRotation(); const { setObjectRotation } = useObjectRotation();
const { deletePointOrLine } = useDeletePointOrLine(); const { deletePointOrLine } = useDeletePointOrLine();
const { setSelectedWallItem } = useSelectedWallItem(); const { setSelectedWallItem } = useSelectedWallItem();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
useEffect(() => { useEffect(() => {
// Load Wall Items from the backend // Load Wall Items from the backend
loadInitialWallItems(setWallItems, AssetConfigurations); loadInitialWallItems(setWallItems, AssetConfigurations);
}, []); }, []);
////////// Update the Scale value changes in thewallItems State ////////// ////////// Update the Scale value changes in thewallItems State //////////
////////// Update the Position value changes in the selected item ////////// ////////// Update the Position value changes in the selected item //////////
////////// Update the Rotation value changes in the selected item ////////// ////////// Update the Rotation value changes in the selected item //////////
useEffect(() => { useEffect(() => {
const canvasElement = state.gl.domElement; const canvasElement = state.gl.domElement;
function handlePointerMove(e: any) { function handlePointerMove(e: any) {
if ( if (
selectedItemsIndex !== null && selectedItemsIndex !== null &&
!deletePointOrLine && !deletePointOrLine &&
e.buttons === 1 e.buttons === 1
) { ) {
const Raycaster = state.raycaster; const Raycaster = state.raycaster;
const intersects = Raycaster.intersectObjects( const intersects = Raycaster.intersectObjects(
CSGGroup.current?.children[0].children!, CSGGroup.current?.children[0].children!,
true true
); );
const Object = intersects.find((child) => const Object = intersects.find((child) =>
child.object.name.includes("WallRaycastReference") child.object.name.includes("WallRaycastReference")
); );
if (Object) { if (Object) {
(state.controls as any)!.enabled = false; (state.controls as any)!.enabled = false;
setWallItems((prevItems: any) => { setWallItems((prevItems: any) => {
const updatedItems = [...prevItems]; const updatedItems = [...prevItems];
let position: [number, number, number] = [0, 0, 0]; let position: [number, number, number] = [0, 0, 0];
if (updatedItems[selectedItemsIndex].type === "Fixed-Move") { if (updatedItems[selectedItemsIndex].type === "Fixed-Move") {
position = [ position = [
Object!.point.x, Object!.point.x,
Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) * Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) *
CONSTANTS.wallConfig.height, CONSTANTS.wallConfig.height,
Object!.point.z, Object!.point.z,
]; ];
} else if (updatedItems[selectedItemsIndex].type === "Free-Move") { } else if (updatedItems[selectedItemsIndex].type === "Free-Move") {
position = [Object!.point.x, Object!.point.y, Object!.point.z]; position = [Object!.point.x, Object!.point.y, Object!.point.z];
}
requestAnimationFrame(() => {
setObjectPosition(new THREE.Vector3(...position));
setObjectRotation({
x: THREE.MathUtils.radToDeg(Object!.object.rotation.x),
y: THREE.MathUtils.radToDeg(Object!.object.rotation.y),
z: THREE.MathUtils.radToDeg(Object!.object.rotation.z),
});
});
updatedItems[selectedItemsIndex] = {
...updatedItems[selectedItemsIndex],
position: position,
quaternion:
Object!.object.quaternion.clone() as Types.QuaternionType,
};
return updatedItems;
});
}
} }
requestAnimationFrame(() => {
setObjectPosition(new THREE.Vector3(...position));
setObjectRotation({
x: THREE.MathUtils.radToDeg(Object!.object.rotation.x),
y: THREE.MathUtils.radToDeg(Object!.object.rotation.y),
z: THREE.MathUtils.radToDeg(Object!.object.rotation.z),
});
});
updatedItems[selectedItemsIndex] = {
...updatedItems[selectedItemsIndex],
position: position,
quaternion:
Object!.object.quaternion.clone() as Types.QuaternionType,
};
return updatedItems;
});
} }
}
}
async function handlePointerUp() { async function handlePointerUp() {
const Raycaster = state.raycaster; const Raycaster = state.raycaster;
const intersects = Raycaster.intersectObjects( const intersects = Raycaster.intersectObjects(
CSGGroup.current?.children[0].children!, CSGGroup.current?.children[0].children!,
true true
);
const Object = intersects.find((child) =>
child.object.name.includes("WallRaycastReference")
);
if (Object) {
if (selectedItemsIndex !== null) {
let currentItem: any = null;
setWallItems((prevItems: any) => {
const updatedItems = [...prevItems];
const WallItemsForStorage = updatedItems.map((item) => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
currentItem = updatedItems[selectedItemsIndex];
localStorage.setItem(
"WallItems",
JSON.stringify(WallItemsForStorage)
); );
return updatedItems; const Object = intersects.find((child) =>
}); child.object.name.includes("WallRaycastReference")
);
if (Object) {
if (selectedItemsIndex !== null) {
let currentItem: any = null;
setWallItems((prevItems: any) => {
const updatedItems = [...prevItems];
const WallItemsForStorage = updatedItems.map((item) => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
setTimeout(async () => { currentItem = updatedItems[selectedItemsIndex];
const email = localStorage.getItem("email"); localStorage.setItem(
const organization = email!.split("@")[1].split(".")[0]; "WallItems",
JSON.stringify(WallItemsForStorage)
);
return updatedItems;
});
//REST setTimeout(async () => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
// await setWallItem( //REST
// organization,
// currentItem?.model?.uuid,
// currentItem.modelName,
// currentItem.type!,
// currentItem.csgposition!,
// currentItem.csgscale!,
// currentItem.position,
// currentItem.quaternion,
// currentItem.scale!,
// )
//SOCKET // await setWallItem(
// organization,
// currentItem?.model?.uuid,
// currentItem.modelName,
// currentItem.type!,
// currentItem.csgposition!,
// currentItem.csgscale!,
// currentItem.position,
// currentItem.quaternion,
// currentItem.scale!,
// )
const data = { //SOCKET
organization: organization,
modelUuid: currentItem.model?.uuid!,
modelName: currentItem.modelName!,
type: currentItem.type!,
csgposition: currentItem.csgposition!,
csgscale: currentItem.csgscale!,
position: currentItem.position!,
quaternion: currentItem.quaternion,
scale: currentItem.scale!,
socketId: socket.id,
};
socket.emit("v1:wallItems:set", data); const data = {
}, 0); organization: organization,
(state.controls as any)!.enabled = true; modelUuid: currentItem.model?.uuid!,
modelName: currentItem.modelName!,
type: currentItem.type!,
csgposition: currentItem.csgposition!,
csgscale: currentItem.csgscale!,
position: currentItem.position!,
quaternion: currentItem.quaternion,
scale: currentItem.scale!,
socketId: socket.id,
};
socket.emit("v1:wallItems:set", data);
}, 0);
(state.controls as any)!.enabled = true;
}
}
} }
}
}
canvasElement.addEventListener("pointermove", handlePointerMove); canvasElement.addEventListener("pointermove", handlePointerMove);
canvasElement.addEventListener("pointerup", handlePointerUp); canvasElement.addEventListener("pointerup", handlePointerUp);
return () => { return () => {
canvasElement.removeEventListener("pointermove", handlePointerMove); canvasElement.removeEventListener("pointermove", handlePointerMove);
canvasElement.removeEventListener("pointerup", handlePointerUp); canvasElement.removeEventListener("pointerup", handlePointerUp);
}; };
}, [selectedItemsIndex]); }, [selectedItemsIndex]);
useEffect(() => { useEffect(() => {
const canvasElement = state.gl.domElement; const canvasElement = state.gl.domElement;
let drag = false; let drag = false;
let isLeftMouseDown = false; let isLeftMouseDown = false;
const onMouseDown = (evt: any) => { const onMouseDown = (evt: any) => {
if (evt.button === 0) { if (evt.button === 0) {
isLeftMouseDown = true; isLeftMouseDown = true;
drag = false; drag = false;
} }
}; };
const onMouseUp = (evt: any) => { const onMouseUp = (evt: any) => {
if (evt.button === 0) { if (evt.button === 0) {
isLeftMouseDown = false; isLeftMouseDown = false;
if (!drag && deleteTool && activeModule === "builder") { if (!drag && deleteTool && activeModule === "builder") {
DeleteWallItems( DeleteWallItems(
hoveredDeletableWallItem, hoveredDeletableWallItem,
setWallItems, setWallItems,
wallItems, wallItems,
socket socket
); );
}
}
};
const onMouseMove = () => {
if (isLeftMouseDown) {
drag = true;
}
};
const onDrop = (event: any) => {
if (!event.dataTransfer?.files[0]) return;
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
if (AssetConfigurations[event.dataTransfer.files[0].name.split(".")[0]]) {
const selected = event.dataTransfer.files[0].name.split(".")[0];
if (AssetConfigurations[selected]?.type) {
AddWallItems(
selected,
raycaster,
CSGGroup,
AssetConfigurations,
setWallItems,
socket
);
}
event.preventDefault();
}
};
const onDragOver = (event: any) => {
event.preventDefault();
};
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [deleteTool, wallItems]);
useEffect(() => {
if (deleteTool && activeModule === "builder") {
handleMeshMissed(
currentWallItem,
setSelectedWallItem,
setSelectedItemsIndex
);
setSelectedWallItem(null);
setSelectedItemsIndex(null);
} }
} }, [deleteTool]);
};
const onMouseMove = () => { return (
if (isLeftMouseDown) { <>
drag = true; {wallItems.map((item: Types.WallItem, index: number) => (
} <group
}; key={index}
position={item.position}
const onDrop = (event: any) => { quaternion={item.quaternion}
if (!event.dataTransfer?.files[0]) return; scale={item.scale}
>
pointer.x = (event.clientX / window.innerWidth) * 2 - 1; <Csg
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; position={item.csgposition!}
raycaster.setFromCamera(pointer, camera); scale={item.csgscale!}
model={item.model!}
if (AssetConfigurations[event.dataTransfer.files[0].name.split(".")[0]]) { hoveredDeletableWallItem={hoveredDeletableWallItem}
const selected = event.dataTransfer.files[0].name.split(".")[0]; />
</group>
if (AssetConfigurations[selected]?.type) { ))}
AddWallItems( </>
selected, );
raycaster,
CSGGroup,
AssetConfigurations,
setWallItems,
socket
);
}
event.preventDefault();
}
};
const onDragOver = (event: any) => {
event.preventDefault();
};
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [deleteTool, wallItems]);
useEffect(() => {
if (deleteTool && activeModule === "builder") {
handleMeshMissed(
currentWallItem,
setSelectedWallItem,
setSelectedItemsIndex
);
setSelectedWallItem(null);
setSelectedItemsIndex(null);
}
}, [deleteTool]);
return (
<>
{wallItems.map((item: Types.WallItem, index: number) => (
<group
key={index}
position={item.position}
quaternion={item.quaternion}
scale={item.scale}
>
<Csg
position={item.csgposition!}
scale={item.csgscale!}
model={item.model!}
hoveredDeletableWallItem={hoveredDeletableWallItem}
/>
</group>
))}
</>
);
}; };
export default WallItemsGroup; export default WallItemsGroup;

View File

@ -1,11 +1,10 @@
import { Geometry } from "@react-three/csg"; import { Geometry } from "@react-three/csg";
import { import {
useDeleteTool, useDeleteTool,
useSelectedWallItem, useSelectedWallItem,
useToggleView, useToggleView,
useTransformMode, useWallItems,
useWallItems, useWalls,
useWalls,
} from "../../../store/store"; } from "../../../store/store";
import handleMeshDown from "../eventFunctions/handleMeshDown"; import handleMeshDown from "../eventFunctions/handleMeshDown";
import handleMeshMissed from "../eventFunctions/handleMeshMissed"; import handleMeshMissed from "../eventFunctions/handleMeshMissed";
@ -14,79 +13,65 @@ import WallItemsGroup from "./wallItemsGroup";
import { useEffect } from "react"; import { useEffect } from "react";
const WallsAndWallItems = ({ const WallsAndWallItems = ({
CSGGroup, CSGGroup,
AssetConfigurations, AssetConfigurations,
setSelectedItemsIndex, setSelectedItemsIndex,
selectedItemsIndex, selectedItemsIndex,
currentWallItem, currentWallItem,
csg, csg,
lines, lines,
hoveredDeletableWallItem, hoveredDeletableWallItem,
}: any) => { }: any) => {
const { walls } = useWalls(); const { walls } = useWalls();
const { wallItems } = useWallItems(); const { wallItems } = useWallItems();
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
const { deleteTool } = useDeleteTool(); const { deleteTool } = useDeleteTool();
const { transformMode } = useTransformMode(); const { setSelectedWallItem } = useSelectedWallItem();
const { setSelectedWallItem } = useSelectedWallItem();
useEffect(() => { return (
if (transformMode === null) { <mesh
if (!deleteTool) { ref={CSGGroup as any}
handleMeshMissed( name="Walls"
currentWallItem, key={walls.length}
setSelectedWallItem, receiveShadow
setSelectedItemsIndex visible={!toggleView}
); onClick={(event) => {
setSelectedWallItem(null); if (!deleteTool) {
setSelectedItemsIndex(null); handleMeshDown(
} event,
} currentWallItem,
}, [transformMode]); setSelectedWallItem,
return ( setSelectedItemsIndex,
<mesh wallItems,
ref={CSGGroup as any} toggleView
name="Walls" );
key={walls.length} }
receiveShadow }}
visible={!toggleView} onPointerMissed={() => {
onClick={(event) => { if (!deleteTool) {
if (!deleteTool && transformMode !== null) { handleMeshMissed(
handleMeshDown( currentWallItem,
event, setSelectedWallItem,
currentWallItem, setSelectedItemsIndex
setSelectedWallItem, );
setSelectedItemsIndex, setSelectedWallItem(null);
wallItems, setSelectedItemsIndex(null);
toggleView }
); }}
} >
}} <Geometry ref={csg as any} computeVertexNormals useGroups>
onPointerMissed={() => { <WallsMesh lines={lines} />
if (!deleteTool) { <WallItemsGroup
handleMeshMissed( currentWallItem={currentWallItem}
currentWallItem, AssetConfigurations={AssetConfigurations}
setSelectedWallItem, hoveredDeletableWallItem={hoveredDeletableWallItem}
setSelectedItemsIndex selectedItemsIndex={selectedItemsIndex}
); setSelectedItemsIndex={setSelectedItemsIndex}
setSelectedWallItem(null); CSGGroup={CSGGroup}
setSelectedItemsIndex(null); />
} </Geometry>
}} </mesh>
> );
<Geometry ref={csg as any} computeVertexNormals useGroups>
<WallsMesh lines={lines} />
<WallItemsGroup
currentWallItem={currentWallItem}
AssetConfigurations={AssetConfigurations}
hoveredDeletableWallItem={hoveredDeletableWallItem}
selectedItemsIndex={selectedItemsIndex}
setSelectedItemsIndex={setSelectedItemsIndex}
CSGGroup={CSGGroup}
/>
</Geometry>
</mesh>
);
}; };
export default WallsAndWallItems; export default WallsAndWallItems;

View File

@ -15,63 +15,63 @@ import texturePath from "../../../assets/textures/floor/wall-tex.png";
const materialCache = new Map<string, THREE.Material>(); const materialCache = new Map<string, THREE.Material>();
const WallsMeshComponent = ({ lines }: any) => { const WallsMeshComponent = ({ lines }: any) => {
const { walls, setWalls } = useWalls(); const { walls, setWalls } = useWalls();
const { updateScene, setUpdateScene } = useUpdateScene(); const { updateScene, setUpdateScene } = useUpdateScene();
useEffect(() => { useEffect(() => {
if (updateScene) { if (updateScene) {
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
getLines(organization).then((data) => { getLines(organization).then((data) => {
const Lines: Types.Lines = objectLinesToArray(data); const Lines: Types.Lines = objectLinesToArray(data);
localStorage.setItem("Lines", JSON.stringify(Lines)); localStorage.setItem("Lines", JSON.stringify(Lines));
if (Lines) { if (Lines) {
loadWalls(lines, setWalls); loadWalls(lines, setWalls);
}
});
setUpdateScene(false);
} }
}); }, [updateScene]);
setUpdateScene(false);
}
}, [updateScene]);
const textureLoader = new THREE.TextureLoader(); const textureLoader = new THREE.TextureLoader();
const wallTexture = textureLoader.load(texturePath); const wallTexture = textureLoader.load(texturePath);
wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping; wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping;
wallTexture.repeat.set(0.1, 0.1); wallTexture.repeat.set(0.1, 0.1);
wallTexture.colorSpace = THREE.SRGBColorSpace; wallTexture.colorSpace = THREE.SRGBColorSpace;
return ( return (
<> <>
{walls.map((wall: Types.Wall, index: number) => ( {walls.map((wall: Types.Wall, index: number) => (
<mesh key={index} renderOrder={1}> <mesh key={index} renderOrder={1}>
<Base <Base
name={`Wall${index + 1}`} name={`Wall${index + 1}`}
geometry={wall[0]} geometry={wall[0]}
rotation={wall[1]} rotation={wall[1]}
position={wall[2]} position={wall[2]}
userData={{ WallType: wall[3], Layer: wall[4] }} userData={{ WallType: wall[3], Layer: wall[4] }}
> >
<meshStandardMaterial <meshStandardMaterial
side={THREE.DoubleSide} side={THREE.DoubleSide}
color={CONSTANTS.wallConfig.defaultColor} color={CONSTANTS.wallConfig.defaultColor}
map={wallTexture} map={wallTexture}
/> />
</Base> </Base>
<mesh <mesh
castShadow castShadow
geometry={wall[0]} geometry={wall[0]}
rotation={wall[1]} rotation={wall[1]}
position={wall[2]} position={wall[2]}
name={`WallRaycastReference_${index + 1}`} name={`WallRaycastReference_${index + 1}`}
> >
<MeshDiscardMaterial /> <MeshDiscardMaterial />
</mesh> </mesh>
</mesh> </mesh>
))} ))}
</> </>
); );
}; };
const WallsMesh = React.memo(WallsMeshComponent); const WallsMesh = React.memo(WallsMeshComponent);

View File

@ -10,6 +10,7 @@ import updateCamPosition from "../camera/updateCameraPosition";
import CamMode from "../camera/camMode"; import CamMode from "../camera/camMode";
import SwitchView from "../camera/switchView"; import SwitchView from "../camera/switchView";
import SelectionControls from "./selectionControls/selectionControls"; import SelectionControls from "./selectionControls/selectionControls";
import TransformControl from "./transformControls/transformControls";
export default function Controls() { export default function Controls() {
const controlsRef = useRef<CameraControls>(null); const controlsRef = useRef<CameraControls>(null);
@ -138,6 +139,8 @@ export default function Controls() {
<SelectionControls /> <SelectionControls />
<TransformControl />
</> </>
); );
} }

View File

@ -1,270 +1,270 @@
import React, { useRef } from "react"; import React, { useRef } 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 DistanceFindingControls = ({ const DistanceFindingControls = ({
boundingBoxRef, boundingBoxRef,
object, object,
}: DistanceFindingControlsProps) => { }: DistanceFindingControlsProps) => {
const { camera, scene } = useThree(); const { camera, scene } = useThree();
// Refs for measurement lines // Refs for measurement lines
const line1 = useRef<Line>(null); const line1 = useRef<Line>(null);
const line2 = useRef<Line>(null); const line2 = useRef<Line>(null);
const line3 = useRef<Line>(null); const line3 = useRef<Line>(null);
const line4 = useRef<Line>(null); const line4 = useRef<Line>(null);
const line5 = useRef<Line>(null); const line5 = useRef<Line>(null);
// Refs for measurement text labels // Refs for measurement text labels
const textPosX = useRef<Group>(null); const textPosX = useRef<Group>(null);
const textNegX = useRef<Group>(null); const textNegX = useRef<Group>(null);
const textPosZ = useRef<Group>(null); const textPosZ = useRef<Group>(null);
const textNegZ = useRef<Group>(null); const textNegZ = useRef<Group>(null);
const textPosY = useRef<Group>(null); const textPosY = useRef<Group>(null);
// Store line geometries to avoid recreation // Store line geometries to avoid recreation
const lineGeometries = useRef({ const lineGeometries = useRef({
posX: new BufferGeometry(), posX: new BufferGeometry(),
negX: new BufferGeometry(), negX: new BufferGeometry(),
posZ: new BufferGeometry(), posZ: new BufferGeometry(),
negZ: new BufferGeometry(), negZ: new BufferGeometry(),
posY: new BufferGeometry(), posY: new BufferGeometry(),
}); });
useFrame(() => { useFrame(() => {
if (!boundingBoxRef?.current) return; if (!boundingBoxRef?.current) return;
boundingBoxRef.current.geometry.computeBoundingBox(); boundingBoxRef.current.geometry.computeBoundingBox();
const bbox = boundingBoxRef.current.geometry.boundingBox; const bbox = boundingBoxRef.current.geometry.boundingBox;
if (!bbox) return; if (!bbox) return;
const size = { const size = {
x: bbox.max.x - bbox.min.x, x: bbox.max.x - bbox.min.x,
y: bbox.max.y - bbox.min.y, y: bbox.max.y - bbox.min.y,
z: bbox.max.z - bbox.min.z, z: bbox.max.z - bbox.min.z,
};
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone();
if (!vec) return;
updateLine({
line: line1.current,
geometry: lineGeometries.current.posX,
direction: new Vector3(1, 0, 0), // Positive X
angle: "pos",
mesh: textPosX,
vec,
size,
});
updateLine({
line: line2.current,
geometry: lineGeometries.current.negX,
direction: new Vector3(-1, 0, 0), // Negative X
angle: "neg",
mesh: textNegX,
vec,
size,
});
updateLine({
line: line3.current,
geometry: lineGeometries.current.posZ,
direction: new Vector3(0, 0, 1), // Positive Z
angle: "pos",
mesh: textPosZ,
vec,
size,
});
updateLine({
line: line4.current,
geometry: lineGeometries.current.negZ,
direction: new Vector3(0, 0, -1), // Negative Z
angle: "neg",
mesh: textNegZ,
vec,
size,
});
updateLine({
line: line5.current,
geometry: lineGeometries.current.posY,
direction: new Vector3(0, -1, 0), // Down (Y)
angle: "posY",
mesh: textPosY,
vec,
size,
});
});
const updateLine = ({
line,
geometry,
direction,
angle,
mesh,
vec,
size,
}: {
line: Line | null;
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 (angle === "pos") {
points[0] = new Vector3(vec.x, vec.y, vec.z).add(
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 (camera) ray.camera = camera;
ray.set(new Vector3(vec.x, vec.y, vec.z), direction);
ray.params.Line.threshold = 0.1;
// Find intersection points
const wallsGroup = scene.children.find((val) =>
val?.name.includes("Walls")
);
const intersects = wallsGroup
? ray.intersectObjects([wallsGroup], true)
: [];
// Find intersection point
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;
}
}
}
// Update line geometry
if (points[1]) {
geometry.dispose();
geometry.setFromPoints([points[0], points[1]]);
line.geometry = geometry;
// 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 = `${points[0].distanceTo(points[1]).toFixed(2)}m`;
}
}
} 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 = "";
}
}; };
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone(); const Material = new LineBasicMaterial({ color: "#d2baff" });
if (!vec) return; return (
updateLine({
line: line1.current,
geometry: lineGeometries.current.posX,
direction: new Vector3(1, 0, 0), // Positive X
angle: "pos",
mesh: textPosX,
vec,
size,
});
updateLine({
line: line2.current,
geometry: lineGeometries.current.negX,
direction: new Vector3(-1, 0, 0), // Negative X
angle: "neg",
mesh: textNegX,
vec,
size,
});
updateLine({
line: line3.current,
geometry: lineGeometries.current.posZ,
direction: new Vector3(0, 0, 1), // Positive Z
angle: "pos",
mesh: textPosZ,
vec,
size,
});
updateLine({
line: line4.current,
geometry: lineGeometries.current.negZ,
direction: new Vector3(0, 0, -1), // Negative Z
angle: "neg",
mesh: textNegZ,
vec,
size,
});
updateLine({
line: line5.current,
geometry: lineGeometries.current.posY,
direction: new Vector3(0, -1, 0), // Down (Y)
angle: "posY",
mesh: textPosY,
vec,
size,
});
});
const updateLine = ({
line,
geometry,
direction,
angle,
mesh,
vec,
size,
}: {
line: Line | null;
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 (angle === "pos") {
points[0] = new Vector3(vec.x, vec.y, vec.z).add(
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 (camera) ray.camera = camera;
ray.set(new Vector3(vec.x, vec.y, vec.z), direction);
ray.params.Line.threshold = 0.1;
// Find intersection points
const wallsGroup = scene.children.find((val) =>
val?.name.includes("Walls")
);
const intersects = wallsGroup
? ray.intersectObjects([wallsGroup], true)
: [];
// Find intersection point
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;
}
}
}
// Update line geometry
if (points[1]) {
geometry.dispose();
geometry.setFromPoints([points[0], points[1]]);
line.geometry = geometry;
// 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 = `${points[0].distanceTo(points[1]).toFixed(2)}m`;
}
}
} 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 = "";
}
};
const Material = new LineBasicMaterial({ color: "#d2baff" });
return (
<>
{/* Measurement text labels */}
{boundingBoxRef.current && object > 0 && (
<> <>
<group name="textPosX" ref={textPosX}> {/* Measurement text labels */}
<Html {boundingBoxRef.current && object > 0 && (
wrapperClass="distance-text-wrapper" <>
className="distance-text" <group name="textPosX" ref={textPosX}>
zIndexRange={[1, 0]} <Html
style={{ pointerEvents: "none" }} wrapperClass="distance-text-wrapper"
> className="distance-text"
<div className="distance-label" id="textPosX"></div> zIndexRange={[1, 0]}
</Html> style={{ pointerEvents: "none" }}
</group> >
<group name="textNegX" ref={textNegX}> <div className="distance-label" id="textPosX"></div>
<Html </Html>
wrapperClass="distance-text-wrapper" </group>
className="distance-text" <group name="textNegX" ref={textNegX}>
zIndexRange={[1, 0]} <Html
style={{ pointerEvents: "none" }} wrapperClass="distance-text-wrapper"
> className="distance-text"
<div className="distance-label" id="textNegX"></div> zIndexRange={[1, 0]}
</Html> style={{ pointerEvents: "none" }}
</group> >
<group name="textPosZ" ref={textPosZ}> <div className="distance-label" id="textNegX"></div>
<Html </Html>
wrapperClass="distance-text-wrapper" </group>
className="distance-text" <group name="textPosZ" ref={textPosZ}>
zIndexRange={[2, 0]} <Html
style={{ pointerEvents: "none" }} wrapperClass="distance-text-wrapper"
> className="distance-text"
<div className="distance-label" id="textPosZ"></div> zIndexRange={[2, 0]}
</Html> style={{ pointerEvents: "none" }}
</group> >
<group name="textNegZ" ref={textNegZ}> <div className="distance-label" id="textPosZ"></div>
<Html </Html>
wrapperClass="distance-text-wrapper" </group>
className="distance-text" <group name="textNegZ" ref={textNegZ}>
zIndexRange={[1, 0]} <Html
style={{ pointerEvents: "none" }} wrapperClass="distance-text-wrapper"
> className="distance-text"
<div className="distance-label" id="textNegZ"></div> zIndexRange={[1, 0]}
</Html> style={{ pointerEvents: "none" }}
</group> >
<div className="distance-label" id="textNegZ"></div>
</Html>
</group>
{/* Measurement lines */} {/* Measurement lines */}
<primitive <primitive
object={new Line(new BufferGeometry(), Material)} object={new Line(new BufferGeometry(), Material)}
ref={line1} ref={line1}
/> />
<primitive <primitive
object={new Line(new BufferGeometry(), Material)} object={new Line(new BufferGeometry(), Material)}
ref={line2} ref={line2}
/> />
<primitive <primitive
object={new Line(new BufferGeometry(), Material)} object={new Line(new BufferGeometry(), Material)}
ref={line3} ref={line3}
/> />
<primitive <primitive
object={new Line(new BufferGeometry(), Material)} object={new Line(new BufferGeometry(), Material)}
ref={line4} ref={line4}
/> />
</>
)}
</> </>
)} );
</>
);
}; };
export default DistanceFindingControls; export default DistanceFindingControls;

View File

@ -2,10 +2,10 @@ import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { import {
useFloorItems, useFloorItems,
useSelectedAssets, useSelectedAssets,
useSocketStore, useSocketStore,
useToggleView, useToggleView,
} from "../../../../store/store"; } from "../../../../store/store";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@ -19,370 +19,337 @@ import { snapControls } from "../../../../utils/handleSnap";
import DistanceFindingControls from "./distanceFindingControls"; import DistanceFindingControls from "./distanceFindingControls";
function MoveControls({ function MoveControls({
movedObjects,
setMovedObjects,
itemsGroupRef,
copiedObjects,
setCopiedObjects,
pastedObjects,
setpastedObjects,
duplicatedObjects,
setDuplicatedObjects,
selectionGroup,
rotatedObjects,
setRotatedObjects,
boundingBoxRef,
}: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
[]
);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { selectedProduct } = useSelectedProduct();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const [keyEvent, setKeyEvent] = useState<
"Ctrl" | "Shift" | "Ctrl+Shift" | ""
>("");
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData,
});
};
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onKeyUp = (event: KeyboardEvent) => {
// When any modifier is released, reset snap
const isModifierKey = event.key === "Control" || event.key === "Shift";
if (isModifierKey) {
setKeyEvent("");
}
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeMovedAssets();
}
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
setKeyEvent("");
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (
pastedObjects.length > 0 ||
duplicatedObjects.length > 0 ||
rotatedObjects.length > 0
)
return;
if (
keyCombination === "Ctrl" ||
keyCombination === "Ctrl+Shift" ||
keyCombination === "Shift"
) {
// update state here
setKeyEvent(keyCombination);
} else {
setKeyEvent("");
}
if (keyCombination === "G") {
if (selectedAssets.length > 0) {
moveAssets();
itemsData.current = floorItems.filter((item: { modelUuid: string }) =>
selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
);
}
}
if (keyCombination === "ESCAPE") {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
canvasElement?.addEventListener("keyup", onKeyUp);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp);
};
}, [
camera,
controls,
scene,
toggleView,
selectedAssets,
socket,
floorItems,
pastedObjects,
duplicatedObjects,
movedObjects, movedObjects,
setMovedObjects,
itemsGroupRef,
pastedObjects,
setpastedObjects,
duplicatedObjects,
setDuplicatedObjects,
selectionGroup,
rotatedObjects, rotatedObjects,
keyEvent, setRotatedObjects,
]); boundingBoxRef,
}: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { selectedProduct } = useSelectedProduct();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
useFrame(() => { const updateBackend = (
if (movedObjects.length > 0) { productName: string,
const intersectionPoint = new THREE.Vector3(); productId: string,
raycaster.setFromCamera(pointer, camera); organization: string,
const point = raycaster.ray.intersectPlane(plane, intersectionPoint); eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData,
});
};
if (point) { useEffect(() => {
let targetX = point.x; if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
let targetZ = point.z;
if (keyEvent === "Ctrl") {
targetX = snapControls(targetX, "Ctrl");
targetZ = snapControls(targetZ, "Ctrl");
}
// else if (keyEvent === "Ctrl+Shift") {
// targetX = snapControls(targetX, "Ctrl+Shift");
// targetZ = snapControls(targetZ, "Ctrl+Shift");
// } else if (keyEvent === "Shift") {
// targetX = snapControls(targetX, "Shift");
// targetZ = snapControls(targetZ, "Shift");
// } else {
// }
const position = new THREE.Vector3(); const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
if (boundingBoxRef.current) { let isMoving = false;
boundingBoxRef.current.getWorldPosition(position);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (position.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (position.z - selectionGroup.current.position.z)
),
moveSpeed
);
} else {
const box = new THREE.Box3();
movedObjects.forEach((obj: THREE.Object3D) =>
box.expandByObject(obj)
);
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.lerp( const onPointerDown = () => {
new THREE.Vector3( isMoving = false;
targetX - (center.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (center.z - selectionGroup.current.position.z)
),
moveSpeed
);
}
}
}
});
const moveAssets = () => {
const updatedItems = floorItems.filter(
(item: { modelUuid: string }) =>
!selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
);
setFloorItems(updatedItems);
setMovedObjects(selectedAssets);
selectedAssets.forEach((asset: any) => {
selectionGroup.current.attach(asset);
});
};
const placeMovedAssets = () => {
if (movedObjects.length === 0) return;
movedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modelUuid: obj.uuid,
modelName: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
}; };
if (obj.userData.eventData) { const onPointerMove = () => {
const eventData = useEventsStore isMoving = true;
.getState() };
.getEventByModelUuid(obj.userData.modelUuid);
const productData = useProductStore
.getState()
.getEventByModelUuid(
useSelectedProduct.getState().selectedProduct.productId,
obj.userData.modelUuid
);
if (eventData) { const onKeyUp = (event: KeyboardEvent) => {
useEventsStore.getState().updateEvent(obj.userData.modelUuid, { const isModifierKey = event.key === "Control" || event.key === "Shift";
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
});
}
if (productData) {
const event = useProductStore
.getState()
.updateEvent(
useSelectedProduct.getState().selectedProduct.productId,
obj.userData.modelUuid,
{
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
}
);
if (event) { if (isModifierKey) {
updateBackend( setKeyEvent("");
selectedProduct.productName, }
selectedProduct.productId, };
organization,
event const onPointerUp = (event: PointerEvent) => {
); if (!isMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeMovedAssets();
}
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
setKeyEvent("");
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0)
return;
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
setKeyEvent(keyCombination);
} else {
setKeyEvent("");
} }
newFloorItem.eventData = eventData; if (keyCombination === "G") {
} if (selectedAssets.length > 0) {
} moveAssets();
itemsData.current = floorItems.filter((item: { modelUuid: string }) =>
selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
);
}
}
setFloorItems((prevItems: Types.FloorItems) => { if (keyCombination === "ESCAPE") {
const updatedItems = [...(prevItems || []), newFloorItem]; event.preventDefault();
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
//REST clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
// await setFloorItemApi( setFloorItems([...floorItems, ...itemsData.current]);
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET setMovedObjects([]);
itemsData.current = [];
const data = { }
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
}; };
socket.emit("v2:model-asset:add", data); if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
canvasElement?.addEventListener("keyup", onKeyUp);
}
itemsGroupRef.current.add(obj); return () => {
} canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp);
};
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]);
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25;
useFrame(() => {
if (movedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
let targetX = point.x;
let targetZ = point.z;
if (keyEvent === "Ctrl") {
targetX = snapControls(targetX, "Ctrl");
targetZ = snapControls(targetZ, "Ctrl");
}
// else if (keyEvent === "Ctrl+Shift") {
// targetX = snapControls(targetX, "Ctrl+Shift");
// targetZ = snapControls(targetZ, "Ctrl+Shift");
// } else if (keyEvent === "Shift") {
// targetX = snapControls(targetX, "Shift");
// targetZ = snapControls(targetZ, "Shift");
// } else {
// }
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current.getWorldPosition(position);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (position.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (position.z - selectionGroup.current.position.z)
),
moveSpeed
);
} else {
const box = new THREE.Box3();
movedObjects.forEach((obj: THREE.Object3D) =>
box.expandByObject(obj)
);
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (center.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (center.z - selectionGroup.current.position.z)
),
moveSpeed
);
}
}
}
}); });
toast.success("Object moved!");
itemsData.current = []; const moveAssets = () => {
clearSelection(); const updatedItems = floorItems.filter(
}; (item: { modelUuid: string }) =>
!selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
);
setFloorItems(updatedItems);
setMovedObjects(selectedAssets);
selectedAssets.forEach((asset: any) => {
selectionGroup.current.attach(asset);
});
};
const clearSelection = () => { const placeMovedAssets = () => {
selectionGroup.current.children = []; if (movedObjects.length === 0) return;
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
setKeyEvent("");
};
return ( movedObjects.forEach(async (obj: THREE.Object3D) => {
<DistanceFindingControls const worldPosition = new THREE.Vector3();
boundingBoxRef={boundingBoxRef} obj.getWorldPosition(worldPosition);
object={movedObjects.length}
/> selectionGroup.current.remove(obj);
); obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modelUuid: obj.uuid,
modelName: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
};
if (obj.userData.eventData) {
const eventData = useEventsStore.getState().getEventByModelUuid(obj.userData.modelUuid);
const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, obj.userData.modelUuid);
if (eventData) {
useEventsStore.getState().updateEvent(obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
});
}
if (productData) {
const event = useProductStore
.getState()
.updateEvent(
selectedProduct.productId,
obj.userData.modelUuid,
{
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
newFloorItem.eventData = eventData;
}
}
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
//REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
};
socket.emit("v2:model-asset:add", data);
itemsGroupRef.current.add(obj);
}
});
toast.success("Object moved!");
itemsData.current = [];
clearSelection();
};
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
setKeyEvent("");
};
return (
<DistanceFindingControls
boundingBoxRef={boundingBoxRef}
object={movedObjects.length}
/>
);
} }
export default MoveControls; export default MoveControls;

View File

@ -0,0 +1,206 @@
import { TransformControls } from "@react-three/drei";
import * as THREE from "three";
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useFloorItems, useActiveTool, useSocketStore } from "../../../../store/store";
import { useThree } from "@react-three/fiber";
import * as Types from '../../../../types/world/worldTypes';
import { useEffect, useState } from "react";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
import { useProductStore } from "../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
export default function TransformControl() {
const state = useThree();
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { setObjectPosition } = useObjectPosition();
const { setObjectRotation } = useObjectRotation();
const { setFloorItems } = useFloorItems();
const { activeTool } = useActiveTool();
const { socket } = useSocketStore();
const { selectedProduct } = useSelectedProduct();
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData,
});
};
function handleObjectChange() {
if (selectedFloorItem) {
setObjectPosition(selectedFloorItem.position);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
});
}
}
function handleMouseUp() {
if (selectedFloorItem) {
setObjectPosition(selectedFloorItem.position);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
});
}
setFloorItems((prevItems: Types.FloorItems) => {
if (!prevItems) {
return
}
let updatedItem: any = null;
const updatedItems = prevItems.map((item) => {
if (item.modelUuid === selectedFloorItem?.uuid) {
updatedItem = {
...item,
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number],
rotation: { x: selectedFloorItem.rotation.x, y: selectedFloorItem.rotation.y, z: selectedFloorItem.rotation.z },
};
return updatedItem;
}
return item;
});
if (updatedItem && selectedFloorItem) {
if (updatedItem.eventData) {
const eventData = useEventsStore.getState().getEventByModelUuid(updatedItem.modelUuid);
const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, updatedItem.modelUuid);
if (eventData) {
useEventsStore.getState().updateEvent(updatedItem.modelUuid, {
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z],
});
}
if (productData) {
const event = useProductStore
.getState()
.updateEvent(
selectedProduct.productId,
updatedItem.modelUuid,
{
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z],
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
updatedItem.eventData = eventData;
}
}
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), updatedItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
// REST
// setFloorItemApi(
// organization,
// updatedItem.modelUuid,
// updatedItem.modelName,
// updatedItem.modelfileid,
// [selectedFloorItem.position.x, 0, selectedFloorItem.position.z,],
// { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z },
// false,
// true,
// );
// SOCKET
const data = {
organization: organization,
modelUuid: updatedItem.modelUuid,
modelName: updatedItem.modelName,
modelfileID: updatedItem.modelfileID,
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id
}
socket.emit("v2:model-asset:add", data);
}
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
}
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const keyCombination = detectModifierKeys(e);
if (!selectedFloorItem) return;
if (keyCombination === "G") {
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
}
if (keyCombination === "R") {
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
}
};
if (selectedFloorItem) {
window.addEventListener("keydown", handleKeyDown);
} else {
setTransformMode(null);
}
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [selectedFloorItem]);
useEffect(() => {
if (activeTool === "delete") {
if (state.controls) {
const target = (state.controls as any).getTarget(new THREE.Vector3());
(state.controls as any).setTarget(target.x, 0, target.z, true);
}
setSelectedFloorItem(null);
setObjectPosition({ x: undefined, y: undefined, z: undefined });
setObjectRotation({ x: undefined, y: undefined, z: undefined });
}
}, [activeTool]);
return (
<>
{(selectedFloorItem && transformMode) &&
<TransformControls
showX={transformMode === "translate"}
showY={transformMode === "rotate"}
showZ={transformMode === "translate"}
object={selectedFloorItem}
mode={transformMode}
onObjectChange={handleObjectChange}
onMouseUp={handleMouseUp}
/>
}
</>
);
}

View File

@ -144,11 +144,6 @@ export const useMovePoint = create<any>((set: any) => ({
setMovePoint: (x: any) => set(() => ({ movePoint: x })), setMovePoint: (x: any) => set(() => ({ movePoint: x })),
})); }));
export const useTransformMode = create<any>((set: any) => ({
transformMode: null,
setTransformMode: (x: any) => set(() => ({ transformMode: x })),
}));
export const useDeletePointOrLine = create<any>((set: any) => ({ export const useDeletePointOrLine = create<any>((set: any) => ({
deletePointOrLine: false, deletePointOrLine: false,
setDeletePointOrLine: (x: any) => set(() => ({ deletePointOrLine: x })), setDeletePointOrLine: (x: any) => set(() => ({ deletePointOrLine: x })),

View File

@ -225,7 +225,6 @@ interface AssetConfiguration {
scale?: [number, number, number]; scale?: [number, number, number];
csgscale?: [number, number, number]; csgscale?: [number, number, number];
csgposition?: [number, number, number]; csgposition?: [number, number, number];
positionY?: (intersectionPoint: { point: THREE.Vector3 }) => number;
type?: "Fixed-Move" | "Free-Move"; type?: "Fixed-Move" | "Free-Move";
} }