- 새 logic/B05_Profile_RouteEdit_Visible(덮인 높이 · 보이는 영역 맞춤 — 덮인 높이 0 이면 computeRouteView 와 같음) - 새 _RouteEdit_Cover(칸 · 판 ResizeObserver → --b05-routeedit-cover) · fitCanvas 가 편집 평면 칸만 붙임 - 보던 화면 되살림도 판 밑 노드는 안 보인 것으로 - 시험: test_345_5b_plan_visible_area 2 · 관련 68 통과 · typecheck 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
61 lines
2.5 KiB
TypeScript
61 lines
2.5 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_RouteEdit_Visible.ts
|
|
* 노선 편집 평면의 **보이는 영역**(345-5 덧) — 아래 뜬 종단 판이 덮은 높이 · 그 위 영역에 노선 맞춤.
|
|
*
|
|
* 덮인 높이 = 칸 아래 끝 − 판 위 끝(0 ~ 칸 높이) · 판 없음 · 접힘(높이 0)이면 0.
|
|
* 맞춤 = B04 `computeRouteView` 와 같은 공식 · 세로 몫만 보이는 높이 비율로 줄이고 가운데를 그 영역
|
|
* 가운데로 · 덮인 높이 0 이면 `computeRouteView` 와 같은 값.
|
|
* ========================================================================== */
|
|
|
|
export interface VerticalSpan {
|
|
top: number;
|
|
bottom: number;
|
|
}
|
|
|
|
/** 칸(`wrap`) 아래쪽을 판(`panel`)이 덮은 높이(px). */
|
|
export function coveredBottomPx(wrap: VerticalSpan, panel: VerticalSpan | null): number {
|
|
if (!panel || panel.bottom - panel.top <= 1) return 0; // 없음 · 접힘(높이 0 + 테두리)
|
|
const height = Math.max(wrap.bottom - wrap.top, 0);
|
|
return Math.min(Math.max(wrap.bottom - panel.top, 0), height);
|
|
}
|
|
|
|
export interface FitInput {
|
|
/** 도엽 크기(m) — `meta.width_meters` · `height_meters`. */
|
|
mapWidthM: number;
|
|
mapHeightM: number;
|
|
/** 배율 1 일 때 도엽이 놓이는 칸 안 사각형(`computeMapRect`). */
|
|
mapRect: { x: number; y: number; width: number; height: number };
|
|
/** 칸 크기(px) · 아래 덮인 높이(px). */
|
|
width: number;
|
|
height: number;
|
|
coveredPx: number;
|
|
/** 노선 가운데 · 크기(m) — 도엽 왼쪽 아래 기준 · 여유 포함. */
|
|
centerXM: number;
|
|
centerYM: number;
|
|
wantWidthM: number;
|
|
wantHeightM: number;
|
|
}
|
|
|
|
/** 노선(+여유)이 보이는 영역 가운데 들도록 배율 · 이동량. */
|
|
export function fitRouteToVisible(input: FitInput): {
|
|
scale: number;
|
|
offsetX: number;
|
|
offsetY: number;
|
|
} {
|
|
const { mapRect, width, height } = input;
|
|
const ratio = height > 0 ? Math.max(height - input.coveredPx, 1) / height : 1;
|
|
const fit = Math.min(
|
|
input.mapWidthM / input.wantWidthM,
|
|
(input.mapHeightM / input.wantHeightM) * ratio,
|
|
);
|
|
// 하한 1(빈 여백 없음 · computeRouteView) — 덮인 만큼 내려 줌(노선이 판 밑에 안 숨게).
|
|
const scale = Math.max(fit, Math.min(1, ratio));
|
|
const baseX = mapRect.x + (input.centerXM / input.mapWidthM) * mapRect.width;
|
|
const baseY = mapRect.y + (1 - input.centerYM / input.mapHeightM) * mapRect.height;
|
|
return {
|
|
scale,
|
|
offsetX: -(baseX - width / 2) * scale,
|
|
offsetY: -(baseY - height / 2) * scale - input.coveredPx / 2,
|
|
};
|
|
}
|