Files
Aislo/resources/tester/helper_281_1_drainage_zoom_range.cjs
T

131 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 281-1 배수유역 지도 줌아웃 한계 헬퍼 — `B05_Profile_UI_Drainage_ZoomRange.ts` 를
* 그 자리에서 트랜스파일해 Node 로 돌리고 값을 JSON 으로 낸다.
* `.cjs` 까닭은 `helper_b05_curve_radius_floor.cjs` 와 같다.
*/
const fs = require("fs");
const path = require("path");
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
const ROOT = path.join(__dirname, "..", "..");
const cache = new Map();
/** TS 한 장을 CommonJS 로 옮겨 실행한다. `@ui/` 는 색 함수만 쓰므로 빈 값으로 갈음한다. */
function loadTs(file) {
const full = path.resolve(file);
if (cache.has(full)) return cache.get(full);
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = { exports: {} };
cache.set(full, box.exports);
const localRequire = (request) => {
if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`));
if (request.startsWith("@ui/")) return { themeColor: () => "" };
return require(request);
};
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
cache.set(full, box.exports);
return box.exports;
}
const zoom = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Drainage_ZoomRange.ts"));
const render = loadTs(path.join(ROOT, "B04_PreProcess", "B04_PreProcess_UI_MapRender.ts"));
const { computeZoomOutLimit, clampWheelScale, ZOOM_OUT_ROUTE_FACTOR, ZOOM_OUT_FALLBACK_SCALE } =
zoom;
// 도엽 4000 × 3000 m — 경위도는 1 도 = 1000 m 로 단순하게 둔다.
const META = {
x_min: 0,
x_max: 4000,
y_min: 0,
y_max: 3000,
width_meters: 4000,
height_meters: 3000,
lon_min: 127,
lon_max: 131,
lat_min: 36,
lat_max: 39,
};
const W = 800;
const H = 600; // 도엽과 같은 비율 — 배율 1 = 화면 폭 4000 m
const toLonLat = (x, y) => [127 + x / 1000, 36 + y / 1000];
/** 배율 s 에서 화면 폭에 드는 실폭(m). */
const viewWidthM = (s) => 4000 / s;
// 노선 1000 × 500 m (가운데 2000, 1500) — 2 배 = 2000 × 1000 m.
const ROUTE = [
{ x: 1500, y: 1250 },
{ x: 2500, y: 1750 },
];
const routeOnly = computeZoomOutLimit(META, ROUTE, [], W, H);
// 유역이 노선 2 배 범위 밖(서쪽 400 m 까지)으로 나감 → 폭 2600 m.
const basinOut = computeZoomOutLimit(
META,
ROUTE,
[{ polygon_lonlat: [toLonLat(400, 1500), toLonLat(1500, 1400), toLonLat(1500, 1600)] }],
W,
H,
);
// 유역이 2 배 범위 안 → 한계 그대로.
const basinIn = computeZoomOutLimit(
META,
ROUTE,
[{ polygon_lonlat: [toLonLat(1800, 1400), toLonLat(2200, 1400), toLonLat(2000, 1600)] }],
W,
H,
);
// 노선이 도엽만큼 길면 2 배가 도엽보다 큼 → 도엽 전체(배율 1)에서 멈춤.
const wide = computeZoomOutLimit(
META,
[
{ x: 100, y: 100 },
{ x: 3900, y: 2900 },
],
[],
W,
H,
);
// 짧은 노선(100 m) — 2 배(200 m)가 처음 보기(노선 + 200 m 여유 양쪽)보다 좁음 → 처음 보기에 맞춤.
const SHORT = [
{ x: 1950, y: 1500 },
{ x: 2050, y: 1500 },
];
const short = computeZoomOutLimit(META, SHORT, [], W, H);
const shortInitial = render.computeRouteView(
META,
{ x_min: 1950, x_max: 2050, y_min: 1500, y_max: 1500 },
W,
H,
).scale;
// 휠 — 밀기(×0.87)를 계속하면 한계에서 멈춤 · 당기기(×1.15)는 상한에서 멈춤.
let scale = 3;
for (let i = 0; i < 40; i += 1) scale = clampWheelScale(scale * 0.87, scale, routeOnly, 200);
const wheelOutStop = scale;
for (let i = 0; i < 80; i += 1) scale = clampWheelScale(scale * 1.15, scale, routeOnly, 200);
const wheelInStop = scale;
// 이미 한계 밖(더 멀리)에 있으면 밀어도 더 안 빠지고 튀지도 않음.
const outside = clampWheelScale(1.2 * 0.87, 1.2, routeOnly, 200);
process.stdout.write(
JSON.stringify({
factor: ZOOM_OUT_ROUTE_FACTOR,
fallbackConst: ZOOM_OUT_FALLBACK_SCALE,
routeOnly,
routeOnlyViewM: viewWidthM(routeOnly),
basinOutViewM: viewWidthM(basinOut),
basinIn,
noRoute: computeZoomOutLimit(META, [], [], W, H),
noMeta: computeZoomOutLimit(null, ROUTE, [], W, H),
wide,
short,
shortInitial,
wheelOutStop,
wheelInStop,
outside,
}),
);