feat(B05/B06): 종횡단 상세 프론트 공유 캐시 + 계획선 호 통과 기하

페이지 간 캐시 공유
- B06_wf3_ProfileCross_Section_Store.ts 신설: 종횡단 상세를 projectId:routeId
  키로 들고 있는 모듈 싱글턴 캐시. B05와 B06이 같은 객체 참조를 보므로 B06이
  측점 설계를 제자리 갱신하면 B05가 다음 그리기에서 그대로 본다 — 횡단을 고친
  뒤 B05 유토곡선이 옛 값으로 그려지던 문제의 원인이 페이지별 개별 fetch였다.
- 두 페이지 진입을 loadSectionDetail()로 통일(동시 호출은 Promise 공유).
- 정본이 다시 쓰이는 조작에 캐시 갱신: 횡단 재생성 → replaceSectionDetail,
  계획선 편집 저장 → invalidateSectionDetail.

계획선 호 기하
- 배관 지점(지면선 × 배관 세로선 교점)이 호 위에 오도록 변화점 표고를 반복
  보정. 대칭 종단곡선은 꼭짓점을 지나지 않아 중앙종거만큼 어긋나 있었다.
  시작점·종점과 각 호, 호와 호 사이는 직선이 접선으로 잇는다.
- 호 반경 기본값을 설계 기준의 종단곡선 최소 반경으로 지정하고 curve_radii
  편집 델타에 실어 B05 테이블에서 사용자가 그대로 고칠 수 있게 했다.

유토곡선 Y축
- B05에만 있던 종단용 sticky 표고축 탓에 가로로 훑으면 유토곡선 눈금은 흘러가고
  표고축만 남아 Y축이 높이로 읽혔다. createMassHaulChart에 onAxis 콜백을 더해
  유토곡선용 sticky 누가토량 축을 따로 고정.

검증: solve 재실행 후 배관 4개 자리에서 계획고=지반고(오차 ≤0.0001m), 호 비겹침
확인. typecheck·vite build·ruff·B03 테스트 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-03 20:28:23 +09:00
co-authored by Claude Opus 5
parent a9741277cf
commit 1ee6516dd5
7 changed files with 155 additions and 13 deletions
+11
View File
@@ -282,6 +282,12 @@ export function createMassHaulChart(
minimumWidthPx: number,
onSelectStation: (stationId: string) => void,
haulPlan: HaulPlan | null,
/**
* Y축 눈금을 호출한 쪽에 알려 준다 — 가로 스크롤에도 왼쪽에 **고정되는 축**을 따로
* 그리려면 SVG와 같은 눈금값이 필요하다(B05 종단 패널이 그렇게 쓴다).
* 이 값을 안 쓰는 화면(B06)은 콜백을 넘기지 않으면 된다.
*/
onAxis?: (axis: { padLeft: number; ticks: Array<{ y: number; label: string }> }) => void,
): SVGSVGElement {
const svg = svgElement("svg", {
class: "b06-section__chart b06-masshaul",
@@ -311,9 +317,11 @@ export function createMassHaulChart(
// 패널을 줄이면 그래프 몫이 60px대까지 내려간다 — 눈금 5개를 그대로 두면 라벨이 서로 겹친다.
const tickRatios = plotHeight < 110 ? [0, 0.5, 1] : [0, 0.25, 0.5, 0.75, 1];
const axisTicks: Array<{ y: number; label: string }> = [];
for (const ratio of tickRatios) {
const gridY = MASS_PAD_TOP + ratio * plotHeight;
const value = max - ratio * span;
axisTicks.push({ y: gridY, label: formatVolume(value) });
svg.append(
svgElement("line", {
x1: axisX,
@@ -332,6 +340,9 @@ export function createMassHaulChart(
}
const zeroY = y(0);
// 0선은 격자보다 진하게 따로 그리므로 고정 축 눈금에도 함께 실어 준다.
axisTicks.push({ y: zeroY, label: "0" });
onAxis?.({ padLeft: axisX, ticks: axisTicks });
const visible = series.filter((entry) => visibleKeys.has(entry.key));
/** 주어진 가로 구간에서 표시 중인 곡선이 지나는 세로 범위. 말풍선·balloon 배치가 함께 쓴다. */