check wall + clean attach/detach

This commit is contained in:
wass08
2026-01-19 08:29:43 +09:00
parent a217d0d3a6
commit 69148705c6
3 changed files with 80 additions and 18 deletions
+40 -10
View File
@@ -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(
currentLevelId,
[gridPosition.current.x, 0, gridPosition.current.z],
selectedItem.dimensions,
[0, 0, 0],
[draftItem.current.id],
);
if (placeable.valid) {
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],
draftItem.current.asset.dimensions,
[0, 0, 0],
[draftItem.current.id],
).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 };
}