feat(B05): 초기노선 갈래 — 제약 못 지킨 구간 한 줄 · 상한 풂 안내 · 기울기 잴 지반 고름 칸
- 비교값 줄 아래 「제약 못 지킨 구간 N곳: No.9 16.4%(지형상 불가능) · …」(없으면 「모두 지킴」 · 5곳 넘으면 「외 N곳」 · 풍선에 전부와 사유) - relaxed 면 「지형상 14.0% 로 못 이어져 상한을 29.1% 로 풂」(경고 색) - 공통 제약 줄에 서버 grade_smooth_m 을 「기울기 잴 지반 5m 고름」으로 - 시험 — 글 만들기(test_b05_routeedit_initial) · 사본 왕복에서 목록 · 풂 · 고름 칸이 화면 줄에(test_b05_routeedit_initial_roundtrip) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015sCeZz8z8YnbovsKgn7HK7
This commit is contained in:
@@ -79,6 +79,36 @@ const plain = model.normalizeRouteInitial(
|
||||
over_limit: { grade: 1, radius: 0, stream: 1, total: 2 },
|
||||
offset_avg_m: 3.25,
|
||||
offset_max_m: 9.8,
|
||||
raw_max_grade_pct: 41.2,
|
||||
violations: [
|
||||
{
|
||||
kind: "grade",
|
||||
from_m: 180,
|
||||
to_m: 220,
|
||||
station: "No.9+0.0",
|
||||
value: 29.13,
|
||||
limit: 14,
|
||||
reason: "지형상 불가능 — 복도 안 어떤 길도 23.5% 이상 기울기를 지나야 이어짐",
|
||||
},
|
||||
{
|
||||
kind: "radius",
|
||||
from_m: 400,
|
||||
to_m: 412,
|
||||
station: "No.20+0.0",
|
||||
value: 9.5,
|
||||
limit: 12,
|
||||
reason: "곡선 둘 자리 모자람 — 앞뒤 직선이 짧아 최소 반지름 원호가 안 들어감",
|
||||
},
|
||||
{
|
||||
kind: "stream",
|
||||
from_m: 600,
|
||||
to_m: 700,
|
||||
station: "No.30+0.0",
|
||||
value: 32,
|
||||
limit: 100,
|
||||
reason: "계류 버퍼 안 — 건너기 · 기점 · 종점 봐줌을 넘음",
|
||||
},
|
||||
],
|
||||
},
|
||||
criteria: {
|
||||
max_grade_pct: 14,
|
||||
@@ -86,8 +116,11 @@ const plain = model.normalizeRouteInitial(
|
||||
stream_offset_m: 5,
|
||||
road_width_m: 4,
|
||||
corridor_m: 100,
|
||||
grade_smooth_m: 5,
|
||||
pending: ["max_grade_pct", "stream_offset_m"],
|
||||
},
|
||||
relaxed: true,
|
||||
terrain_bound_pct: 23.46,
|
||||
},
|
||||
"earthwork",
|
||||
null,
|
||||
@@ -132,6 +165,21 @@ const labels = {
|
||||
criteria: model.criteriaLine(plain.criteria, 0),
|
||||
criteriaConfirmed: model.criteriaLine({ ...plain.criteria, confirmed: true }, 0),
|
||||
criteriaFallback: model.criteriaLine(null, 15),
|
||||
violations: model.violationsLine(plain.metrics),
|
||||
violationsNone: model.violationsLine({ ...plain.metrics, violations: [] }),
|
||||
violationsOld: model.violationsLine({ ...plain.metrics, violations: null }),
|
||||
violationsMany: model.violationsLine({
|
||||
...plain.metrics,
|
||||
violations: Array.from({ length: 7 }, (_, i) => ({
|
||||
kind: "grade",
|
||||
station: `No.${i}+0.0`,
|
||||
value: 15 + i,
|
||||
limit: 14,
|
||||
reason: "곡선 맞춤 뒤 지반 차 — 측점 사이 지반이 상한을 넘음",
|
||||
})),
|
||||
}),
|
||||
relaxed: model.relaxedLine(plain),
|
||||
notRelaxed: model.relaxedLine(planShape),
|
||||
pendingEmpty: model.normalizeRouteInitial(
|
||||
{
|
||||
nodes: [
|
||||
|
||||
@@ -72,6 +72,9 @@ const out = jobs.map((job) => {
|
||||
},
|
||||
label: model.choiceLabel(choice.logic, choice.weights),
|
||||
metrics: model.metricsLine(choice.metrics),
|
||||
violations: model.violationsLine(choice.metrics),
|
||||
relaxed: model.relaxedLine(choice),
|
||||
criteria: model.criteriaLine(choice.criteria, 0),
|
||||
};
|
||||
}
|
||||
const flat = edits.flattenServerPlan(job.plan.nodes ?? [], job.plan.curves ?? []);
|
||||
|
||||
@@ -81,15 +81,32 @@ def test_비교값과_기준값_글(results):
|
||||
)
|
||||
assert "로직을 누르면" in labels["metricsNone"]
|
||||
assert labels["criteria"] == (
|
||||
"공통 제약 종단기울기 ≤ 14.0% · 최소 R 12m · 계류 이격 ≥ 5.0m (후보 · 사용자 확정 대기)"
|
||||
"공통 제약 종단기울기 ≤ 14.0% · 최소 R 12m · 계류 이격 ≥ 5.0m · 기울기 잴 지반 5m 고름"
|
||||
" (후보 · 사용자 확정 대기)"
|
||||
)
|
||||
assert labels["criteriaConfirmed"].endswith("5.0m")
|
||||
assert labels["criteriaConfirmed"].endswith("지반 5m 고름")
|
||||
assert labels["pendingEmpty"] is True
|
||||
assert labels["criteriaFallback"] == (
|
||||
"공통 제약 종단기울기 ≤ — · 최소 R 15m · 계류 이격 ≥ — (후보 · 사용자 확정 대기)"
|
||||
)
|
||||
|
||||
|
||||
def test_제약_못_지킨_구간과_상한_풂(results):
|
||||
labels = results["labels"]
|
||||
assert labels["violations"] == (
|
||||
"제약 못 지킨 구간 3곳: No.9+0.0 29.1%(지형상 불가능)"
|
||||
" · No.20+0.0 R 9.5m(곡선 둘 자리 모자람) · No.30+0.0 계류 32.0m(계류 버퍼 안)"
|
||||
)
|
||||
assert labels["violationsNone"] == "제약 못 지킨 구간 없음 — 모두 지킴"
|
||||
assert labels["violationsOld"] == "" # 옛 답(목록 칸 없음)은 줄을 숨긴다
|
||||
assert labels["violationsMany"].startswith(
|
||||
"제약 못 지킨 구간 7곳: No.0+0.0 15.0%(곡선 맞춤 뒤 지반 차)"
|
||||
)
|
||||
assert labels["violationsMany"].endswith(" 외 2곳")
|
||||
assert labels["relaxed"] == "지형상 14.0% 로 못 이어져 상한을 23.5% 로 풂"
|
||||
assert labels["notRelaxed"] == ""
|
||||
|
||||
|
||||
def test_서버가_적어_둔_현재_초기값(results):
|
||||
bases = results["bases"]
|
||||
assert bases["follow"] == {"logic": "follow", "weights": None}
|
||||
|
||||
@@ -192,6 +192,28 @@ def test_비교값이_응답에_다_있음(rounds):
|
||||
assert "—" not in item["applied"]["metrics"], item["applied"]["metrics"]
|
||||
|
||||
|
||||
def test_제약_못_지킨_구간이_목록과_화면_줄에(rounds):
|
||||
for item in rounds["rounds"]:
|
||||
answer, applied = item["answer"], item["applied"]
|
||||
violations = answer["metrics"]["violations"]
|
||||
assert answer["metrics"]["over_limit"]["total"] == len(violations)
|
||||
for entry in violations:
|
||||
assert entry["kind"] in ("grade", "radius", "stream")
|
||||
assert entry["station"].startswith("No.") and entry["reason"]
|
||||
assert isinstance(entry["value"], (int, float))
|
||||
head = (
|
||||
f"제약 못 지킨 구간 {len(violations)}곳: " if violations else "제약 못 지킨 구간 없음"
|
||||
)
|
||||
assert applied["violations"].startswith(head), applied["violations"]
|
||||
if answer.get("relaxed"):
|
||||
bound = f"{answer['terrain_bound_pct']:.1f}%"
|
||||
assert applied["relaxed"].endswith(f"상한을 {bound} 로 풂"), applied["relaxed"]
|
||||
else:
|
||||
assert applied["relaxed"] == ""
|
||||
smooth = answer["criteria"]["grade_smooth_m"]
|
||||
assert f"기울기 잴 지반 {smooth:.0f}m 고름" in applied["criteria"]
|
||||
|
||||
|
||||
def test_확인하면_서버가_초기값을_적고_다시_열면_그대로(rounds, api):
|
||||
assert len(api.recomputed) == len(LOGICS)
|
||||
for item in rounds["rounds"]:
|
||||
|
||||
Reference in New Issue
Block a user