Add stair openings to ceilings

This commit is contained in:
sudhir
2026-04-17 13:19:42 +05:30
parent 7ca94ce375
commit 49836360ca
@@ -1,4 +1,4 @@
import type { AnyNode, AnyNodeId, LevelNode, SlabNode, StairNode, StairSegmentNode } from '../../schema'
import type { AnyNode, AnyNodeId, CeilingNode, LevelNode, SlabNode, StairNode, StairSegmentNode } from '../../schema'
import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync'
import { DEFAULT_WALL_HEIGHT } from '../wall/wall-footprint'
@@ -542,6 +542,23 @@ function getTargetSlabElevationForStair(
)
}
function getTargetCeilingElevationForStair(
stair: StairNode,
ceiling: CeilingNode,
ceilingLevelId: string,
nodes: Record<string, AnyNode>,
) {
const { fromLevelId } = getResolvedStairLevelIds(stair, nodes)
const fromLevel = getLevelNumber(fromLevelId, nodes)
const ceilingLevel = getLevelNumber(ceilingLevelId, nodes)
if (fromLevel === undefined || ceilingLevel === undefined) {
return ceiling.height ?? DEFAULT_WALL_HEIGHT
}
return (ceilingLevel - fromLevel) * DEFAULT_WALL_HEIGHT + (ceiling.height ?? DEFAULT_WALL_HEIGHT) - (stair.position[1] ?? 0)
}
function shouldApplyStairToSlab(stair: StairNode, slabLevelId: string, nodes: Record<string, AnyNode>) {
const { fromLevelId, toLevelId } = getResolvedStairLevelIds(stair, nodes)
const fromLevel = getLevelNumber(fromLevelId, nodes)
@@ -561,10 +578,30 @@ function shouldApplyStairToSlab(stair: StairNode, slabLevelId: string, nodes: Re
return slabLevel > minLevel && slabLevel <= maxLevel
}
function shouldApplyStairToCeiling(stair: StairNode, ceilingLevelId: string, nodes: Record<string, AnyNode>) {
const { fromLevelId, toLevelId } = getResolvedStairLevelIds(stair, nodes)
const fromLevel = getLevelNumber(fromLevelId, nodes)
const toLevel = getLevelNumber(toLevelId, nodes)
const ceilingLevel = getLevelNumber(ceilingLevelId, nodes)
if (ceilingLevel === undefined) {
return fromLevelId === ceilingLevelId
}
if (fromLevel === undefined || toLevel === undefined) {
return fromLevelId === ceilingLevelId
}
const minLevel = Math.min(fromLevel, toLevel)
const maxLevel = Math.max(fromLevel, toLevel)
return ceilingLevel >= minLevel && ceilingLevel < maxLevel
}
export function syncAutoStairOpenings(nodes: Record<string, AnyNode>) {
const stairs = Object.values(nodes).filter((node): node is StairNode => node.type === 'stair' && node.visible !== false)
const slabs = Object.values(nodes).filter((node): node is SlabNode => node.type === 'slab')
const updates: Array<{ id: AnyNodeId; data: Partial<SlabNode> }> = []
const ceilings = Object.values(nodes).filter((node): node is CeilingNode => node.type === 'ceiling')
const updates: Array<{ id: AnyNodeId; data: Partial<SlabNode | CeilingNode> }> = []
for (const slab of slabs) {
const slabLevelId = resolveLevelId(slab, nodes)
@@ -611,5 +648,50 @@ export function syncAutoStairOpenings(nodes: Record<string, AnyNode>) {
}
}
for (const ceiling of ceilings) {
const ceilingLevelId = resolveLevelId(ceiling, nodes)
const existingHoles = ceiling.holes ?? []
const existingMetadata = normalizeExistingMetadata(existingHoles, ceiling.holeMetadata)
const manualHoles = existingHoles.filter((_hole, index) => existingMetadata[index]?.source !== 'stair')
const manualMetadata = existingMetadata
.filter((entry) => entry.source !== 'stair')
.map((entry) => ({ ...entry }))
const stairHoles = stairs
.filter((stair) => shouldApplyStairToCeiling(stair, ceilingLevelId, nodes))
.flatMap((stair) =>
getStairOpeningPolygons(
stair,
nodes,
getTargetCeilingElevationForStair(stair, ceiling, ceilingLevelId, nodes),
).map((polygon) => ({
polygon:
stair.stairType === 'straight'
? polygon
: expandPolygonFromCentroid(
polygon,
Math.max((stair.openingOffset ?? 0) - STAIR_SLAB_OPENING_TIGHTENING, 0),
),
metadata: {
source: 'stair' as const,
stairId: stair.id,
},
})),
)
const nextHoles = [...manualHoles, ...stairHoles.map((hole) => hole.polygon)]
const nextMetadata = [...manualMetadata, ...stairHoles.map((hole) => hole.metadata)]
if (!polygonsEqual(existingHoles, nextHoles) || !metadataEqual(existingMetadata, nextMetadata)) {
updates.push({
id: ceiling.id,
data: {
holes: nextHoles,
holeMetadata: nextMetadata,
},
})
}
}
return updates
}