fix(B06): 356-1 양측절토 맹암거 — 낮은 쪽 늘인 노면이 원지반과 만나는 점(tilt_reach)에서 끊음

- reach 너머 설계선 점 거름 · 꼬리가 reach 에서 수직으로 옵셋선까지 닫음
- 시험 test_288 · helper_288 에 양측절토 + reach 형상 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
This commit is contained in:
2026-10-16 20:30:02 +09:00
co-authored by Claude Opus 5.5
parent ec02165954
commit 789922cedb
3 changed files with 35 additions and 1 deletions
@@ -5,6 +5,7 @@
* · 노면 = 설계 노면 — 서버 · TS 짝이 암거 경사로 지대 낮은 쪽 한 경사로 기울여 냄 · 측구 없음.
* · 옵셋선 = 노면을 암거 높이만큼 아래로 같은 각도.
* · 성토 쪽 = 옵셋선을 성토 사면과 만날 때까지 · 절토 쪽 = 절토 사면을 아래로 늘여 옵셋선과 만남.
* · 양측절토(낮은 쪽 늘인 노면 `tilt_reach`) = 늘인 노면이 원지반과 만나는 점에서 수직으로 끊음(356-1).
* · 노면 · 양쪽 선 · 옵셋선이 둘러싼 안 = 돌 무늬(306-1) — 켜마다 반 칸 엇갈리고 이웃과 겹친 돌 ·
* 기울기를 번갈아 서로 교차 · 그림 전용(누름 안 받음 · CSS `.b06-chart__underdrain`).
* 높이가 비면 그리지 않음(수치를 지어내지 않음).
@@ -93,7 +94,12 @@ export function underdrainOutline(
const span = left.offset_m - right.offset_m;
if (!(Math.abs(span) > 1e-9)) return null;
const grade = (left.elevation_m - right.elevation_m) / span;
const line = design.design_line ?? [];
// 양측절토(356-1) — 낮은 쪽 늘인 노면이 원지반과 만나는 점(tilt_reach)에서 끊음 · 너머 점 거름 → 꼬리가 수직으로 닫음
const reach = design.tilt_reach;
const outward = reach?.side === "left" ? 1 : -1;
const line = (design.design_line ?? []).filter(
(p) => !reach || (p.offset_m - reach.offset_m) * outward <= 1e-6,
);
const leftPath = sidePath(left, 1, line, grade, heightM);
const rightPath = sidePath(right, -1, line, grade, heightM);
return [
@@ -65,6 +65,17 @@ const design = {
{ offset_m: -7, elevation_m: 97.2 },
],
};
// 356-1 양측절토 — 우(낮은 쪽) 노면 끝도 절토 → 노면을 늘여 원지반과 만난 점(tilt_reach) · 그 너머 원지반 오름
const bothCut = {
...design,
design_line: [
...design.design_line.slice(0, 3),
{ offset_m: -4, elevation_m: 99.8 },
{ offset_m: -4.5, elevation_m: 99.775 },
{ offset_m: -6, elevation_m: 100.5 },
],
tilt_reach: { side: "right", offset_m: -4.5, elevation_m: 99.775 },
};
const svg = new Node("g");
const group = draw.appendUnderdrainOverlay(
svg,
@@ -96,6 +107,7 @@ process.stdout.write(
attached,
sections: sections.map((s) => s.underdrain ?? null),
outline: draw.underdrainOutline(design, 1),
bothCut: draw.underdrainOutline(bothCut, 1),
noHeight: draw.underdrainOutline(design, null),
drew: !!group,
bodyId: body?.attrs["data-structure-id"] ?? null,
@@ -131,6 +131,22 @@ def test_형상_옵셋선_성토_절토와_만남(ts):
assert ts["drew"] is True and ts["stones"] > 0
def test_양측절토_늘인_노면_원지반_교점에서_끊음(ts):
"""356-1 — 낮은 쪽 노면 끝도 절토(tilt_reach) = reach 에서 수직으로 끊음 · 너머 원지반 따라가지 않음."""
outline = [(p["offset"], p["elevation"]) for p in ts["bothCut"]]
expected = [
(3, 100.15),
(-3, 99.85),
(-4, 99.8),
(-4.5, 99.775), # 늘인 노면 ∩ 원지반(tilt_reach)
(-4.5, 98.775), # reach 아래 옵셋선
(1.846154, 99.092308),
]
assert len(outline) == len(expected)
for got, want in zip(outline, expected):
assert got == pytest.approx(want, abs=1e-5)
def test_횡단도_B07_이_맹암거를_그림():
view = (ROOT / "B06_Section" / "B06_Section_UI_Cross_View.ts").read_text(encoding="utf-8")
assert (