/* ============================================================================= * spreadsheet_spill.ts * 긴 글 넘침(엑셀) — 칸보다 긴 글을 옆 빈 칸 위로 보임. 왼쪽 · 일반 맞춤은 오른쪽으로 · 오른쪽 맞춤은 * 왼쪽으로 · 가운데는 양쪽으로. 비지 않은 칸 · 병합 칸 · 판 끝(틀 고정 경계 · 그린 범위 밖)에서 멈춤. * 격자(`spreadsheet_grid.ts`)가 판 하나를 그릴 때 줄마다 칸 자리(`slots`)를 모아 부름 — 계산만 여기. * ========================================================================== */ /** 같은 판 · 같은 줄 · 병합 아닌 칸 하나(병합 칸 · 안 그린 칸은 넣지 않음 → 거기서 멈춤) */ export interface SpillSlot { w: number; empty: boolean; } /** 넘침 — 왼 · 오른쪽으로 더 쓸 폭(px) · 덮은 칸 열 범위 · 가운데 맞춤이 한쪽만 막혀 잘라 낼 폭 */ export interface Spill { left: number; right: number; from: number; to: number; /** 가운데 맞춤 — 글 상자는 양쪽 반씩 늘리고 막힌 쪽은 이만큼 잘라 냄(글은 칸 가운데 그대로) */ clipLeft: number; clipRight: number; } export type SpillAlign = "left" | "right" | "center"; /** 칸 c(폭 own)의 글 폭 need 가 넘치면 정렬 쪽 빈 칸으로 늘린 결과 · 안 넘치거나 못 늘리면 null */ export function spillOf( slots: Map, c: number, own: number, need: number, align: SpillAlign, ): Spill | null { if (need <= own) return null; let from = c; let to = c; const grow = (dir: -1 | 1, want: number): number => { let got = 0; for (let k = c + dir; got < want; k += dir) { const s = slots.get(k); if (!s || !s.empty) break; got += s.w; if (dir < 0) from = k; else to = k; } return got; }; if (align === "center") { const half = (need - own) / 2; const l = Math.min(grow(-1, half), half); const r = Math.min(grow(1, half), half); if (!l && !r) return null; return { left: half, right: half, from, to, clipLeft: half - l, clipRight: half - r }; } const got = grow(align === "right" ? -1 : 1, need - own); if (!got) return null; return align === "right" ? { left: got, right: 0, from, to, clipLeft: 0, clipRight: 0 } : { left: 0, right: got, from, to, clipLeft: 0, clipRight: 0 }; }