From bd84ac6176799050fb40fe4e726754445d09ed51 Mon Sep 17 00:00:00 2001 From: ypshim Date: Tue, 10 Nov 2026 15:38:30 +0900 Subject: [PATCH] =?UTF-8?q?fix(shape):=20=ED=9B=84=EC=97=B4=20=EA=B3=84?= =?UTF-8?q?=EB=8B=A8=20=EA=B2=BD=EA=B3=84=EB=A5=BC=20=ED=9B=84=EC=97=B4=20?= =?UTF-8?q?=EB=B0=9B=EC=B9=A8=20=EC=82=AC=EC=9D=B4=20=EC=A4=91=EC=95=99?= =?UTF-8?q?=EC=97=90=20=EB=91=94=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 전·후열 받침 수가 같으면 후열 계단 경계를 전열 경계로 썼기 때문에, 후열 받침이 전열과 마주 보지 않는 교각 (산외교 P11 ⑥|⑦: 경계 12.75, 받침 11.64 · 18.49)에서 경계가 중앙에서 벗어났다. 후열도 자기 받침 사이 중앙을 쓰되, 전열 경계와 블럭 반폭(0.6 m) 이내로 가까우면 전열 경계에 맞춘다 (P1처럼 약간 엇갈린 열에 얇은 단이 생기지 않게). Co-Authored-By: Claude Haiku 5.5 --- src/core/geometry.ts | 11 +++++++++-- tests/validate-and-foundation.test.ts | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/core/geometry.ts b/src/core/geometry.ts index 01c5271..fb2a00c 100644 --- a/src/core/geometry.ts +++ b/src/core/geometry.ts @@ -344,9 +344,16 @@ export function buildPier(input: PierInput, ctx: PierContext = {}): PierModel { const segments: Segment[] = frontPlaced.map((b, k) => ({ s0: bounds[k], s1: bounds[k + 1], top: seatOf.get(b.no) ?? b.z - h1 })); // Lateral step (spec §4.3, Q1·Q2 추천안): front and back tops differ by 5 mm or more in some segment. + // The back steps break midway between consecutive back bearings (2026-11-10), as the front ones do: back bearings need + // not sit opposite the front ones (산외교 P11 ⑥ ↔ ⑦). With equal counts, a back bound less than half a block from the + // front bound snaps to it, so slightly staggered rows (P1, ≈ 0.09 m) keep one boundary and no sliver step. + const backBound = (k: number) => { + const own = (backPlaced[k-1].g + backPlaced[k].g) / 2; + return !unequalRows && Math.abs(own - bounds[k]) < DIM.blockSize / 2 ? bounds[k] : own; + }; const backSegments: Segment[] = backPlaced.map((b,k) => ({ - s0: unequalRows ? (k === 0 ? 0 : (backPlaced[k-1].g+b.g)/2) : segments[k].s0, - s1: unequalRows ? (k === backPlaced.length-1 ? L : (b.g+backPlaced[k+1].g)/2) : segments[k].s1, + s0: k === 0 ? 0 : backBound(k), + s1: k === backPlaced.length-1 ? L : backBound(k + 1), top: seatOf.get(b.no) ?? b.z-h2, })); const stepBounds = [...new Set([0,L,...segments.slice(1).map(s=>s.s0),...backSegments.slice(1).map(s=>s.s0)])].sort((a,b)=>a-b); diff --git a/tests/validate-and-foundation.test.ts b/tests/validate-and-foundation.test.ts index 5559103..d519167 100644 --- a/tests/validate-and-foundation.test.ts +++ b/tests/validate-and-foundation.test.ts @@ -255,6 +255,27 @@ describe('횡방향 단차 (spec §4.3, Q1·Q2 추천안)', () => { } }); + it('slightly staggered rows (P1, < half a block) keep the front bounds for the back steps', () => { + const m = buildPier(loadP1()); + expect(m.backSegments!.map((x) => x.s0)).toEqual(m.segments.map((x) => x.s0)); + }); + + it('back steps break midway between back bearings, even when they are not opposite the front ones (2026-11-10)', () => { + const p1 = loadP1(); + // Move back bearing 7 by 1.5 m along the row: its bounds follow it, the front bounds do not. + const m0 = buildPier(p1); + const u = m0.u; + const bearings = p1.bearings.map((b) => (b.no === 7 ? { ...b, x: b.x + 1.5 * u.e, y: b.y + 1.5 * u.n } : b)); + const m = buildPier({ ...p1, bearings }); + const back = m.bearings.filter((b) => b.row === '후열').sort((a, b) => a.g - b.g); + const bs = m.backSegments!.map((x) => x.s0); + // ⑥|⑦ and ⑦|⑧ move 0.75 m (> half a block): own midpoints. ⑧|⑨ and ⑨|⑩ stay on the front bounds. + expect(bs[1]).toBeCloseTo((back[0].g + back[1].g) / 2, 9); + expect(bs[2]).toBeCloseTo((back[1].g + back[2].g) / 2, 9); + expect(bs.slice(3)).toEqual(m.segments.slice(3).map((x) => x.s0)); + expect(m.segments.map((x) => x.s0)).toEqual(m0.segments.map((x) => x.s0)); + }); + it('a 3 mm difference is below the 5 mm tolerance: no step', () => { const p1 = loadP1(); const bearings = p1.bearings.map((b) => (b.row === '후열' && b.no === 7 ? { ...b, z: b.z + 0.003 } : b));