From 8106b1ed9b78be85a70b2e4ef14c9e350f3ed4f5 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 30 Aug 2026 13:19:58 +0900 Subject: [PATCH] =?UTF-8?q?fix(B06):=20=EC=9B=94=EB=A5=98=20=EC=A0=84=20?= =?UTF-8?q?=EB=85=B8=EB=A9=B4=20=EC=A0=90=EC=84=A0=EC=9D=84=20=EC=B8=A1?= =?UTF-8?q?=EB=B2=BD=20=EC=83=81=EB=8B=A8=20=EA=BC=AD=EC=A7=93=EC=A0=90?= =?UTF-8?q?=EC=97=90=20=EB=B6=99=EC=9D=B8=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 날개벽 상단(바닥판 바깥 끝)까지 내려 이으니 점선이 구체 밖으로 크게 벌어져 읽기 나빴다. 좌측 벽이면 **좌상단**, 우측 벽이면 **우상단** — 측벽 상단의 계류측 꼭짓점에 붙인다(2026-08-30 사용자, 각도는 무시). - `FordSideLayout.wingTop` → `wallTopOuter`로 바꿨다. 벽 폴리곤의 상단 두 점 중 계류측 것이라 날개벽 설치 여부와 무관하게 항상 있다(널 분기가 사라졌다). 자체검증(공용 브라우저 5174 + 워크트리 백엔드 8001, 측점 7+9.7): - tsc --noEmit 통과. - 점선 오른쪽 끝 `404.125,89.644` = 우측 벽 폴리곤의 우상단 꼭짓점과 좌표 일치 (벽 = `399.25,133.519 399.25,89.644 404.125,89.644 404.125,133.519`). 왼쪽 끝도 대칭으로 좌상단에 붙는다. Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_UI_Cross_Ford_Geom.ts | 25 +++++++++---------- .../B06_Section_UI_Cross_Ford_Pavement.ts | 22 ++++++++-------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/B06_Section/B06_Section_UI_Cross_Ford_Geom.ts b/B06_Section/B06_Section_UI_Cross_Ford_Geom.ts index 8a166d10..d40326c0 100644 --- a/B06_Section/B06_Section_UI_Cross_Ford_Geom.ts +++ b/B06_Section/B06_Section_UI_Cross_Ford_Geom.ts @@ -72,9 +72,9 @@ export interface FordSideLayout { pipeEnd: OffsetPoint; /** 바닥판 안쪽 변(위·아래) — 두 측벽 사이를 잇는 판이 여기서 시작한다. */ slabInner: [OffsetPoint, OffsetPoint]; - /** 날개벽 상단(바닥판 바깥 끝 × 바닥판 상면 + 짧은쪽 높이). 설치가 없으면 null. - * 월류 전 노면(점선)이 이 점까지 대각으로 내려온다(2026-08-30 사용자). */ - wingTop: OffsetPoint | null; + /** 측벽 상단의 **계류측 꼭짓점** — 좌측 벽이면 좌상단, 우측 벽이면 우상단. + * 월류 전 노면(점선)이 이 점까지 대각으로 내려온다(2026-08-30 사용자, 각도 무시). */ + wallTopOuter: OffsetPoint; /** 구조물 계류측 끝이 원지반 위일 때의 성토부선(집수정과 같은 체계). */ fillSegments: OutletFillSegment[]; /** 원지반 안으로 박힐 때의 절토선. */ @@ -225,16 +225,15 @@ export function computeFordLayout( slabTopElevation: built.pipeEnd.elevation, pipeEnd: built.pipeEnd, slabInner: [floor.points[0], floor.points[3]], - // 날개벽 상단 — 바닥판 바깥 끝(계류측 극점) 위로 짧은쪽 높이만큼. - wingTop: wing.installed - ? { - offset: floor.points.reduce( - (far, point) => ((point.offset - far) * outward > 0 ? point.offset : far), - floor.points[0].offset, - ), - elevation: built.pipeEnd.elevation + (wing.height_m ?? 0), - } - : null, + // 벽 상단 두 꼭짓점 중 계류측 것 — 점선이 여기로 내려와 붙는다. + wallTopOuter: (() => { + const topZ = Math.max(...wall.points.map((point) => point.elevation)); + const tops = wall.points.filter((point) => point.elevation > topZ - 1e-6); + return tops.reduce( + (far, point) => ((point.offset - far.offset) * outward > 0 ? point : far), + tops[0], + ); + })(), fillSegments: extras.segments, cutLine: built.basin.cutLine, adjust: { diff --git a/B06_Section/B06_Section_UI_Cross_Ford_Pavement.ts b/B06_Section/B06_Section_UI_Cross_Ford_Pavement.ts index 3802cf50..734c63d0 100644 --- a/B06_Section/B06_Section_UI_Cross_Ford_Pavement.ts +++ b/B06_Section/B06_Section_UI_Cross_Ford_Pavement.ts @@ -74,31 +74,31 @@ function line(points: string[], className: string): SVGPolylineElement { * (`design.surface_drop_m`) 여기서는 그 양만큼 위로 올린 선을 얹는다. * 물넘이포장의 「기존 계획고 점선」과 같은 표기라 사용자가 한 규칙으로 읽는다. * - * 양 끝은 **날개벽 상단까지 대각으로 내려 잇는다**(2026-08-30 사용자) — 월류 전 - * 노면이 구체 밖에서 어디로 떨어지는지가 그 선으로 읽힌다. 날개벽이 없는 측은 - * 노견에서 끊는다. + * 양 끝은 **측벽 상단의 계류측 꼭짓점까지 대각으로 내려 잇는다**(2026-08-30 사용자: + * 좌측 벽이면 좌상단, 우측 벽이면 우상단, 각도는 무시). 월류 전 노면이 구체 밖에서 + * 어디로 떨어지는지가 그 선으로 읽힌다. */ export function appendFordSurfaceDropPlan( svg: SVGElement, design: CrossDesign, x: (offset: number) => number, y: (elevation: number) => number, - ford?: { sides: ReadonlyArray<{ outward: number; wingTop: OffsetPoint | null }> } | null, + ford?: { sides: ReadonlyArray<{ outward: number; wallTopOuter: OffsetPoint }> } | null, ): void { const drop = design.surface_drop_m ?? 0; if (!(drop > 0) || !design.road_edges) return; const { left, right } = design.road_edges; - // +offset이 좌측이다 — 좌 노견 바깥(날개벽) → 좌 노견 → 우 노견 → 우 노견 바깥 순. - const wingAt = (side: 1 | -1): OffsetPoint | null => - ford?.sides.find((entry) => Math.sign(entry.outward) === side)?.wingTop ?? null; + // +offset이 좌측이다 — 좌측 벽 상단 → 좌 노견 → 우 노견 → 우측 벽 상단 순. + const cornerAt = (side: 1 | -1): OffsetPoint | undefined => + ford?.sides.find((entry) => Math.sign(entry.outward) === side)?.wallTopOuter; const points: OffsetPoint[] = [ { offset: left.offset_m, elevation: left.elevation_m + drop }, { offset: right.offset_m, elevation: right.elevation_m + drop }, ]; - const leftWing = wingAt(1); - if (leftWing) points.unshift(leftWing); - const rightWing = wingAt(-1); - if (rightWing) points.push(rightWing); + const leftCorner = cornerAt(1); + if (leftCorner) points.unshift(leftCorner); + const rightCorner = cornerAt(-1); + if (rightCorner) points.push(rightCorner); svg.append( line( points.map((point) => `${x(point.offset)},${y(point.elevation)}`),