- 새 logic/D03_PlanAnalysis_Select.ts — 토막 판정 · 끌어 고르기 토막 · 토막 지움(가운데 = 구역 둘 · 뒤쪽 새 구역) · 전체 지움 · 지점 ↔ 노드 같은 자리 - 새 UI_Select.ts — 고름 상태(다시 누름 = 한 단 넓힘 · 같은 해 = 왼쪽 해 줄로만) - UI_Edit.ts — 우클릭 즉시 지움 걷음 · 노드 끌면 지점 같이 · 지점 12 px 안 붙음 · 지움 창구(dropNode · dropRegion · dropAll · splitAt) - UI_Props.ts — 판 = 노드 · 선 · 구역 · 전체 · 구간 · 마커 몸 · [방향 바꾸기] · [갈래 시점 지정] 걷음 - 새 UI_Menu.ts — 우클릭 메뉴(Points 에서 옮김 · 노드 지움 · 지점으로 · 연도 구역 나눔) - UI_Points.ts — 핀 놓을 때 노드 12 px 안 붙음(nodeNear) - key_registry plan 줄 · 시험 585_2 갈래 시점 지정 줄 지움(요구 463-15 걷음) · 553_4 메뉴 자리 · 새 시험 598_1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
119 lines
5.7 KiB
Python
119 lines
5.7 KiB
Python
"""585-2 · 585-3 예정노선 끌어 고르기 판정 · 여러 구간 연도 · 연도 구역 마커 나눔 · 합침
|
|
(요구 445 A4~A6 · 446) — TS `logic/D03_PlanAnalysis_Plan.ts`. 갈래 시점 지정은 걷음(요구 463-15 · PLAN 598-3).
|
|
|
|
창(오른쪽으로) = 구간 꺾은선 점이 모두 상자 안 · 걸침(왼쪽으로) = 점 하나라도 안 또는 토막이 상자 변과 만남 ·
|
|
돌린 화면도 보이는 px 로 셈 · 여러 구간 연도 한 번에.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_585_2_plan_select.py -q
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
TS_FILE = ROOT / "D03_PlanAnalysis" / "logic" / "D03_PlanAnalysis_Plan.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";
|
|
const Y = 2026;
|
|
// 본선 (0,0)→(100,0)→(100,100) · 마크 50 · 150 → p1 [0,50] p2 [50,150] p3 [150,200] · 둘째 선 (200,0)→(300,0)
|
|
let n = plan.newLine(plan.emptyNetwork(), [0, 0]);
|
|
n = plan.appendVertex(plan.appendVertex(n, 0, [100, 0]), 0, [100, 100]);
|
|
n = plan.normalizePieces(n, Y);
|
|
n = plan.normalizePieces(plan.splitPiece(n, "main", 50), Y);
|
|
n = plan.normalizePieces(plan.splitPiece(n, "main", 150), Y);
|
|
n = plan.normalizePieces(plan.appendVertex(plan.newLine(n, [200, 0]), 1, [300, 0]), Y);
|
|
const view = ([x, y]) => [x, -y]; // 화면 y 아래로
|
|
const turned = ([x, y]) => [-y, -x]; // 90° 돌린 화면
|
|
const box = (x0, y0, x1, y1) => ({ x0, y0, x1, y1 });
|
|
const pick = (b, crossing, to = view) => plan.piecesInBox(n, to, b, crossing);
|
|
writeFileSync(process.argv[2], JSON.stringify({
|
|
pieces: n.pieces.map((p) => [p.id, p.line, p.from_m, p.to_m]),
|
|
window: pick(box(-10, -10, 60, 10), false),
|
|
crossing: pick(box(60, 10, -10, -10), true),
|
|
through: [pick(box(70, -5, 80, 5), true), pick(box(70, -5, 80, 5), false)],
|
|
wholeLine: pick(box(190, -5, 310, 5), false),
|
|
partLine: [pick(box(190, -5, 250, 5), false), pick(box(250, 5, 190, -5), true)],
|
|
all: pick(box(-10, -110, 310, 10), false),
|
|
rotated: pick(box(-110, -110, -40, -90), false, turned),
|
|
years: plan.setPiecesYear(n, ["p1", "p3"], 2028).pieces.map((p) => [p.id, p.year]),
|
|
...marks(),
|
|
}));
|
|
|
|
/** 585-3 — 마커 = 구역 나누기만(해 그대로) · 지움 = 앞 구간이 먹음 · 갈래 처음 해. */
|
|
function marks() {
|
|
const span = (net) => net.pieces.filter((p) => p.line === "main").map((p) => [p.id, p.from_m, p.to_m, p.year]);
|
|
let m = plan.newLine(plan.emptyNetwork(), [0, 0]);
|
|
m = plan.normalizePieces(plan.appendVertex(m, 0, [100, 0]), Y);
|
|
const cut = plan.normalizePieces(plan.splitPiece(m, "main", 40), Y);
|
|
const edge = plan.splitPiece(cut, "main", 40.5); // 다른 마커 1 m 안 = 그대로
|
|
const yeared = plan.setPiecesYear(cut, ["p2"], 2028);
|
|
const merged = plan.normalizePieces(plan.removeMark(yeared, "main", 40, 1e-3), Y);
|
|
// 갈래 처음 해 = 붙은 자리(60 m · p2 = 2028) 구간 해
|
|
let b = plan.normalizePieces(plan.insertVertex(yeared, 0, 0, [60, 0]), Y);
|
|
b = plan.normalizePieces(plan.appendVertex(plan.addBranch(b, 0, 1), 1, [60, 50]), Y);
|
|
return {
|
|
cut: span(cut), edgeSame: edge === cut, merged: span(merged),
|
|
branchYear: b.pieces.filter((p) => p.line !== "main").map((p) => p.year),
|
|
};
|
|
}
|
|
"""
|
|
|
|
|
|
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,
|
|
) # fmt: skip
|
|
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_창_걸침_판정(tmp_path: Path) -> None:
|
|
got = _run_ts(tmp_path)
|
|
ids = {p[0]: p[1:] for p in got["pieces"]}
|
|
assert ids["p1"] == ["main", 0, 50] and ids["p2"] == ["main", 50, 150]
|
|
other = next(k for k, v in ids.items() if v[0] != "main")
|
|
assert got["window"] == ["p1"] # p2 는 (100, 100) 쪽이 상자 밖
|
|
assert got["crossing"] == ["p1", "p2"] # 왼쪽으로 = p2 앞 끝이 상자 안
|
|
assert got["through"] == [["p2"], []] # 점 없이 토막만 지남 = 걸침만
|
|
assert got["wholeLine"] == [other] # 마커 없는 선 = 구간 하나 = 선 통째
|
|
assert got["partLine"] == [[], [other]] # 선 반만 = 창 없음 · 걸침 있음
|
|
assert sorted(got["all"]) == sorted(ids)
|
|
assert got["rotated"] == ["p3"] # 돌린 화면 = 보이는 px 로 셈
|
|
|
|
|
|
def test_여러_구간_연도(tmp_path: Path) -> None:
|
|
got = _run_ts(tmp_path)
|
|
years = dict(got["years"])
|
|
assert years["p1"] == years["p3"] == 2028 and years["p2"] == 2026
|
|
|
|
|
|
def test_마커_구역_나눔_합침_갈래_해(tmp_path: Path) -> None:
|
|
got = _run_ts(tmp_path)
|
|
assert got["cut"] == [["p1", 0, 40, 2026], ["p2", 40, 100, 2026]] # 구역만 나눔 · 해 그대로
|
|
assert got["edgeSame"] # 다른 마커 1 m 안 = 안 놓음
|
|
assert got["merged"] == [["p1", 0, 100, 2026]] # 지움 = 앞 구간 해 · id 가 먹음
|
|
assert got["branchYear"] == [2028] # 갈래 처음 해 = 붙은 자리 구간 해
|