check wall + clean attach/detach
This commit is contained in:
@@ -21,7 +21,7 @@ export const ItemTool: React.FC = () => {
|
||||
const draftItem = useRef<ItemNode | null>(null);
|
||||
const gridPosition = useRef(new Vector3(0, 0, 0));
|
||||
const selectedItem = useEditor((state) => state.selectedItem);
|
||||
const { canPlace } = useSpatialQuery();
|
||||
const { canPlaceOnFloor, canPlaceOnWall } = useSpatialQuery();
|
||||
|
||||
useEffect(() => {
|
||||
console.log("item-tool-reloaded");
|
||||
@@ -35,14 +35,30 @@ export const ItemTool: React.FC = () => {
|
||||
const checkCanPlace = () => {
|
||||
const currentLevelId = useViewer.getState().currentLevelId;
|
||||
if (currentLevelId && draftItem.current) {
|
||||
const placeable = canPlace(
|
||||
let placeable = true;
|
||||
if (draftItem.current.asset.attachTo) {
|
||||
if (!isOnWall) {
|
||||
placeable = false;
|
||||
} else {
|
||||
placeable = canPlaceOnWall(
|
||||
currentLevelId,
|
||||
draftItem.current.parentId as WallNode["id"],
|
||||
gridPosition.current.x,
|
||||
gridPosition.current.y,
|
||||
draftItem.current.asset.dimensions,
|
||||
[draftItem.current.id],
|
||||
).valid;
|
||||
}
|
||||
} else {
|
||||
placeable = canPlaceOnFloor(
|
||||
currentLevelId,
|
||||
[gridPosition.current.x, 0, gridPosition.current.z],
|
||||
selectedItem.dimensions,
|
||||
draftItem.current.asset.dimensions,
|
||||
[0, 0, 0],
|
||||
[draftItem.current.id],
|
||||
);
|
||||
if (placeable.valid) {
|
||||
).valid;
|
||||
}
|
||||
if (placeable) {
|
||||
cursorRef.current.material.color.set("green");
|
||||
return true;
|
||||
} else {
|
||||
@@ -133,6 +149,7 @@ export const ItemTool: React.FC = () => {
|
||||
],
|
||||
parentId: event.node.id,
|
||||
});
|
||||
checkCanPlace();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -140,6 +157,18 @@ export const ItemTool: React.FC = () => {
|
||||
console.log("Wall leave", event);
|
||||
isOnWall = false;
|
||||
event.stopPropagation();
|
||||
if (!draftItem.current) return;
|
||||
const currentLevelId = useViewer.getState().currentLevelId;
|
||||
draftItem.current.parentId = currentLevelId;
|
||||
useScene.getState().updateNode(draftItem.current.id, {
|
||||
position: [
|
||||
gridPosition.current.x,
|
||||
gridPosition.current.y,
|
||||
gridPosition.current.z,
|
||||
],
|
||||
parentId: currentLevelId,
|
||||
});
|
||||
checkCanPlace();
|
||||
};
|
||||
|
||||
const onWallClick = (event: WallEvent) => {
|
||||
@@ -147,7 +176,7 @@ export const ItemTool: React.FC = () => {
|
||||
if (wallLocked) {
|
||||
return;
|
||||
}
|
||||
const { currentLevelId } = useViewer.getState();
|
||||
const currentLevelId = useViewer.getState().currentLevelId;
|
||||
if (!currentLevelId || !draftItem.current || !checkCanPlace()) return;
|
||||
|
||||
wallLocked = true;
|
||||
@@ -166,6 +195,7 @@ export const ItemTool: React.FC = () => {
|
||||
|
||||
useScene.temporal.getState().pause();
|
||||
createDraftItem();
|
||||
checkCanPlace();
|
||||
};
|
||||
|
||||
const onWallMove = (event: WallEvent) => {
|
||||
|
||||
@@ -138,25 +138,36 @@ export class SpatialGridManager {
|
||||
return grid.canPlace(position, dimensions, rotation, ignoreIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an item can be placed on a wall
|
||||
* @param levelId - the level containing the wall
|
||||
* @param wallId - the wall to check
|
||||
* @param localX - X position in wall-local space (distance from wall start)
|
||||
* @param localY - Y position (height from floor)
|
||||
* @param dimensions - item dimensions [width, height, depth]
|
||||
* @param ignoreIds - item IDs to ignore in collision check
|
||||
*/
|
||||
canPlaceOnWall(
|
||||
levelId: string,
|
||||
wallId: string,
|
||||
tCenter: number,
|
||||
itemWidth: number,
|
||||
yCenter: number,
|
||||
itemHeight: number,
|
||||
localX: number,
|
||||
localY: number,
|
||||
dimensions: [number, number, number],
|
||||
ignoreIds?: string[],
|
||||
) {
|
||||
const wallLength = this.getWallLength(wallId);
|
||||
if (wallLength === 0) {
|
||||
return { valid: false, conflictIds: [] };
|
||||
}
|
||||
// Convert local X position to parametric t (0-1)
|
||||
const tCenter = localX / wallLength;
|
||||
const [itemWidth, itemHeight] = dimensions;
|
||||
return this.getWallGrid(levelId).canPlaceOnWall(
|
||||
wallId,
|
||||
wallLength,
|
||||
tCenter,
|
||||
itemWidth,
|
||||
yCenter,
|
||||
localY,
|
||||
itemHeight,
|
||||
ignoreIds,
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useCallback } from "react";
|
||||
import { spatialGridManager } from "./spatial-grid-manager";
|
||||
import { LevelNode } from "../../schema";
|
||||
import { LevelNode, WallNode } from "../../schema";
|
||||
|
||||
export function useSpatialQuery() {
|
||||
const canPlace = useCallback(
|
||||
const canPlaceOnFloor = useCallback(
|
||||
(
|
||||
levelId: LevelNode["id"],
|
||||
position: [number, number, number],
|
||||
@@ -22,5 +22,26 @@ export function useSpatialQuery() {
|
||||
[],
|
||||
);
|
||||
|
||||
return { canPlace };
|
||||
const canPlaceOnWall = useCallback(
|
||||
(
|
||||
levelId: LevelNode["id"],
|
||||
wallId: WallNode["id"],
|
||||
localX: number,
|
||||
localY: number,
|
||||
dimensions: [number, number, number],
|
||||
ignoreIds?: string[],
|
||||
) => {
|
||||
return spatialGridManager.canPlaceOnWall(
|
||||
levelId,
|
||||
wallId,
|
||||
localX,
|
||||
localY,
|
||||
dimensions,
|
||||
ignoreIds,
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return { canPlaceOnFloor, canPlaceOnWall };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user