Merge remote-tracking branch 'origin/sub_laptop_2' into main_laptop_1
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
/* 490-1 임시 횡단 판정 — 진짜 `logic/B05_Profile_Profile_CrossPick.ts` 를 Node 로 돌려 냄.
|
||||
* 입력 argv[2] JSON {samples, cases: [{hit, chainageM, pointerY}]} · groundY = 100 - 표고 × 2 px.
|
||||
* 짝 = test_490_1_cross_pick.py. */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
||||
|
||||
const file = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"B05_Profile",
|
||||
"logic",
|
||||
"B05_Profile_Profile_CrossPick.ts",
|
||||
);
|
||||
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText;
|
||||
const box = { exports: {} };
|
||||
new Function("exports", "module", "require", js)(box.exports, box, require);
|
||||
const logic = box.exports;
|
||||
|
||||
const job = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
||||
const groundY = (elevation) => 100 - elevation * 2;
|
||||
const out = job.cases.map((c) => ({
|
||||
pick: logic.crossPickAt({ ...c, samples: job.samples, groundY }),
|
||||
ground: logic.groundElevationAt(job.samples, c.chainageM),
|
||||
}));
|
||||
process.stdout.write(JSON.stringify({ slack: logic.GROUND_SLACK_PX, out }));
|
||||
@@ -24,7 +24,7 @@ def test_추가_측점_선_점선_끌기_없음() -> None:
|
||||
|
||||
def test_누름_작은_창_우클릭_삭제() -> None:
|
||||
render = _read("B05_Profile/B05_Profile_UI_Profile_Render.ts")
|
||||
assert "groundPickAt(chartWrap, event" in render and "openCrossModal(" in render
|
||||
assert "crossPickFromEvent(chartWrap, event" in render and "openCrossModal(" in render
|
||||
assert "onRemovePlus: removePlusStation" in render
|
||||
menu = _read("B05_Profile/B05_Profile_UI_Profile_Structures.ts")
|
||||
assert '"측점 삭제", () => options.onRemovePlus?.(near.station.chainage_m)' in menu
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
"""490-1 B05 종단 — 임시 횡단이 열리는 자리 가까이 표시 = 누름과 같은 판정(`crossPickAt`).
|
||||
|
||||
판정 함수 = 옛 누름 판정(원지반선 위아래 8 px · 정수 m · 추가 측점 선 = 그 측점 · 측점선 · 단추 = null)과
|
||||
같은 값 · 허용 밖 null. TS 쪽 = `helper_490_1_cross_pick.cjs`(Node). 화면 조작은 ORCA 로 확인.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
ROOT = HERE.parents[1]
|
||||
|
||||
SAMPLES = [
|
||||
{"chainage_m": 0.0, "elevation_m": 10.0},
|
||||
{"chainage_m": 20.0, "elevation_m": 20.0},
|
||||
{"chainage_m": 40.0, "elevation_m": None}, # 빈 표고는 건너뜀
|
||||
{"chainage_m": 60.0, "elevation_m": 0.0},
|
||||
]
|
||||
|
||||
|
||||
def _ground_y(elevation: float) -> float:
|
||||
return 100 - elevation * 2
|
||||
|
||||
|
||||
def _old_pick(hit, chainage: float, pointer_y: float) -> int | None:
|
||||
"""옛 `groundPickAt` + 누름 처리(Render.ts 329~346) 그대로."""
|
||||
if hit == "other":
|
||||
return None
|
||||
if hit:
|
||||
return round(hit["plusM"])
|
||||
valid = [s for s in SAMPLES if s["elevation_m"] is not None]
|
||||
after = next((i for i, s in enumerate(valid) if s["chainage_m"] >= chainage), -1)
|
||||
if after <= 0:
|
||||
return None
|
||||
a, b = valid[after - 1], valid[after]
|
||||
t = (chainage - a["chainage_m"]) / (b["chainage_m"] - a["chainage_m"] or 1)
|
||||
ground = a["elevation_m"] + (b["elevation_m"] - a["elevation_m"]) * t
|
||||
return round(chainage) if abs(pointer_y - _ground_y(ground)) <= 8 else None
|
||||
|
||||
|
||||
CASES = [
|
||||
{"hit": None, "chainageM": 10.2, "pointerY": 69.8}, # 지반 y = 69.8 · 딱 위
|
||||
{"hit": None, "chainageM": 10.2, "pointerY": 77.8}, # 8 px 끝
|
||||
{"hit": None, "chainageM": 10.2, "pointerY": 78.3}, # 허용 밖
|
||||
{"hit": None, "chainageM": 10.2, "pointerY": 61.3}, # 위쪽 허용 밖
|
||||
{"hit": None, "chainageM": 50.0, "pointerY": 93.0}, # 빈 표고 건너 20→60 보간
|
||||
{"hit": None, "chainageM": 0.0, "pointerY": 80.0}, # 첫 표본 = null(옛 판정 그대로)
|
||||
{"hit": None, "chainageM": 70.0, "pointerY": 100.0}, # 끝 뒤
|
||||
{"hit": {"plusM": 33.6}, "chainageM": 10.0, "pointerY": 0.0}, # 추가 측점 선
|
||||
{"hit": "other", "chainageM": 10.4, "pointerY": 70.0}, # 측점선 · 단추
|
||||
]
|
||||
|
||||
|
||||
def _run() -> dict:
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
path = Path(folder) / "job.json"
|
||||
path.write_text(json.dumps({"samples": SAMPLES, "cases": CASES}), encoding="utf-8")
|
||||
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
||||
["node", str(HERE / "helper_490_1_cross_pick.cjs"), str(path)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def test_판정_함수_옛_누름과_같은_값() -> None:
|
||||
result = _run()
|
||||
assert result["slack"] == 8
|
||||
picks = [row["pick"] for row in result["out"]]
|
||||
assert picks == [_old_pick(c["hit"], c["chainageM"], c["pointerY"]) for c in CASES]
|
||||
assert picks == [10, 10, None, None, 50, None, None, 34, None]
|
||||
|
||||
|
||||
def test_누름_표시_같은_길() -> None:
|
||||
render = (ROOT / "B05_Profile/B05_Profile_UI_Profile_Render.ts").read_text(encoding="utf-8")
|
||||
assert render.count("crossPickFromEvent(chartWrap, event") == 2 # 누름 · 표시
|
||||
assert "mountCrossHint(chartWrap, body" in render and "blocked: ctx.toolActive" in render
|
||||
modal = (ROOT / "B05_Profile/B05_Profile_UI_Profile_CrossModal.ts").read_text(encoding="utf-8")
|
||||
assert "crossPickAt({" in modal and "event.buttons || options.blocked()" in modal
|
||||
assert 'scroller.addEventListener("scroll", hide' in modal
|
||||
Reference in New Issue
Block a user