- 토량 분배(전체 맞춤) 중에는 유토곡선 ↔ 종단 스크롤 동기화를 끊음 — 0 으로 되돌던 것 없앰 - 횡단 카드는 사면 교차점 · 배수관 세트 폭으로 줌아웃하지 않음 · 밖은 가운데 버튼 팬 · ⤢ 는 보기값으로 - 줌인해도 중심 십자선 길이 그대로(도형 레이어 배율을 십자선 중심에서 되돌림) - 횡단 보기 반폭 기본 12 → 10 m(계산 반폭과 따로) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019EMfGERKUg5nMMNQsyVZ2j
115 lines
4.0 KiB
TypeScript
115 lines
4.0 KiB
TypeScript
import type { CrossSection, SectionSample } from "./B06_Section_Api_Fetch";
|
|
import { CROSS_HEIGHT, CROSS_PAD, validElevation } from "./B06_Section_UI_Section_Common";
|
|
|
|
const AREA_OVERLAY_HEADROOM_PX = 58;
|
|
|
|
export interface CrossPlotMetrics {
|
|
sourceSamples: SectionSample[];
|
|
minOffset: number;
|
|
maxOffset: number;
|
|
elevationMid: number;
|
|
displaySpan: number;
|
|
displayMax: number;
|
|
pixelsPerMeter: number;
|
|
heightPx: number;
|
|
}
|
|
|
|
/**
|
|
* 강제 높이를 얹기 **전**까지의 값 — 카드 두 벌(행 높이 재기 / 실제 그리기)이 나눠 쓴다.
|
|
* 예전에는 같은 측점의 기하를 두 번 계산했고, 그 값이 B06 진입에서 `draw` 자기 시간
|
|
* 213ms 의 대부분이었다(2026-09-06 CPU 프로파일, 측점 67곳 기준 3.2ms/장).
|
|
*/
|
|
export interface CrossPlotBase {
|
|
sourceSamples: SectionSample[];
|
|
minOffset: number;
|
|
maxOffset: number;
|
|
elevationMid: number;
|
|
pixelsPerMeter: number;
|
|
/** 강제 높이가 없을 때 쓰는 높이(바닥 `CROSS_HEIGHT` 적용 전). */
|
|
naturalHeight: number;
|
|
}
|
|
|
|
export function crossPlotBase(
|
|
section: CrossSection,
|
|
verticalExaggeration: number,
|
|
widthPx: number,
|
|
crossHalfWidth?: number,
|
|
designElevation?: number,
|
|
): CrossPlotBase | null {
|
|
const sourceSamples = section.samples.filter(
|
|
(sample) =>
|
|
crossHalfWidth === undefined || Math.abs(sample.offset_m ?? 0) <= crossHalfWidth + 1e-6,
|
|
);
|
|
const valid = sourceSamples.filter(validElevation);
|
|
if (!valid.length) return null;
|
|
const offsets = sourceSamples.map((sample) => sample.offset_m ?? 0);
|
|
const minOffset = Math.min(...offsets, -1);
|
|
const maxOffset = Math.max(...offsets, 1);
|
|
const elevations = valid.map((sample) => sample.elevation_m);
|
|
if (designElevation !== undefined && Number.isFinite(designElevation))
|
|
elevations.push(designElevation);
|
|
const rawMin = Math.min(...elevations);
|
|
const rawMax = Math.max(...elevations);
|
|
const elevationMid = (rawMin + rawMax) / 2;
|
|
const padding = rawMax > rawMin ? (rawMax - rawMin) * 0.08 : 0.5;
|
|
const pixelsPerMeter =
|
|
(widthPx - CROSS_PAD.left - CROSS_PAD.right) / Math.max(maxOffset - minOffset, 1e-6);
|
|
const rawSpan = Math.max(
|
|
(rawMax - rawMin + padding * 2) * Math.max(verticalExaggeration, 0.1),
|
|
1e-6,
|
|
);
|
|
const naturalHeight =
|
|
rawSpan * pixelsPerMeter + CROSS_PAD.top + CROSS_PAD.bottom + AREA_OVERLAY_HEADROOM_PX;
|
|
return { sourceSamples, minOffset, maxOffset, elevationMid, pixelsPerMeter, naturalHeight };
|
|
}
|
|
|
|
/** 재 둔 기하에 행 높이만 얹는다 — 다시 계산하지 않는다. */
|
|
export function crossPlotFromBase(base: CrossPlotBase, forcedHeightPx?: number): CrossPlotMetrics {
|
|
const heightPx = forcedHeightPx ?? Math.max(base.naturalHeight, CROSS_HEIGHT);
|
|
const displaySpan =
|
|
Math.max(heightPx - CROSS_PAD.top - CROSS_PAD.bottom, 1e-6) / base.pixelsPerMeter;
|
|
const displayMax =
|
|
base.elevationMid + displaySpan / 2 + AREA_OVERLAY_HEADROOM_PX / base.pixelsPerMeter / 2;
|
|
return {
|
|
sourceSamples: base.sourceSamples,
|
|
minOffset: base.minOffset,
|
|
maxOffset: base.maxOffset,
|
|
elevationMid: base.elevationMid,
|
|
displaySpan,
|
|
displayMax,
|
|
pixelsPerMeter: base.pixelsPerMeter,
|
|
heightPx,
|
|
};
|
|
}
|
|
|
|
export function crossPlotMetrics(
|
|
section: CrossSection,
|
|
verticalExaggeration: number,
|
|
widthPx: number,
|
|
crossHalfWidth?: number,
|
|
designElevation?: number,
|
|
forcedHeightPx?: number,
|
|
): CrossPlotMetrics | null {
|
|
const base = crossPlotBase(
|
|
section,
|
|
verticalExaggeration,
|
|
widthPx,
|
|
crossHalfWidth,
|
|
designElevation,
|
|
);
|
|
return base ? crossPlotFromBase(base, forcedHeightPx) : null;
|
|
}
|
|
|
|
/**
|
|
* 카드가 실제로 그릴 표시 반폭 — 개별값 > 전역값(2026-08-06).
|
|
*
|
|
* 횡단 보기 설정 값 그대로다 — 사면 교차점 · 배수관 세트가 그 밖이어도 줌아웃하지 않는다
|
|
* (PLAN 79-2 사용자: 너무 작아짐). 계산은 캐시 전 구간으로 해 두었으니 팬으로 끌어다 본다.
|
|
*/
|
|
export function effectiveCardHalfWidth(
|
|
_section: CrossSection,
|
|
baseHalfWidth: number | undefined,
|
|
): number | undefined {
|
|
return baseHalfWidth;
|
|
}
|