can place on wall
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user