63 lines
2.2 KiB
TypeScript

import type { WallNode } from '@pascal-app/core'
/**
* Default sill height (metres from the floor to the BOTTOM of a window) for a
* fresh window that has no wall-face height yet — the off-wall ghost and the
* floor-cursor placement use it so a new window floats slightly above the
* ground rather than sitting on it. The committed Y is the window's CENTRE, so
* callers add `height / 2`. An existing window keeps its own sill.
*/
export const DEFAULT_WINDOW_SILL_M = 0.5
/**
* 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).
* Pass levelYOffset (the level group's current world Y) and slabElevation (the
* 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(
wallNode: WallNode,
localX: number,
localY: number,
levelYOffset = 0,
slabElevation = 0,
): [number, number, number] {
const wallAngle = Math.atan2(
wallNode.end[1] - wallNode.start[1],
wallNode.end[0] - wallNode.start[0],
)
return [
wallNode.start[0] + localX * Math.cos(wallAngle),
slabElevation + localY + levelYOffset,
wallNode.start[1] + localX * Math.sin(wallAngle),
]
}
/**
* Clamps window center position so it stays fully within wall bounds.
*/
export function clampToWall(
wallNode: WallNode,
localX: number,
localY: number,
width: number,
height: number,
): { clampedX: number; clampedY: number } {
const dx = wallNode.end[0] - wallNode.start[0]
const dz = wallNode.end[1] - wallNode.start[1]
const wallLength = Math.sqrt(dx * dx + dz * dz)
const wallHeight = wallNode.height ?? 2.5
const clampedX = Math.max(width / 2, Math.min(wallLength - width / 2, localX))
const clampedY = Math.max(height / 2, Math.min(wallHeight - height / 2, localY))
return { clampedX, clampedY }
}
/**
* Wall-child overlap is shared by door + window placement (one source of
* truth in `shared/wall-attach-target.ts`). Re-exported here so existing
* `./window-math` importers don't change.
*/
export { hasWallChildOverlap } from '../shared/wall-attach-target'