- 겹 표(map_layer_list) = 그룹1 22 줄 표 차례 · title = 호버제목 · group(수계망도 · 도로 · 토지·건물 · 제한·재해) · pages · 숨김 표 걷음 · 도로300m 한 줄(「도로」 묶음 · 기능 남김) - 겹 줄(map_layer_bar) = 단독 넷 | 묶음 넷(여럿 고르기 메뉴 · 켠 수 배지 · 안 닫힘) | 그래픽 카드 · sync 가 단추 · 메뉴 줄 막힘 · 글을 맞춤(why = 페이지 까닭) - 도구 표(map_toolbar) = 그룹2 17 줄 title · mapToolGroups 가 글을 표로 맞춤 · 기본 단추 생성 · 지형 칩 막힘 「수치지형도/라이다 — 까닭」 - 띠 = 한 자료 · 겹 둘(GL fillEdge 거름 · 옛 캔버스 · 범례 줄 따로) · B05 띠 받기(D03 과 같은 서버 길 · bandCollection 공용) - D03 = 겹 줄 · 도구줄 한 행(createMapToolRow) · 222px 기댐 · 줄바꿈 CSS 걷음 · 측점 · 기울기 호버 덮어쓰기 걷음 · LAS 올리기 호버 걷음 - B05 = 세로 두 줄 → 한 행 · 호버 덮어쓰기 걷음(도구줄 · 1m 등고선 · 3D · 추천) · 겹 막힘 querySelector 덮어쓰기 → 공용 sync · 임도(업로드) 보임 + 「받은 임도 자료 없음」 막힘 · 스냅 간격 날 select → 고르기 메뉴(하나) - 켬끔 기억 열쇠 · 측점 · 기울기 처음 값 · 그래픽 카드 취향 열쇠 둘 그대로 - 낡은 시험 고침(568_1 · 622_2 · 624_2 · 624_3 · 553_4 · 625_draw · b05_routeedit_initial · 626_3 · 390_4 · 346_2 · 353_1 · 581_2 · 584_1 · 584_3) — 표 꼴 row() · 호버 글 = 표 · 한 행 · 고르기 메뉴로 바뀐 글을 잡음 · 새 지킴 634_2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
4.9 KiB
Python
120 lines
4.9 KiB
Python
"""PLAN 346-2 — 노선 편집 평면 발자국 그리기.
|
|
|
|
가짜 footprint → 그릴 선 수 · 색 갈래(도로 끝 · 깎기 · 쌓기 · 깎기 여부 없음) · 바뀌는 점은 앞뒤가
|
|
함께 가짐 · 편집기 그리기 · [발자국] 단추 · 밝음 · 어두움 색 토큰이 이어져 있음.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
B05 = ROOT / "B05_Profile"
|
|
TS_FILE = B05 / "logic" / "B05_Profile_Footprint_Strokes.ts"
|
|
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
|
|
_RUNNER = """
|
|
import { writeFileSync } from "node:fs";
|
|
import { footprintStrokes } from "./B05_Profile/logic/B05_Profile_Footprint_Strokes.js";
|
|
const line = (y) => [[0, y], [10, y], [20, y], [30, y], [40, y]];
|
|
const base = { roadLeft: line(3), roadRight: line(-3), slopeLeft: line(6), slopeRight: line(-7) };
|
|
const out = {
|
|
none: footprintStrokes(null),
|
|
plain: footprintStrokes(base),
|
|
flagged: footprintStrokes({
|
|
...base,
|
|
slopeLeftCut: [true, true, false, false, true],
|
|
slopeRightCut: [false, false, false, false, false],
|
|
}),
|
|
short: footprintStrokes({ roadLeft: [[0, 0]], roadRight: [], slopeLeft: [], slopeRight: [] }),
|
|
badFlags: footprintStrokes({ ...base, slopeLeftCut: [true] }),
|
|
};
|
|
writeFileSync(process.argv[2], JSON.stringify(out));
|
|
"""
|
|
|
|
|
|
def _run(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 _kinds(strokes: list[dict]) -> list[str]:
|
|
return [stroke["kind"] for stroke in strokes]
|
|
|
|
|
|
@pytest.mark.skipif(not TSC.is_file(), reason="프론트엔드 의존성 미설치")
|
|
def test_가짜_발자국_선_수와_색_갈래(tmp_path: Path) -> None:
|
|
out = _run(tmp_path)
|
|
assert out["none"] == [] and out["short"] == []
|
|
# 깎기 여부 없음 = 도로 끝 둘 + 비탈 끝 둘(한 색)
|
|
assert _kinds(out["plain"]) == ["road", "road", "slope", "slope"]
|
|
assert _kinds(out["badFlags"]) == ["road", "road", "slope", "slope"]
|
|
# 좌 = 깎기 → 쌓기(끝 한 점 깎기는 2점 미만이라 뺌) · 우 = 쌓기 한 줄
|
|
flagged = out["flagged"]
|
|
assert _kinds(flagged) == ["road", "road", "cut", "fill", "fill"]
|
|
assert flagged[2]["points"] == [[0, 6], [10, 6], [20, 6]] # 바뀌는 점(20)을 앞 조각도 가짐
|
|
assert flagged[3]["points"] == [[20, 6], [30, 6], [40, 6]]
|
|
assert flagged[4]["points"] == [[0, -7], [10, -7], [20, -7], [30, -7], [40, -7]]
|
|
|
|
|
|
def test_편집기가_발자국을_그리고_단추로_켬끔() -> None:
|
|
render = (B05 / "B05_Profile_UI_RouteEdit_Render.ts").read_text(encoding="utf-8")
|
|
assert "drawFootprint(context, toScreen, style, () => drawRouteEditScene(context, scene))" in (
|
|
render
|
|
)
|
|
# 등고선 뒤 · 노선 앞(노선이 발자국 위에 보임)
|
|
assert render.index("drawFootprint(context") < render.index("scene.expected,")
|
|
chrome = (B05 / "B05_Profile_UI_RouteEdit_Chrome.ts").read_text(encoding="utf-8")
|
|
# 634-2 — 단추 = 공용 표 기본 단추(`mapToolGroups`) · 켬끔 배선은 페이지
|
|
assert 'bindFootprintToggle(mapTools.button("footprint")!)' in chrome
|
|
footprint = (B05 / "B05_Profile_UI_RouteEdit_Footprint.ts").read_text(encoding="utf-8")
|
|
assert 'subscribe("footprint"' in footprint and "let shown = true;" in footprint
|
|
css = (B05 / "B05_Profile_UI_Style_RouteEdit_Footprint.css").read_text(encoding="utf-8")
|
|
for token in ("--b05-footprint-road", "--b05-footprint-cut", "--b05-footprint-fill"):
|
|
assert token in footprint
|
|
assert css.count(f"{token}:") == 3 # 밝음 · 어두움 · 시스템 어두움
|
|
# 381-1 — 절토 · 성토 테마 색 · 굵게(도로 끝 1.8 · 비탈 끝 2.6)
|
|
for color in ("#e11d48", "#ff6b6b", "#0d9488", "#2dd4bf"):
|
|
assert color in css
|
|
assert '"#e11d48", 2.6]' in footprint and '"#0d9488", 2.6]' in footprint
|
|
assert 'road: ["--b05-footprint-road", "#475569", 1.8]' in footprint
|