Files
Aislo/B05_Profile/logic/B05_Profile_RouteEdit_Fit.ts
T
eomsangdonandClaude Opus 5.5 031b5be1d4 feat(B05 노선편집): 349-0 처음 그림 · [맞춤] = 등고 영역 맞춤 · 뜬 종단 판은 덮이기만 — 판이 없다고 보고 칸 전체 가운데에 등고선 범위 ∪ 노선을 85% 몫(둘레 여유) · 판 끌기 · 접기에 평면 자리 · 글판 · 잰 값 창 · 곡선 바 그대로
- 새 logic/B05_Profile_RouteEdit_Fit(unionArea · fitAreaToView) — 옛 _Visible 자리
- _RouteEdit_Cover 지움 · View · CurveBar · CSS 의 덮인 높이 따라가기 걷음(345-5 덧 · 덧 2)
- 글판 · 도움말 접기(덧 3)는 그대로
- 시험: test_345_5b_plan_visible_area 3 고쳐 씀 · test_345_5c 자리 줄 고침 · 관련 86 통과 · typecheck 0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
2026-10-16 13:55:35 +09:00

69 lines
2.6 KiB
TypeScript

/* =============================================================================
* B05_Profile_RouteEdit_Fit.ts
* 노선 편집 평면의 **처음 그림 · [맞춤]**(349-0) — 등고 영역(등고선 범위 ∪ 노선)을 칸 가운데에
* 알맞게(꽉 차지 않게 · 둘레 여유) 넣는 배율 · 이동량.
*
* 뜬 종단 판은 없다고 보고 칸 전체 기준 — 판은 위에 덮이기만 한다(다시 맞추지 않음).
* 영역은 도엽 정규화 좌표(0~1 · y 는 위가 0 · `PreparedFeature` 와 같은 꼴).
* ========================================================================== */
/** 칸에서 등고 영역이 차지하는 몫 — 나머지가 둘레 여유. */
export const AREA_FILL = 0.85;
export interface NormalizedArea {
minX: number;
minY: number;
maxX: number;
maxY: number;
}
export interface FitInput {
/** 배율 1 일 때 도엽이 놓이는 칸 안 사각형(`computeMapRect`). */
mapRect: { x: number; y: number; width: number; height: number };
/** 칸 크기(px). */
width: number;
height: number;
area: NormalizedArea;
/** 배율 하한 · 상한(지도 조작과 같은 값). */
minScale: number;
maxScale: number;
}
/** 영역들을 하나로 — 빈 목록이면 null. */
export function unionArea(areas: ReadonlyArray<NormalizedArea | null>): NormalizedArea | null {
let union: NormalizedArea | null = null;
// 등고선 가닥이 수만 개일 수 있음 — 펼쳐 넘기지 않고 하나씩.
for (const area of areas) {
if (!area) continue;
union = union
? {
minX: Math.min(union.minX, area.minX),
minY: Math.min(union.minY, area.minY),
maxX: Math.max(union.maxX, area.maxX),
maxY: Math.max(union.maxY, area.maxY),
}
: { minX: area.minX, minY: area.minY, maxX: area.maxX, maxY: area.maxY };
}
return union;
}
/** 영역이 칸 가운데 `AREA_FILL` 몫으로 들도록 배율 · 이동량. */
export function fitAreaToView(input: FitInput): {
scale: number;
offsetX: number;
offsetY: number;
} {
const { mapRect, width, height, area } = input;
const areaWidth = Math.max((area.maxX - area.minX) * mapRect.width, 1e-6);
const areaHeight = Math.max((area.maxY - area.minY) * mapRect.height, 1e-6);
const fit = AREA_FILL * Math.min(width / areaWidth, height / areaHeight);
const scale = Math.min(input.maxScale, Math.max(input.minScale, fit));
const baseX = mapRect.x + ((area.minX + area.maxX) / 2) * mapRect.width;
const baseY = mapRect.y + ((area.minY + area.maxY) / 2) * mapRect.height;
return {
scale,
offsetX: -(baseX - width / 2) * scale,
offsetY: -(baseY - height / 2) * scale,
};
}