Merge remote-tracking branch 'origin/dev' into sub_laptop_4 — Api(595 받기 꼴은 dev 쪽 · 임도 상자 중복 칸 하나로) · Style(597 · 599 절 둘 다)

This commit is contained in:
2026-10-30 14:49:26 +09:00
19 changed files with 676 additions and 248 deletions
+12 -3
View File
@@ -30,11 +30,13 @@ def test_d03_parts():
three = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_3D.ts")
css = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css")
draw = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Draw.ts")
draw_panel = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts")
tools = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts")
layers = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Layers.ts")
assert 'createElement("progress")' not in ts and "createProgressBar()" in ts
assert 'createElement("progress")' not in ts and "createProgressBar(" not in ts
assert "createProgressCircle({ size: 40, corner: true })" in ts # 596-3 받기 = 지도 귀퉁이 서클
# 562-3 — 날 단추 · 입력 · 고르기 없음 · 도구줄 · 제약 칸 · 연도 고름은 부품
for src in (ts, draw, tools, layers):
for src in (ts, draw, draw_panel, tools, layers):
for raw in ('createElement("button")', 'createElement("input")', 'createElement("select")'):
assert raw not in src, raw
# 568-3 — 도구줄 = 공용 지도 도구줄 · 아홉([?] · 되돌리기 · 다시하기 · 3D · 돌리기 둘 · 측점 · 기울기 · 재기)
@@ -76,7 +78,14 @@ def test_d03_parts():
("place", "false"),
], keys
assert ".d03-plan__layers" in css and ".d03-plan__legend" in css
assert "createSegment({" in draw and draw.count("createInputField(") == 2
# 599-1 — 방식 칸 = 마디 고름 2 × 2 · 길이 · 기울기 두 열 · 노드 판(598-4)과 같은 함수
assert "createSegment({" in draw_panel and draw_panel.count("createInputField(") == 2
assert "columns: 2" in draw_panel and "export function createDrawKindFields(" in draw_panel
assert ".d03-plan__draw-fields" in css and "createDrawKindFields(" in draw
assert '"길이·기울기 고정"' in draw_panel and '"둘 다"' not in draw_panel
assert "d03-plan__hint" not in draw # 칸 설명 글 걷음(요구 463-6)
# 599-4 — 연도 구역 마커 = 그리기 칸 단추(끌어 놓기) · 지도 도구줄에서 뺌
assert "YEAR_MARK_DRAG" in draw_panel and '"year-mark"' not in tools
props = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Props.ts") # 585-2 조각 연도 = 속성 판
assert "createSelectField(" in props and "createFloatPanel(" in props
assert 'createElement("button")' not in props and 'createElement("select")' not in props
-1
View File
@@ -55,7 +55,6 @@ def test_단추_목록_아이콘_차례():
"footprint",
"measure",
"station-snap",
"year-mark",
]
names = _icon_names()
assert all(icon in names for _, icon, _ in rows)
+101
View File
@@ -0,0 +1,101 @@
"""D03 다시 긋기(PLAN 599-3 · 요구 463-12) — TS `logic/D03_PlanAnalysis_Span.ts`.
두 꺾임점 사이를 새 점으로 바꿈 · 끝 두 점 그대로 · 뒤 갈래 차례 밈 · 속 점 갈래 = 막음 ·
마크 = 바뀐 구간 안 길이 비율 · 뒤는 길이 차 · 조각 id 그대로.
./venv/Scripts/python.exe -m pytest resources/tester/test_599_3_plan_span.py -q
"""
from __future__ import annotations
import json
import math
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
TS_FILE = ROOT / "D03_PlanAnalysis" / "logic" / "D03_PlanAnalysis_Span.ts"
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
_RUNNER = """
import { writeFileSync } from "node:fs";
import * as plan from "./D03_PlanAnalysis/logic/D03_PlanAnalysis_Plan.js";
import * as span from "./D03_PlanAnalysis/logic/D03_PlanAnalysis_Span.js";
const Y = 2026;
let n = plan.newLine(plan.emptyNetwork(), [0, 0]);
for (const p of [[100, 0], [200, 0], [300, 0]]) n = plan.appendVertex(n, 0, p);
n = plan.normalizePieces(n, Y);
n = plan.normalizePieces(plan.splitPiece(n, "main", 150), Y);
n = plan.normalizePieces(plan.splitPiece(n, "main", 250), Y);
n = plan.addBranch(n, 0, 2);
n = plan.normalizePieces(plan.appendVertex(n, 1, [200, 80]), Y);
const done = span.replaceSpan(n, 0, 1, 2, [[150, 50]]);
const wide = span.replaceSpan(n, 0, 0, 3, [[150, 50]]);
const bad = span.replaceSpan(n, 0, 2, 2, []);
const straight = span.replaceSpan(n, 0, 0, 2, []);
writeFileSync(process.argv[2], JSON.stringify({ before: n, done, wide, bad, straight }));
"""
def _run_ts(tmp_path: Path) -> dict:
out = tmp_path / "js"
subprocess.run( # noqa: S603 — 고정 실행 파일
[
"node",
str(TSC),
str(TS_FILE),
"--outDir",
str(out),
"--rootDir",
str(ROOT),
"--module",
"esnext",
"--target",
"es2022",
"--moduleResolution",
"bundler",
"--ignoreConfig",
],
cwd=str(ROOT),
check=True,
capture_output=True,
)
for emitted in out.rglob("*.js"):
emitted.write_text(
re.sub(r'(from\s+"\.\.?/[^"]+)"', r'\1.js"', emitted.read_text(encoding="utf-8")),
encoding="utf-8",
)
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
result = tmp_path / "out.json"
subprocess.run( # noqa: S603
["node", str(out / "runner.mjs"), str(result)],
cwd=str(ROOT),
check=True,
capture_output=True,
)
return json.loads(result.read_text(encoding="utf-8"))
def test_replace_span(tmp_path: Path) -> None:
r = _run_ts(tmp_path)
done = r["done"]
main = done["lines"][0]
assert main["vertices"] == [[0, 0], [100, 0], [150, 50], [200, 0], [300, 0]]
assert done["lines"][1]["branch"] == {"line": "main", "vertex": 3} # 뒤 갈래 한 칸 밈
new_span = 2 * math.hypot(50, 50)
pieces = [(p["id"], p["from_m"], p["to_m"]) for p in done["pieces"] if p["line"] == "main"]
before = [p["id"] for p in r["before"]["pieces"] if p["line"] == "main"]
assert [p[0] for p in pieces] == before # 조각 id 그대로
assert pieces[1][1] == round(100 + new_span / 2, 3) # 구간 속 마크 = 길이 비율
assert pieces[2][1] == round(250 - 200 + 100 + new_span, 3) # 뒤 마크 = 길이 차
assert pieces[2][2] == round(300 - 200 + 100 + new_span, 3)
def test_blocked_and_straight(tmp_path: Path) -> None:
r = _run_ts(tmp_path)
assert "갈래" in r["wide"] # 속 점(2)에 갈래
assert "고르세요" in r["bad"]
straight = r["straight"]["lines"][0]["vertices"]
assert straight == [[0, 0], [200, 0], [300, 0]] # 속 점 없이 곧게
assert r["straight"]["lines"][1]["branch"]["vertex"] == 1