From 37e3ee612648f77db9a9fe87ccc6eeba49968235 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 22:03:40 +0900 Subject: [PATCH] =?UTF-8?q?fix(479=20B06=20=ED=9A=A1=EB=8B=A8=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=B9=B8):=20=EC=B2=98=EC=9D=8C=20=EB=B9=88=20?= =?UTF-8?q?=EC=B9=B8=20=C2=B7=20=EC=95=94=20=EC=A0=95=EB=B3=B4=EB=8A=94=20?= =?UTF-8?q?=EC=95=94=20=EC=A0=90=EC=84=A0=20=EA=B3=A0=EB=A5=BC=20=EB=95=8C?= =?UTF-8?q?=EB=A7=8C=20=E2=80=94=20=EC=95=88=20=EA=B3=A0=EB=A6=84=20?= =?UTF-8?q?=EC=95=94=20=EA=B0=88=EB=9E=98=20=C2=B7=20=EA=B7=B8=EB=A6=BC=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=B9=B4=EB=93=9C=20=EC=95=94=20=EC=B9=B8?= =?UTF-8?q?=20=C2=B7=20=EC=A3=BD=EC=9D=80=20buildRockBoundaryControl=20?= =?UTF-8?q?=EA=B1=B7=EC=9D=8C=20=C2=B7=20=EC=8B=9C=ED=97=98=20370-3=20?= =?UTF-8?q?=C2=B7=20318=20=C2=B7=20137=20=EA=B3=A0=EC=B3=90=20=EC=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../B06_Section_UI_Cross_Card_Chrome.ts | 20 ++----- B06_Section/B06_Section_UI_Cross_Design.ts | 15 +---- .../B06_Section_UI_Cross_Design_Pick.ts | 57 +++++++++---------- B06_Section/B06_Section_UI_Cross_Line_Cell.ts | 2 +- B06_Section/B06_Section_UI_Cross_View.ts | 2 +- resources/tester/helper_318_cross_card.cjs | 7 +-- resources/tester/helper_370_3_rock_cell.cjs | 4 +- resources/tester/test_137_design_pick.py | 4 +- resources/tester/test_318_cross_card.py | 4 +- resources/tester/test_370_3_rock_cell.py | 14 +++-- 10 files changed, 54 insertions(+), 75 deletions(-) diff --git a/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts b/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts index 118d43b52..5426239be 100644 --- a/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts +++ b/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts @@ -7,13 +7,8 @@ * ========================================================================== */ import type { CrossSection } from "./B06_Section_Api_Fetch"; -import type { RockBoundaryControl } from "./B06_Section_UI_Cross_Design"; import { buildCutSlopeControl, type CutSlopeControl } from "./B06_Section_UI_Cross_CutSlope"; -import { - buildDesignControls, - buildRockBoundaryControl, - sectionModeLabel, -} from "./B06_Section_UI_Cross_Design"; +import { buildDesignControls, sectionModeLabel } from "./B06_Section_UI_Cross_Design"; import { type FillSlopeLength, fillSlopeLengths } from "./logic/B06_Section_UI_Cross_Fit"; import { type DesignChangeHandler, @@ -159,27 +154,20 @@ export function appendCardHeader( card.append(header); } -/** 하단 정보행 — 왼쪽 = 선 공용 칸(370-3 · 안 고르면 암 경계선) · 오른쪽 = (암 지반일 때) 암 절토 경사. */ +/** 하단 정보행 — 왼쪽 = 선 공용 칸(370-3 · 안 고르면 빈 칸 · 479) · 오른쪽 = (암 지반일 때) 암 절토 경사. */ export function appendCardFooter( card: HTMLElement, section: CrossSection, - rockBoundary?: RockBoundaryControl, cutSlope?: CutSlopeControl, - /** 설계선 고름 공용 칸(370-3 · `_Cross_Line_Cell`) — 있으면 왼쪽 칸(안 고르면 암 · 고르면 그 선). */ + /** 설계선 고름 공용 칸(370-3 · `_Cross_Line_Cell`) — 있으면 왼쪽 칸(안 고르면 빈 칸 · 고르면 그 선). */ lineCell?: HTMLElement, ): void { - // 중심고는 그래프 Y축 라벨 상자로 옮김(318-2 · `_Cross_Axes`) — 하단 띠는 암 제어만 · 비어도 띠 높이 그대로 + // 중심고는 그래프 Y축 라벨 상자로 옮김(318-2 · `_Cross_Axes`) — 비어도 띠 높이 그대로 const footer = document.createElement("footer"); if (lineCell) { lineCell.classList.add("b06-cross-card__rockb"); footer.append(lineCell); } - // 암 경계선 제어는 하단 정보 행 왼쪽(318-2) — 암 지반만 · 설계선 고름이 없는 카드. - else if (rockBoundary && section.design?.geometry_preset === "rock") { - const rockControl = buildRockBoundaryControl(section, rockBoundary); - rockControl.classList.add("b06-cross-card__rockb"); - footer.append(rockControl); - } // 암 절토 경사(각도)는 암 측점에만 — 토사 측점은 암반이 없어 쓰이지 않는다 // (2026-09-07 사용자 확정). 전체를 바꾸는 자리는 좌측 [표준 횡단면 설정]이다. if (cutSlope && section.design?.geometry_preset === "rock") { diff --git a/B06_Section/B06_Section_UI_Cross_Design.ts b/B06_Section/B06_Section_UI_Cross_Design.ts index 4876cd1a1..aa7638021 100644 --- a/B06_Section/B06_Section_UI_Cross_Design.ts +++ b/B06_Section/B06_Section_UI_Cross_Design.ts @@ -28,7 +28,7 @@ export { } from "./B06_Section_UI_Cross_Design_Surface"; export { appendCrossDesignOverlay } from "./B06_Section_UI_Cross_Design_Line"; import { openCrossDetail, refreshCrossDetail } from "./B06_Section_UI_Cross_Detail"; -import { buildLineCell, rockCellState, type RockCellSpec } from "./B06_Section_UI_Cross_Line_Cell"; +import type { RockCellSpec } from "./B06_Section_UI_Cross_Line_Cell"; /** 넘침 정리 한 장 몫 — 쓰기·읽기·쓰기 세 토막으로 갈라 두어 프레임 단위로 묶는다. */ interface ReflowCard { @@ -207,18 +207,7 @@ function toggle( return { wrap, button }; } -/** 암 경계선 상/하/리셋 컨트롤 — 아래 띠 공용 칸(370-3 · `_Cross_Line_Cell`)의 「안 고름」 꼴. 설계선 고름이 - * 없는 카드(설계 없음)에서 띠 왼쪽 칸으로 씀. */ -export function buildRockBoundaryControl( - section: CrossSection, - control: RockBoundaryControl, -): HTMLElement { - const cell = buildLineCell(); - cell.show(rockCellState(rockCellSpec(section, control))); - return cell.root; -} - -/** 암 경계선 칸 제원 — 공용 칸(안 고름 · 암 고름)이 같이 씀. */ +/** 암 경계선 칸 제원 — 공용 칸 암 고름(479 — 안 고르면 빈 칸). */ export function rockCellSpec(section: CrossSection, control: RockBoundaryControl): RockCellSpec { return { legend: L("B06_Design_RockBoundary_Legend"), diff --git a/B06_Section/B06_Section_UI_Cross_Design_Pick.ts b/B06_Section/B06_Section_UI_Cross_Design_Pick.ts index c132550a9..75e309713 100644 --- a/B06_Section/B06_Section_UI_Cross_Design_Pick.ts +++ b/B06_Section/B06_Section_UI_Cross_Design_Pick.ts @@ -5,7 +5,7 @@ * 그려진 보라 선(`b06-chart__design-cross`)을 화면 좌표에서 m 로 되돌려 같은 방향 토막을 * 직선 조각 하나로 합치고, 벽 · 본체 폴리곤에 가린 부분을 뺀 **보이는 조각**마다 투명 판정 * 선을 구조물 판정면 아래에 깐다. 고르면 그 조각을 강조하고 아래 띠 공용 칸(370-3 · `_Cross_Line_Cell`)에 - * 제목 · 축에 맞는 버튼 · 치수 한 줄을 적는다(안 고르면 암 경계선 칸 · 떠 있는 선 조정창 · 우측 위 라벨 걷음). + * 제목 · 축에 맞는 버튼 · 치수 한 줄을 적는다(안 고르면 빈 칸 · 479 · 떠 있는 선 조정창 · 우측 위 라벨 걷음). * 구역 판정(`crossZoneAt` · 도로 · 절토 · 성토)은 설계선 쉬프트(139-6)도 쓴다. * 고름 상태는 페이지에 한 칸(측점 + 조각 번호) — 카드를 다시 그려도 되살아난다. * 고른 조각 쪽 사면을 노견째 ◀ ↺ ▶ · 방향키 · 0.1 m 로 옮김(139-6 쉬프트 · 151-3 · @@ -444,7 +444,7 @@ export function attachDesignLinePick(input: { toes?: ReturnType; /** 있으면 쪽마다 노견 끝 = 원지반(절·성토 없음 · 379-1) — 손잡이 값 앞 알림. */ bare?: ReturnType; - /** 있으면 암 경계선 고름(키 `rock` · ▲▼↺ · 세로 끌기) · 안 고른 때 공용 칸 = 암(370-3). */ + /** 있으면 암 경계선 고름(키 `rock` · ▲▼↺ · 세로 끌기 · 370-3) · 안 고르면 빈 칸(479). */ rock?: RockCellSpec; }): DesignLinePick { const { layer, design, x, toDisplayY } = input; @@ -627,33 +627,32 @@ export function attachDesignLinePick(input: { const toe = toeSide ? input.toes?.[toeSide] : null; const dims = piece ? pieceLabel(design, piece).replace("\n", " · ") : ""; const bare = toeSide && input.bare?.[toeSide] ? "절·성토 없음 · " : ""; // 379-1 - // 아래 띠 공용 칸(370-3) — 안 고르면 암(암 지반만) · 고르면 그 선 제목 · 버튼 · 값 한 줄 - const state: LineCellState | null = - onRock || (!chosen && rock) - ? rockCellState(rock as RockCellSpec) - : onCenter - ? { title: "중심점", value: CENTER_HINT, buttons: {} } - : toe - ? { - title: `사면 이동 ${sideName}`, - value: `${bare}노선에서 ${Math.abs(toe.offset_m).toFixed(2)}m${moved}`, - buttons: shift ? sideways(true) : {}, - } - : !piece - ? null - : axis === "y" - ? { title: "노면 높이", value: `${dims} · ▲▼ = 종단 계획고`, buttons: {} } - : axis === "depth" && side - ? { - title: `소단 높이 ${sideName}`, - value: `도로 아래 ${bermDepthOf(design, side).toFixed(1)}m · ${dims}`, - buttons: berm ? lift : {}, - } - : { - title: `사면 이동 ${sideName}`, - value: dims + moved, - buttons: shift ? sideways(true) : {}, - }; + // 아래 띠 공용 칸(370-3) — 안 고르면 빈 칸(479) · 고르면 그 선 제목 · 버튼 · 값 한 줄 + const state: LineCellState | null = onRock + ? rockCellState(rock as RockCellSpec) + : onCenter + ? { title: "중심점", value: CENTER_HINT, buttons: {} } + : toe + ? { + title: `사면 이동 ${sideName}`, + value: `${bare}노선에서 ${Math.abs(toe.offset_m).toFixed(2)}m${moved}`, + buttons: shift ? sideways(true) : {}, + } + : !piece + ? null + : axis === "y" + ? { title: "노면 높이", value: `${dims} · ▲▼ = 종단 계획고`, buttons: {} } + : axis === "depth" && side + ? { + title: `소단 높이 ${sideName}`, + value: `도로 아래 ${bermDepthOf(design, side).toFixed(1)}m · ${dims}`, + buttons: berm ? lift : {}, + } + : { + title: `사면 이동 ${sideName}`, + value: dims + moved, + buttons: shift ? sideways(true) : {}, + }; cell.show(state); const open = onCenter || diff --git a/B06_Section/B06_Section_UI_Cross_Line_Cell.ts b/B06_Section/B06_Section_UI_Cross_Line_Cell.ts index 3e7f6d6ee..e4251125d 100644 --- a/B06_Section/B06_Section_UI_Cross_Line_Cell.ts +++ b/B06_Section/B06_Section_UI_Cross_Line_Cell.ts @@ -2,7 +2,7 @@ * B06_Section_UI_Cross_Line_Cell.ts * 횡단 카드 아래 띠 왼쪽 **공용 칸**(370-3) — 고른 선의 제목 · 축에 맞는 버튼 · 값 한 줄. * - * · 안 고르면 암 경계선 ▲▼↺ · 값(암 지반만 · 토사는 빈 칸 · 띠 높이 그대로) + * · 안 고르면 빈 칸(479 · 띠 높이 그대로) * · 고르면 그 선(노면 · 사면 · 소단 · 중심점 · 교차점 · 암) — `_Cross_Design_Pick` 이 상태를 넣음 * · 버튼 다섯 칸(◀ ▲ ↺ ▼ ▶)은 한 벌 · 상태마다 보임 · 툴팁 · 동작만 바꿈 · 방향키는 보이는 슬롯만 찾음 * · ▲▼ 길게 누름 = 0.5 초 뒤 0.1 초마다 반복(암 · B05 종단 선 제어 꼴) diff --git a/B06_Section/B06_Section_UI_Cross_View.ts b/B06_Section/B06_Section_UI_Cross_View.ts index edbed244c..3f1ad8390 100644 --- a/B06_Section/B06_Section_UI_Cross_View.ts +++ b/B06_Section/B06_Section_UI_Cross_View.ts @@ -680,6 +680,6 @@ export function createCrossSectionCard( card.append(chartWrap); } - appendCardFooter(card, section, rockBoundary, cutSlope, linePick?.cell); // 왼쪽 = 선 공용 칸(370-3) + appendCardFooter(card, section, cutSlope, linePick?.cell); // 왼쪽 = 선 공용 칸(370-3) return card; } diff --git a/resources/tester/helper_318_cross_card.cjs b/resources/tester/helper_318_cross_card.cjs index b79b29dd7..c574bec62 100644 --- a/resources/tester/helper_318_cross_card.cjs +++ b/resources/tester/helper_318_cross_card.cjs @@ -400,7 +400,7 @@ const out = {}; out.draftProfile = { samples: profile.samples, id: profile.id }; } -// ── 2. 아래 띠(318-2 · 318-3) — 중심고 글 없음 · 암 경계 맨 왼쪽 · 토사도 띠 남음 ───── +// ── 2. 아래 띠(318-2 · 318-3) — 중심고 글 없음 · 선 칸 없으면 암 절토만(479) · 토사도 띠 남음 ─ { const control = (cls) => () => { const node = new FakeNode("div"); @@ -410,7 +410,6 @@ const out = {}; const load = makeLoader([B06("B06_Section_UI_Cross_Card_Chrome")], { "./B06_Section_UI_Cross_CutSlope": { buildCutSlopeControl: control("cutslope") }, "./B06_Section_UI_Cross_Design": { - buildRockBoundaryControl: control("rockb"), buildDesignControls: () => ({ bar: new FakeNode("div"), groundSegment: new FakeNode("div") }), sectionModeLabel: () => "", }, @@ -424,7 +423,7 @@ const out = {}; const { appendCardFooter } = load(B06("B06_Section_UI_Cross_Card_Chrome")); const footerOf = (preset) => { const card = new FakeNode("article"); - appendCardFooter(card, { center_z: 876.57, design: { geometry_preset: preset } }, {}, {}); + appendCardFooter(card, { center_z: 876.57, design: { geometry_preset: preset } }, {}); return card.querySelector("footer"); }; const rock = footerOf("rock"); @@ -440,7 +439,7 @@ const out = {}; const cell = new FakeNode("div"); cell.className = "linecell"; const cellCard = new FakeNode("article"); - appendCardFooter(cellCard, { design: { geometry_preset: "rock" } }, {}, {}, cell); + appendCardFooter(cellCard, { design: { geometry_preset: "rock" } }, {}, cell); out.footer.cell_classes = cellCard.querySelector("footer").children.map((kid) => kid.className); } diff --git a/resources/tester/helper_370_3_rock_cell.cjs b/resources/tester/helper_370_3_rock_cell.cjs index a89734c7d..24995d42f 100644 --- a/resources/tester/helper_370_3_rock_cell.cjs +++ b/resources/tester/helper_370_3_rock_cell.cjs @@ -226,7 +226,7 @@ function card(rockOn = true) { const out = {}; -// ① 안 고름 — 공용 칸 = 암 ▲▼↺ · 값 · 방향키는 안 먹음(종단 계획고로 그대로) +// ① 안 고름 — 공용 칸 = 빈 칸(479) · 숨은 단추 눌러도 암 안 움직임 · 방향키는 종단 계획고로 그대로 { const c = card(); c.handle.setActive(null); @@ -283,7 +283,7 @@ const out = {}; sent: c.offsets.slice(before), clickSwallowed: (winListeners.click ?? []).length === 1, }; - // 풀면 다시 암 칸 · 방향키 안 먹음 + // 풀면 빈 칸(479) · 방향키 안 먹음 c.handle.setActive(null); out.released = { ...c.state(), upPassed: !press("ArrowUp") }; // 사면을 고르면 그 선 칸(제목 · ◀↺▶ · 값 한 줄) diff --git a/resources/tester/test_137_design_pick.py b/resources/tester/test_137_design_pick.py index 5ab75f6f0..37b86e9a5 100644 --- a/resources/tester/test_137_design_pick.py +++ b/resources/tester/test_137_design_pick.py @@ -102,7 +102,9 @@ def test_카드_배선_하나만() -> None: assert "setLineActive(pickedLineAt(section.station_id));" in view # 다시 그려도 되살림 # 치수 · 이동 = 아래 띠 왼쪽 공용 칸(370-3 · 옛 [+ − ⤢] 왼쪽 치수 · 떠 있는 이동 패널 걷음) assert "linePick.label" not in view and "linePick.panel" not in view - assert "appendCardFooter(card, section, rockBoundary, cutSlope, linePick?.cell);" in view + assert ( + "appendCardFooter(card, section, cutSlope, linePick?.cell);" in view + ) # 479 — 암 칸 인자 걷음 css = _src("B06_Section/B06_Section_UI_Style_Cross_Areas.css") assert ".b06-chart__design-hit.is-active {" in css diff --git a/resources/tester/test_318_cross_card.py b/resources/tester/test_318_cross_card.py index 65d491dde..2ba716448 100644 --- a/resources/tester/test_318_cross_card.py +++ b/resources/tester/test_318_cross_card.py @@ -118,8 +118,8 @@ def test_footer_without_center_text(result: dict) -> None: footer = result["footer"] assert "중심고" not in footer["rock_text"] assert "876" not in footer["rock_text"] - assert footer["rock_first"].split()[-1] == "b06-cross-card__rockb" # 암 경계 = 왼쪽 첫 칸 - assert footer["rock_classes"][-1].split()[-1] == "b06-cross-card__cutslope" + # 479 — 선 공용 칸이 없는 암 카드 = 암 절토 칸만(암 경계 칸 따로 안 만듦) + assert footer["rock_classes"] == ["cutslope b06-cross-card__cutslope"] assert footer["soil_exists"] and footer["soil_children"] == 0 # 토사도 띠는 남음(빈 띠) # 370-3 — 선 공용 칸 = 왼쪽 첫 칸(암 꼴 클래스) · 암 절토 경사 = 오른쪽 그대로 assert footer["cell_classes"] == [ diff --git a/resources/tester/test_370_3_rock_cell.py b/resources/tester/test_370_3_rock_cell.py index c67fc3de4..1dd9ca9cc 100644 --- a/resources/tester/test_370_3_rock_cell.py +++ b/resources/tester/test_370_3_rock_cell.py @@ -1,5 +1,7 @@ """PLAN 370-3 — B06 암 경계선 고름 · 방향키 · 세로 끌기 · 아래 띠 공용 칸(요구 240-22). +479(요구 280-1) — 안 고르면 공용 칸 = 빈 칸 · 암 정보는 암 점선을 고를 때만. + 짝: `B06_Section/B06_Section_UI_Cross_Design_Pick.ts`(rock 축 · 판정 선) · `B06_Section/B06_Section_UI_Cross_Line_Cell.ts`(공용 칸) · `B06_Section_UI_Cross_Center.ts`(`bindAxisDrag`). 실제 TS 를 `helper_370_3_rock_cell.cjs` 가 Node 로 돌린다. @@ -31,12 +33,11 @@ def out() -> dict: return json.loads(done.stdout) -def test_안_고르면_공용_칸은_암_버튼과_값(out: dict) -> None: +def test_안_고르면_공용_칸은_빈_칸(out: dict) -> None: idle = out["idle"] - assert idle["title"] == "암 경계선" and idle["value"] == "-0.5m" - assert idle["slots"] == ["up", "reset", "down"] + assert idle["empty"] is True and idle["slots"] == [] # 479 — 암 지반도 빈 칸 assert idle["upPassed"] is True # 안 고르면 ▲▼ = 종단 계획고 그대로 - assert idle["offsets"] == [0.1, -0.1] and idle["resets"] == 1 + assert idle["offsets"] == [] and idle["resets"] == 0 # 숨은 단추 눌러도 암 안 움직임 def test_암_경계선_판정_선(out: dict) -> None: @@ -67,8 +68,8 @@ def test_세로_끌기는_놓을_때_한_번(out: dict) -> None: assert out["idleDrag"]["stopped"] is False # 안 고르면 끌기 아님 -def test_풀면_암_칸_사면_고르면_그_선_칸(out: dict) -> None: - assert out["released"]["title"] == "암 경계선" and out["released"]["upPassed"] +def test_풀면_빈_칸_사면_고르면_그_선_칸(out: dict) -> None: + assert out["released"]["empty"] is True and out["released"]["upPassed"] slope = out["slope"] assert slope["title"] == "사면 이동 (좌)" and slope["slots"] == ["left", "reset", "right"] assert slope["value"].startswith("길이 ") and "\n" not in slope["value"] @@ -87,6 +88,7 @@ def test_배선_떠_있는_선_조정창_걷음() -> None: assert "rockBoundary && rockCellSpec(section, rockBoundary)" in view chrome = (B06 / "B06_Section_UI_Cross_Card_Chrome.ts").read_text(encoding="utf-8") assert "if (lineCell) {" in chrome + assert "buildRockBoundaryControl" not in chrome # 479 — 그림 없는 암 카드도 빈 칸 detail = (B06 / "B06_Section_UI_Cross_Detail_Pick.ts").read_text(encoding="utf-8") assert ".b06-chart__design-hit:not(.b06-chart__rock-hit)" in detail # 상세 표 강조에 안 섞임 css = (B06 / "B06_Section_UI_Style_Cross.css").read_text(encoding="utf-8")