Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/ifc-converter/src/cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,23 @@ function toWallSegment(wall: WallNode): WallSegment | null {
}

function wallLineTolerance(a: WallSegment, b: WallSegment) {
return Math.max(0.06, Math.min(0.14, Math.max(a.thickness, b.thickness) * 0.5))
// Fragments must share essentially the same centerline. A tolerance based on
// half the wall thickness can collapse adjacent walls whose faces merely meet.
return Math.max(0.005, Math.min(0.025, Math.max(a.thickness, b.thickness) * 0.1))
}

function wallHeightCompatible(a: WallSegment, b: WallSegment) {
return Math.abs(a.height - b.height) <= WALL_HEIGHT_TOLERANCE
}

function wallMaterialCompatible(a: WallSegment, b: WallSegment) {
const materialA = (a.wall.metadata as { material?: unknown } | undefined)?.material
const materialB = (b.wall.metadata as { material?: unknown } | undefined)?.material
const nameA = typeof materialA === 'string' ? materialA : null
const nameB = typeof materialB === 'string' ? materialB : null
return nameA === nameB
}
Comment thread
yorhodes marked this conversation as resolved.

function wallIntervalsCompatible(a: WallSegment, b: WallSegment, maxJoinGap: number) {
const gap = Math.max(a.t0, b.t0) - Math.min(a.t1, b.t1)
if (gap <= maxJoinGap) return true
Expand All @@ -220,6 +230,7 @@ function wallsCanMerge(a: WallSegment, b: WallSegment, maxJoinGap: number) {
if (Math.abs(a.angleBucket - b.angleBucket) > 1) return false
if (Math.abs(a.offset - b.offset) > wallLineTolerance(a, b)) return false
if (!wallHeightCompatible(a, b)) return false
if (!wallMaterialCompatible(a, b)) return false
return wallIntervalsCompatible(a, b, maxJoinGap)
}

Expand Down
45 changes: 45 additions & 0 deletions packages/ifc-converter/tests/cleanup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,51 @@ describe('simplifyConvertedSceneGraph', () => {
expect((nodes.level_1 as { children: string[] }).children).toEqual([keptWall?.id])
})

it('does not merge parallel wall fragments on centerlines offset by two inches', () => {
const nodes: Record<string, AnyNode> = {
level_1: level('level_1', ['wall_a', 'wall_b']),
wall_a: wall('wall_a', [0, 0], [2, 0]),
wall_b: wall('wall_b', [2, 0.0508], [4, 0.0508]),
}

const stats = simplifyConvertedSceneGraph(nodes)

expect(stats.removedMergedWalls).toBe(0)
expect(Object.values(nodes).filter((node) => node.type === 'wall')).toHaveLength(2)
})

it('does not merge collinear wall fragments with different IFC materials', () => {
const exterior = wall('wall_exterior', [0, 0], [2, 0])
const interior = wall('wall_interior', [2.9, 0], [5, 0])
exterior.metadata = { material: 'Exterior Finish Assembly' }
interior.metadata = { material: 'Interior Partition Assembly' }
const nodes: Record<string, AnyNode> = {
level_1: level('level_1', ['wall_exterior', 'wall_interior']),
wall_exterior: exterior,
wall_interior: interior,
}

const stats = simplifyConvertedSceneGraph(nodes)

expect(stats.removedMergedWalls).toBe(0)
expect(Object.values(nodes).filter((node) => node.type === 'wall')).toHaveLength(2)
})

it('does not merge a material-tagged wall with an untagged wall', () => {
const tagged = wall('wall_tagged', [0, 0], [2, 0])
tagged.metadata = { material: 'Exterior Finish Assembly' }
const nodes: Record<string, AnyNode> = {
level_1: level('level_1', ['wall_tagged', 'wall_unknown']),
wall_tagged: tagged,
wall_unknown: wall('wall_unknown', [2.9, 0], [5, 0]),
}

const stats = simplifyConvertedSceneGraph(nodes)

expect(stats.removedMergedWalls).toBe(0)
expect(Object.values(nodes).filter((node) => node.type === 'wall')).toHaveLength(2)
})

it('reprojects openings from removed walls onto the merged wall', () => {
const nodes: Record<string, AnyNode> = {
level_1: level('level_1', ['wall_a', 'wall_b']),
Expand Down