- logic/B06_Section_Label_Tiers 새로(labelTiers · labelWidthPx) · 이름은 지우지 않음 · 위 끝 3단 - 올린 측점 이름 = 맨 위 겹 · 바탕색 테두리 · 고른 측점 색 그대로 · 구조물 이름은 올린 측점 이름 위로 - B05 종단도 같은 함수(createLongitudinalProfile) - 시험: test_407_4_label_tiers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
32 lines
1.7 KiB
TypeScript
32 lines
1.7 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section/logic/B06_Section_Label_Tiers.ts
|
|
* 종단 측점 · 구조물 이름표 비켜 그리기(PLAN 407-4) — 가까운 측점(1.6 m 등)끼리 이름이 겹치면 위 단으로
|
|
* 올림 · 이름은 지우지 않음. 그리는 곳 = `B06_Section_UI_Longitudinal`(B05 종단도 같은 함수).
|
|
* 시험 `resources/tester/test_407_4_label_tiers.py`. 화면 · 세션 없음(순수).
|
|
* ========================================================================== */
|
|
|
|
/** 단 수 위 끝 — 넘으면 마지막 단에 겹쳐 둠(이름은 남김). */
|
|
export const MAX_LABEL_TIER = 3;
|
|
|
|
/** 글 폭 짐작(px) — 한글 · 한자 = 글자 크기 · 그 밖 = 0.6 배. */
|
|
export const labelWidthPx = (text: string, fontPx: number): number =>
|
|
[...text].reduce((sum, ch) => sum + (ch.charCodeAt(0) > 0x2e80 ? fontPx : fontPx * 0.6), 0);
|
|
|
|
/** 가운데 맞춤 이름표마다 단(0 = 제자리) — 왼쪽부터 겹치지 않는 가장 낮은 단 · `gapPx` = 글 사이 틈. */
|
|
export function labelTiers(
|
|
items: ReadonlyArray<{ x: number; width: number }>,
|
|
gapPx = 3,
|
|
): number[] {
|
|
const order = items.map((_, index) => index).sort((a, b) => items[a].x - items[b].x);
|
|
const rightEdge: number[] = []; // 단마다 마지막 이름표 오른쪽 끝
|
|
const tiers = items.map(() => 0);
|
|
for (const index of order) {
|
|
const { x, width } = items[index];
|
|
let tier = rightEdge.findIndex((edge) => edge + gapPx <= x - width / 2);
|
|
if (tier < 0) tier = Math.min(rightEdge.length, MAX_LABEL_TIER);
|
|
rightEdge[tier] = Math.max(rightEdge[tier] ?? -Infinity, x + width / 2);
|
|
tiers[index] = tier;
|
|
}
|
|
return tiers;
|
|
}
|