- 새 logic/D03_PlanAnalysis_Span.ts — replaceSpan · respanBlocked(속 점 갈래 막음 · 뒤 갈래 차례 밈 · 마크 길이 비율 · 조각 id 그대로) - draw.respan(lineId, from, to) — 598 판 [다시 긋기]가 부름 · 속 구간 흐리게(paintNetwork dim) · Enter = 취소 · Backspace = 점 빼기 - 긋기 끝 꼬리가 다른 구역 꺾임점 = 뒤집어 그 점의 갈래(No.0 = 닿은 점) - 새 시험 599_3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
3.8 KiB
Python
102 lines
3.8 KiB
Python
"""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
|