fix wall items wrong level + elevation

This commit is contained in:
wass08
2026-02-23 08:43:38 +09:00
parent 16ba5bf9c6
commit 4555325389
3 changed files with 54 additions and 9 deletions
@@ -2,6 +2,7 @@ import {
type AnyNodeId, type AnyNodeId,
emitter, emitter,
sceneRegistry, sceneRegistry,
spatialGridManager,
useScene, useScene,
type WallEvent, type WallEvent,
WindowNode, WindowNode,
@@ -78,10 +79,17 @@ export const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWin
if (wallId) useScene.getState().dirtyNodes.add(wallId as AnyNodeId) if (wallId) useScene.getState().dirtyNodes.add(wallId as AnyNodeId)
} }
const getLevelId = () => useViewer.getState().selection.levelId
const getLevelYOffset = () => { const getLevelYOffset = () => {
const id = useViewer.getState().selection.levelId const id = getLevelId()
return id ? (sceneRegistry.nodes.get(id as AnyNodeId)?.position.y ?? 0) : 0 return id ? (sceneRegistry.nodes.get(id as AnyNodeId)?.position.y ?? 0) : 0
} }
const getSlabElevation = (wallEvent: WallEvent) =>
spatialGridManager.getSlabElevationForWall(
wallEvent.node.parentId ?? '',
wallEvent.node.start,
wallEvent.node.end,
)
const hideCursor = () => { const hideCursor = () => {
if (cursorGroupRef.current) cursorGroupRef.current.visible = false if (cursorGroupRef.current) cursorGroupRef.current.visible = false
@@ -102,6 +110,8 @@ export const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWin
const onWallEnter = (event: WallEvent) => { const onWallEnter = (event: WallEvent) => {
if (!isValidWallSideFace(event.normal)) return if (!isValidWallSideFace(event.normal)) return
// Only interact with walls on the current level
if (event.node.parentId !== getLevelId()) return
const side = getSideFromNormal(event.normal) const side = getSideFromNormal(event.normal)
const itemRotation = calculateItemRotation(event.normal) const itemRotation = calculateItemRotation(event.normal)
@@ -134,12 +144,18 @@ export const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWin
movingWindowNode.id, movingWindowNode.id,
) )
updateCursor(wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset()), cursorRotation, valid) updateCursor(
wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset(), getSlabElevation(event)),
cursorRotation,
valid,
)
event.stopPropagation() event.stopPropagation()
} }
const onWallMove = (event: WallEvent) => { const onWallMove = (event: WallEvent) => {
if (!isValidWallSideFace(event.normal)) return if (!isValidWallSideFace(event.normal)) return
// Only interact with walls on the current level
if (event.node.parentId !== getLevelId()) return
const side = getSideFromNormal(event.normal) const side = getSideFromNormal(event.normal)
const itemRotation = calculateItemRotation(event.normal) const itemRotation = calculateItemRotation(event.normal)
@@ -172,12 +188,18 @@ export const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWin
movingWindowNode.id, movingWindowNode.id,
) )
updateCursor(wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset()), cursorRotation, valid) updateCursor(
wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset(), getSlabElevation(event)),
cursorRotation,
valid,
)
event.stopPropagation() event.stopPropagation()
} }
const onWallClick = (event: WallEvent) => { const onWallClick = (event: WallEvent) => {
if (!isValidWallSideFace(event.normal)) return if (!isValidWallSideFace(event.normal)) return
// Only interact with walls on the current level
if (event.node.parentId !== getLevelId()) return
const side = getSideFromNormal(event.normal) const side = getSideFromNormal(event.normal)
const itemRotation = calculateItemRotation(event.normal) const itemRotation = calculateItemRotation(event.normal)
@@ -1,16 +1,18 @@
import { type AnyNodeId, type ItemNode, useScene, type WallNode, type WindowNode } from '@pascal-app/core' import { type AnyNodeId, type ItemNode, useScene, type WallNode, type WindowNode } from '@pascal-app/core'
/** /**
* Converts wall-local (X along wall, Y = height above level floor) to world XYZ. * Converts wall-local (X along wall, Y = height above wall base) to world XYZ.
* Wall XZ uses level-local coordinates (levels only offset in Y, not XZ). * Wall XZ uses level-local coordinates (levels only offset in Y, not XZ).
* Pass levelYOffset (the level group's current world Y) so the cursor lands at the * Pass levelYOffset (the level group's current world Y) and slabElevation (the
* correct world height when the cursor group is at the scene root. * wall mesh's Y within the level group) so the cursor lands at the correct world
* height — matching how WallSystem positions the wall mesh at slabElevation.
*/ */
export function wallLocalToWorld( export function wallLocalToWorld(
wallNode: WallNode, wallNode: WallNode,
localX: number, localX: number,
localY: number, localY: number,
levelYOffset = 0, levelYOffset = 0,
slabElevation = 0,
): [number, number, number] { ): [number, number, number] {
const wallAngle = Math.atan2( const wallAngle = Math.atan2(
wallNode.end[1] - wallNode.start[1], wallNode.end[1] - wallNode.start[1],
@@ -18,7 +20,7 @@ export function wallLocalToWorld(
) )
return [ return [
wallNode.start[0] + localX * Math.cos(wallAngle), wallNode.start[0] + localX * Math.cos(wallAngle),
localY + levelYOffset, slabElevation + localY + levelYOffset,
wallNode.start[1] + localX * Math.sin(wallAngle), wallNode.start[1] + localX * Math.sin(wallAngle),
] ]
} }
@@ -2,6 +2,7 @@ import {
type AnyNodeId, type AnyNodeId,
emitter, emitter,
sceneRegistry, sceneRegistry,
spatialGridManager,
useScene, useScene,
type WallEvent, type WallEvent,
WindowNode, WindowNode,
@@ -44,6 +45,12 @@ export const WindowTool: React.FC = () => {
const id = getLevelId() const id = getLevelId()
return id ? (sceneRegistry.nodes.get(id as AnyNodeId)?.position.y ?? 0) : 0 return id ? (sceneRegistry.nodes.get(id as AnyNodeId)?.position.y ?? 0) : 0
} }
const getSlabElevation = (wallEvent: WallEvent) =>
spatialGridManager.getSlabElevationForWall(
wallEvent.node.parentId ?? '',
wallEvent.node.start,
wallEvent.node.end,
)
const markWallDirty = (wallId: string) => { const markWallDirty = (wallId: string) => {
useScene.getState().dirtyNodes.add(wallId as AnyNodeId) useScene.getState().dirtyNodes.add(wallId as AnyNodeId)
@@ -79,6 +86,8 @@ export const WindowTool: React.FC = () => {
if (!isValidWallSideFace(event.normal)) return if (!isValidWallSideFace(event.normal)) return
const levelId = getLevelId() const levelId = getLevelId()
if (!levelId) return if (!levelId) return
// Only interact with walls on the current level
if (event.node.parentId !== levelId) return
destroyDraft() destroyDraft()
@@ -108,12 +117,18 @@ export const WindowTool: React.FC = () => {
const valid = !hasWallChildOverlap(event.node.id, clampedX, clampedY, width, height, node.id) const valid = !hasWallChildOverlap(event.node.id, clampedX, clampedY, width, height, node.id)
updateCursor(wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset()), cursorRotation, valid) updateCursor(
wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset(), getSlabElevation(event)),
cursorRotation,
valid,
)
event.stopPropagation() event.stopPropagation()
} }
const onWallMove = (event: WallEvent) => { const onWallMove = (event: WallEvent) => {
if (!isValidWallSideFace(event.normal)) return if (!isValidWallSideFace(event.normal)) return
// Only interact with walls on the current level
if (event.node.parentId !== getLevelId()) return
const side = getSideFromNormal(event.normal) const side = getSideFromNormal(event.normal)
const itemRotation = calculateItemRotation(event.normal) const itemRotation = calculateItemRotation(event.normal)
@@ -142,13 +157,19 @@ export const WindowTool: React.FC = () => {
draftRef.current?.id, draftRef.current?.id,
) )
updateCursor(wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset()), cursorRotation, valid) updateCursor(
wallLocalToWorld(event.node, clampedX, clampedY, getLevelYOffset(), getSlabElevation(event)),
cursorRotation,
valid,
)
event.stopPropagation() event.stopPropagation()
} }
const onWallClick = (event: WallEvent) => { const onWallClick = (event: WallEvent) => {
if (!draftRef.current) return if (!draftRef.current) return
if (!isValidWallSideFace(event.normal)) return if (!isValidWallSideFace(event.normal)) return
// Only interact with walls on the current level
if (event.node.parentId !== getLevelId()) return
const side = getSideFromNormal(event.normal) const side = getSideFromNormal(event.normal)
const itemRotation = calculateItemRotation(event.normal) const itemRotation = calculateItemRotation(event.normal)