can place on wall

This commit is contained in:
wass08
2026-01-19 09:32:46 +09:00
parent 69148705c6
commit f3456aed42
7 changed files with 34 additions and 26 deletions
+1
View File
@@ -34,6 +34,7 @@ import { ToolManager } from "./tools/tool-manager";
const selectedObjects: Object3D[] = []; const selectedObjects: Object3D[] = [];
initSpatialGridSync(); initSpatialGridSync();
useScene.getState().loadScene();
export default function Editor() { export default function Editor() {
return ( return (
@@ -24,30 +24,31 @@ export const ItemTool: React.FC = () => {
const { canPlaceOnFloor, canPlaceOnWall } = useSpatialQuery(); const { canPlaceOnFloor, canPlaceOnWall } = useSpatialQuery();
useEffect(() => { useEffect(() => {
console.log("item-tool-reloaded");
if (!selectedItem) { if (!selectedItem) {
return; return;
} }
let isOnWall = false; let isOnWall = false;
let wallLocked = false; let wallLocked = false;
let currentWallId: string | null = null;
const checkCanPlace = () => { const checkCanPlace = () => {
const currentLevelId = useViewer.getState().currentLevelId; const currentLevelId = useViewer.getState().currentLevelId;
if (currentLevelId && draftItem.current) { if (currentLevelId && draftItem.current) {
let placeable = true; let placeable = true;
if (draftItem.current.asset.attachTo) { if (draftItem.current.asset.attachTo) {
if (!isOnWall) { if (!isOnWall || !currentWallId) {
placeable = false; placeable = false;
} else { } else {
placeable = canPlaceOnWall( const result = canPlaceOnWall(
currentLevelId, currentLevelId,
draftItem.current.parentId as WallNode["id"], currentWallId as WallNode["id"],
gridPosition.current.x, gridPosition.current.x,
gridPosition.current.y, gridPosition.current.y,
draftItem.current.asset.dimensions, draftItem.current.asset.dimensions,
[draftItem.current.id], [draftItem.current.id],
).valid; );
placeable = result.valid;
} }
} else { } else {
placeable = canPlaceOnFloor( placeable = canPlaceOnFloor(
@@ -116,7 +117,6 @@ export const ItemTool: React.FC = () => {
if (!currentLevelId || !draftItem.current || !checkCanPlace()) return; if (!currentLevelId || !draftItem.current || !checkCanPlace()) return;
console.log("ongridclick action");
useScene.temporal.getState().resume(); useScene.temporal.getState().resume();
useScene.getState().updateNode(draftItem.current.id, { useScene.getState().updateNode(draftItem.current.id, {
position: [gridPosition.current.x, 0, gridPosition.current.z], position: [gridPosition.current.x, 0, gridPosition.current.z],
@@ -128,13 +128,13 @@ export const ItemTool: React.FC = () => {
}; };
const onWallEnter = (event: WallEvent) => { const onWallEnter = (event: WallEvent) => {
console.log("Wall enter", event);
event.stopPropagation(); event.stopPropagation();
if ( if (
draftItem.current?.asset.attachTo === "wall" || draftItem.current?.asset.attachTo === "wall" ||
draftItem.current?.asset.attachTo === "wall-side" draftItem.current?.asset.attachTo === "wall-side"
) { ) {
isOnWall = true; isOnWall = true;
currentWallId = event.node.id;
gridPosition.current.set( gridPosition.current.set(
Math.round(event.localPosition[0] * 2) / 2, Math.round(event.localPosition[0] * 2) / 2,
Math.round(event.localPosition[1] * 2) / 2, Math.round(event.localPosition[1] * 2) / 2,
@@ -154,8 +154,8 @@ export const ItemTool: React.FC = () => {
}; };
const onWallLeave = (event: WallEvent) => { const onWallLeave = (event: WallEvent) => {
console.log("Wall leave", event);
isOnWall = false; isOnWall = false;
currentWallId = null;
event.stopPropagation(); event.stopPropagation();
if (!draftItem.current) return; if (!draftItem.current) return;
const currentLevelId = useViewer.getState().currentLevelId; const currentLevelId = useViewer.getState().currentLevelId;
@@ -180,7 +180,6 @@ export const ItemTool: React.FC = () => {
if (!currentLevelId || !draftItem.current || !checkCanPlace()) return; if (!currentLevelId || !draftItem.current || !checkCanPlace()) return;
wallLocked = true; wallLocked = true;
console.log("onwallclick action");
useScene.temporal.getState().resume(); useScene.temporal.getState().resume();
useScene.getState().updateNode(draftItem.current.id, { useScene.getState().updateNode(draftItem.current.id, {
position: [ position: [
@@ -200,7 +199,6 @@ export const ItemTool: React.FC = () => {
const onWallMove = (event: WallEvent) => { const onWallMove = (event: WallEvent) => {
event.stopPropagation(); event.stopPropagation();
console.log("Wall move", event);
wallLocked = false; wallLocked = false;
if (!draftItem.current) return; if (!draftItem.current) return;
gridPosition.current.set( gridPosition.current.set(
@@ -45,18 +45,21 @@ export class SpatialGridManager {
item.asset.attachTo === "wall" || item.asset.attachTo === "wall" ||
item.asset.attachTo === "wall-side" item.asset.attachTo === "wall-side"
) { ) {
// Wall-attached item // Wall-attached item - use parentId as the wall ID
if (item.wallId && item.wallT !== undefined) { const wallId = item.parentId;
const wallLength = this.getWallLength(item.wallId); if (wallId && this.walls.has(wallId)) {
const wallLength = this.getWallLength(wallId);
if (wallLength > 0) { if (wallLength > 0) {
const [width, height] = item.asset.dimensions; const [width, height] = item.asset.dimensions;
const halfW = width / wallLength / 2; const halfW = width / wallLength / 2;
const halfH = height / 2; const halfH = height / 2;
// Calculate t from local X position (position[0] is distance along wall)
const t = item.position[0] / wallLength;
this.getWallGrid(levelId).insert({ this.getWallGrid(levelId).insert({
itemId: item.id, itemId: item.id,
wallId: item.wallId, wallId: wallId,
tStart: item.wallT - halfW, tStart: t - halfW,
tEnd: item.wallT + halfW, tEnd: t + halfW,
yStart: item.position[1] - halfH, yStart: item.position[1] - halfH,
yEnd: item.position[1] + halfH, yEnd: item.position[1] + halfH,
}); });
@@ -86,17 +89,20 @@ export class SpatialGridManager {
) { ) {
// Remove old placement and re-insert // Remove old placement and re-insert
this.getWallGrid(levelId).removeByItemId(item.id); this.getWallGrid(levelId).removeByItemId(item.id);
if (item.wallId && item.wallT !== undefined) { const wallId = item.parentId;
const wallLength = this.getWallLength(item.wallId); if (wallId && this.walls.has(wallId)) {
const wallLength = this.getWallLength(wallId);
if (wallLength > 0) { if (wallLength > 0) {
const [width, height] = item.asset.dimensions; const [width, height] = item.asset.dimensions;
const halfW = width / wallLength / 2; const halfW = width / wallLength / 2;
const halfH = height / 2; const halfH = height / 2;
// Calculate t from local X position (position[0] is distance along wall)
const t = item.position[0] / wallLength;
this.getWallGrid(levelId).insert({ this.getWallGrid(levelId).insert({
itemId: item.id, itemId: item.id,
wallId: item.wallId, wallId: wallId,
tStart: item.wallT - halfW, tStart: t - halfW,
tEnd: item.wallT + halfW, tEnd: t + halfW,
yStart: item.position[1] - halfH, yStart: item.position[1] - halfH,
yEnd: item.position[1] + halfH, yEnd: item.position[1] + halfH,
}); });
@@ -45,13 +45,14 @@ export function initSpatialGridSync() {
} }
} }
// Detect updated nodes (only items with position/rotation changes) // Detect updated nodes (items with position/rotation/parentId changes)
for (const [id, node] of Object.entries(state.nodes)) { for (const [id, node] of Object.entries(state.nodes)) {
const prev = prevState.nodes[id as AnyNode["id"]]; const prev = prevState.nodes[id as AnyNode["id"]];
if (prev && node.type === "item" && prev.type === "item") { if (prev && node.type === "item" && prev.type === "item") {
if ( if (
!arraysEqual(node.position, prev.position) || !arraysEqual(node.position, prev.position) ||
!arraysEqual(node.rotation, prev.rotation) !arraysEqual(node.rotation, prev.rotation) ||
node.parentId !== prev.parentId
) { ) {
const levelId = resolveLevelId(node, state.nodes); const levelId = resolveLevelId(node, state.nodes);
spatialGridManager.handleNodeUpdated(node, levelId); spatialGridManager.handleNodeUpdated(node, levelId);
@@ -1,6 +1,6 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { spatialGridManager } from "./spatial-grid-manager";
import { LevelNode, WallNode } from "../../schema"; import { LevelNode, WallNode } from "../../schema";
import { spatialGridManager } from "./spatial-grid-manager";
export function useSpatialQuery() { export function useSpatialQuery() {
const canPlaceOnFloor = useCallback( const canPlaceOnFloor = useCallback(
@@ -64,7 +64,6 @@ export class WallSpatialGrid {
this.itemToWall.delete(itemId); this.itemToWall.delete(itemId);
} }
// The missing method!
removeByItemId(itemId: string) { removeByItemId(itemId: string) {
const wallId = this.itemToWall.get(itemId); const wallId = this.itemToWall.get(itemId);
if (wallId) { if (wallId) {
+4 -1
View File
@@ -69,24 +69,28 @@ const useScene = create<SceneState>()(
start: [0, 0], start: [0, 0],
end: [5, 0], end: [5, 0],
children: [], children: [],
parentId: level0.id,
}); });
const wall1 = WallNode.parse({ const wall1 = WallNode.parse({
start: [0, 0], start: [0, 0],
end: [0, 5], end: [0, 5],
children: [], children: [],
parentId: level0.id,
}); });
const wall2 = WallNode.parse({ const wall2 = WallNode.parse({
start: [5, 5], start: [5, 5],
end: [0, 5], end: [0, 5],
children: [], children: [],
parentId: level0.id,
}); });
const wall3 = WallNode.parse({ const wall3 = WallNode.parse({
start: [5, 5], start: [5, 5],
end: [5, 0], end: [5, 0],
children: [], children: [],
parentId: level0.id,
}); });
const window1 = ItemNode.parse({ const window1 = ItemNode.parse({
@@ -165,7 +169,6 @@ const useScene = create<SceneState>()(
), ),
); );
useScene.getState().loadScene();
export default useScene; export default useScene;
// Subscribe to the temporal store (Undo/Redo events) // Subscribe to the temporal store (Undo/Redo events)