From 02aae8daf19a417adb3b1ce9ccd22a26dddf0080 Mon Sep 17 00:00:00 2001 From: ahmadalguydi Date: Wed, 5 Aug 2026 06:13:41 +0300 Subject: [PATCH] fix(core): stabilize collinear wall junction ordering --- .../core/src/systems/wall/wall-mitering.test.ts | 15 +++++++++++++++ packages/core/src/systems/wall/wall-mitering.ts | 9 +++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/core/src/systems/wall/wall-mitering.test.ts b/packages/core/src/systems/wall/wall-mitering.test.ts index 815c505a7..cbe1b331d 100644 --- a/packages/core/src/systems/wall/wall-mitering.test.ts +++ b/packages/core/src/systems/wall/wall-mitering.test.ts @@ -79,6 +79,21 @@ describe('wall miter boundary sides', () => { }) }) +describe('deterministic junction ordering', () => { + test('same wall set produces the same miter data regardless of input order', () => { + const thickWall = (id: string, thickness: number, end: [number, number]) => + ({ ...wall(id, [0, 0], end), thickness }) as WallNode + const wide = thickWall('wide', 0.6, [4, 0]) + const narrow = thickWall('narrow', 0.2, [4, 0]) + const branch = thickWall('branch', 0.4, [0, 4]) + + const forward = calculateLevelMiters([wide, narrow, branch]).junctionData.get('0,0') + const reversed = calculateLevelMiters([narrow, wide, branch]).junctionData.get('0,0') + + expect(forward).toEqual(reversed) + }) +}) + describe('junction grid prefilter', () => { function thickWall( id: string, diff --git a/packages/core/src/systems/wall/wall-mitering.ts b/packages/core/src/systems/wall/wall-mitering.ts index d9fa5bc0a..b1fd5a6bd 100644 --- a/packages/core/src/systems/wall/wall-mitering.ts +++ b/packages/core/src/systems/wall/wall-mitering.ts @@ -357,8 +357,13 @@ function calculateJunctionIntersections( } } - // Sort by outgoing angle - processedWalls.sort((a, b) => a.angle - b.angle) + // Sort by outgoing angle, then by wall ID so equal-angle walls produce the + // same pairing regardless of scene iteration order. + processedWalls.sort((a, b) => { + const angleOrder = a.angle - b.angle + if (angleOrder !== 0) return angleOrder + return a.wallId < b.wallId ? -1 : a.wallId > b.wallId ? 1 : 0 + }) const wallIntersections = new Map() const n = processedWalls.length