From 9c811c4d20a1617707df3d24518f90fa4ad40c8a Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 22:16:24 +0900 Subject: [PATCH] =?UTF-8?q?fix(B06):=20=EA=B5=AC=EC=A1=B0=EB=AC=BC?= =?UTF-8?q?=EC=9D=84=20=EB=86=93=EC=95=84=EB=8F=84=20=ED=9A=A1=EB=8B=A8?= =?UTF-8?q?=EB=8F=84=EC=97=90=20=EC=95=88=20=EB=B3=B4=EC=9D=B4=EB=8D=98=20?= =?UTF-8?q?=EA=B2=83=20=E2=80=94=20=EB=B9=88=20=EC=B4=88=EC=95=88=EC=9D=B4?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=EB=B6=84=EC=9D=84=20=EC=A7=80=EC=9A=B0?= =?UTF-8?q?=EA=B3=A0=20=EC=9E=88=EC=97=88=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 「구조물을 놓아도 횡단도에 아무것도 안 보인다」가 창 둘에서 같은 증상이었다. 화면에 표식을 심어 재 보니 **브라우저 쪽 단면에 `revetment` 자체가 없었다**. - 원인: `withDraftWalls` 가 미저장 초안 목록이 **빈 배열**일 때도 「초안 있음」으로 읽고 **서버 저장분(`revetment`)을 통째로 지운 뒤** 아무것도 안 얹었다. ⇒ `!drafts || !drafts.length` 로 고쳤다 — 빈 목록도 「초안 없음」이다. - 초안 경로가 `foundation` 을 안 실어 터파기가 안 그려지던 것도 함께 고침 (`WallSpec.foundation` 추가, 거울 시험도 같이 갱신). - 겹침 방지 가드를 **좁혔다** — 링크가 있기만 하면 막던 것을, 그 링크의 벽이 **이 카드에 실제로 설 때만** 막게 했다(`culvertWallsStandAt`). 관이 아홉·열하나인 노선에서는 링크가 거의 모든 측점을 덮어 독립 벽 경로가 통째로 죽어 있었다. 화면 확인: 벽 터파기 「기초유 · 폭 1.62m · 깊이 0.50m (수직)」, 관 터파기 Φ1000 1.80m · Φ800 1.60m, 그려진 선 전부 좌우 변이 수직. Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_Section_Store.ts | 5 ++++- .../B06_Section_UI_Cross_Culvert_Wire.ts | 21 +++++++++++++++++++ B06_Section/B06_Section_UI_Cross_View.ts | 8 ++++++- common_util/common_util_structure_walls.ts | 4 ++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/B06_Section/B06_Section_Section_Store.ts b/B06_Section/B06_Section_Section_Store.ts index 38e54977..41ce275e 100644 --- a/B06_Section/B06_Section_Section_Store.ts +++ b/B06_Section/B06_Section_Section_Store.ts @@ -52,7 +52,10 @@ async function withDraftWalls( projectId: string, ): Promise { const drafts = readPendingStructures(projectId); - if (!drafts) return withStructureAreas(detail); + // ⚠ **빈 목록도 「초안 없음」이다**(2026-09-09). 예전에는 `[]` 가 「초안이 있는데 벽이 + // 하나도 없다」로 읽혀 **아래에서 서버 저장분을 통째로 지웠다** — 구조물을 놓아도 + // 횡단도에 아무것도 안 보이던 결함의 원인이다(창 둘에서 같은 증상, 실측 확인). + if (!drafts || !drafts.length) return withStructureAreas(detail); const types = await fetchStructureTypes().catch(() => []); const names = new Map( types diff --git a/B06_Section/B06_Section_UI_Cross_Culvert_Wire.ts b/B06_Section/B06_Section_UI_Cross_Culvert_Wire.ts index 27c1484b..e4ccee55 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert_Wire.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert_Wire.ts @@ -256,6 +256,27 @@ export function wallStandsAt(owner: CrossSection, section: CrossSection, key: st return spanCovers(revetSpanOfSpec(side), deltaM); } +/** + * 링크된 관의 벽이 **이 카드에 실제로 서는가**. 하나도 안 서면 그 링크는 이 측점의 + * 그림을 막을 이유가 없다 — 독립 벽(D경로)이 그려져야 한다. + * + * 왜 있나(2026-09-09) — 「관이 없는 측점인데도 구조물을 놓으면 아무것도 안 보인다」가 + * 사용자에게 보이던 결함이었다. 겹침 방지 가드가 **링크가 있기만 하면** 막고 있었는데, + * 관이 아홉·열하나인 노선에서는 링크가 거의 모든 측점을 덮어 D경로가 통째로 죽었다. + * 가드를 없애지 않고 **좁힌다** — 벽이 실제로 서는 카드에서만 막는다. + */ +export function culvertWallsStandAt(owner: CrossSection, section: CrossSection): boolean { + const keys = ["inlet", "outlet"]; + const counts = owner.design?.extra_wall_counts ?? {}; + for (const [side, count] of Object.entries(counts)) { + const total = Math.max(Math.trunc(Number(count) || 0), 0); + for (let index = 0; index < total; index += 1) { + keys.push(side === "basin" ? `bextra${index}` : `extra${index}`); + } + } + return keys.some((key) => wallStandsAt(owner, section, key)); +} + export function culvertLinkFor( section: CrossSection, sections: readonly CrossSection[], diff --git a/B06_Section/B06_Section_UI_Cross_View.ts b/B06_Section/B06_Section_UI_Cross_View.ts index b5c18b79..061fd070 100644 --- a/B06_Section/B06_Section_UI_Cross_View.ts +++ b/B06_Section/B06_Section_UI_Cross_View.ts @@ -18,6 +18,7 @@ import { appendFordPavementOverlay, appendFordSurfaceDropPlan, } from "./B06_Section_UI_Cross_Ford_Pavement"; +import { culvertWallsStandAt } from "./B06_Section_UI_Cross_Culvert_Wire"; import { appendRevetmentOverlay, computeRevetmentLayout } from "./B06_Section_UI_Cross_Revetment"; import { appendCrossDesignOverlay, @@ -422,7 +423,12 @@ export function createCrossSectionCard( // 이 측점에 배관/숨김 기슭막이 세트가 직접 붙었거나(section.culvert) **연동으로 // 옆에서 이어져 온**(culvertLink) 경우엔 배관 경로가 그린다 — 옛 D경로는 건너뛴다 // (둘 다 그리면 이웃 카드에 벽이 겹친다 — 2026-08-28 이관 이중그리기 방지). - if (!section.culvert && !culvertLink) { + // ⚠ 가드를 **좁혔다**(2026-09-09) — 예전에는 링크가 **있기만 하면** 막았는데, + // 관이 아홉·열하나인 노선에서는 링크가 거의 모든 측점을 덮어 **구조물을 놓아도 + // 횡단도에 아무것도 안 보였다**(사용자에게 보이던 결함). 겹침 방지라는 까닭은 + // 그대로 두고, **그 링크의 벽이 이 카드에 실제로 설 때만** 막는다. + const linkedWallsHere = !!culvertLink && culvertWallsStandAt(culvertLink.source, section); + if (!section.culvert && !linkedWallsHere) { const ownAdjust = revetOffset?.adjustFor(section, "own"); const ownLayout = computeRevetmentLayout(section, ownAdjust); ownDesignTrim = ownLayout?.designTrim; diff --git a/common_util/common_util_structure_walls.ts b/common_util/common_util_structure_walls.ts index 1047ed46..5cd7c2e8 100644 --- a/common_util/common_util_structure_walls.ts +++ b/common_util/common_util_structure_walls.ts @@ -32,6 +32,8 @@ export interface WallSpec { form: string | null; height_m: number | null; side: string | null; + /** 기초 축 — "기초유" | "기초버림". 저장 칸과 같은 글자(터파기 그림이 이 값으로 갈린다). */ + foundation: string | null; tiers: number | null; lift_m: number | null; shift_m: number | null; @@ -78,6 +80,8 @@ export function wallSpecsFrom( form: (options.form as string) || FORM_BY_TYPE[structure.type_id] || null, height_m: num(options.height_m), side: (options.side as string) ?? null, + // 기초 축 — 저장 칸과 **같은 글자**. 초안 경로에서 빠지면 터파기가 안 그려진다. + foundation: (options.foundation as string) ?? null, tiers: num(options.tiers), lift_m: num(options.lift_m), shift_m: num(options.shift_m),