fix(B06): 유출 관 끝점·전면 근입 기준 + 줌 유지·조정창 레이아웃 정리

- 유출 관 하단 끝점 = 벽 바닥 +0.5m 기준선과 전면 경사선(1:0.3)의 교차점
  (성토부선 시작점과 동일 자리) — 정수 맞춤 재배치에도 동일 적용
- 근입 0.5m는 경사선(전면) 측 깊이 기준 — 전면 발끝 지반 아래 0.5까지
  수렴 반복으로 내림(배관 벽·추가 벽 공통, 뜬 벽은 기준선 아래 0.5)
- 카드 내부 조작(조정창 등)으로 다시 그려져도 줌·팬 배율 유지
  (측점별 상태 보관, ZoomPanState)
- 조정창: 재질 행을 높이 행 위 별도 행으로(폭 축소 — 한계는 툴팁),
  추가 기슭막이(단) 행은 유출 벽에서 항상 표시(레이아웃 널뜀 방지)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-22 14:32:30 +09:00
co-authored by Claude Opus 5
parent ff3e3b734b
commit 97bf00a8ef
6 changed files with 111 additions and 53 deletions
+21 -4
View File
@@ -18,11 +18,22 @@ export interface ZoomPanHandle {
reset: () => void;
}
/** 줌·팬 상태 — 카드가 다시 그려질 때 배율을 되살리는 데 쓴다(2026-08-22 사용자 ③). */
export interface ZoomPanState {
scale: number;
tx: number;
ty: number;
}
export function attachZoomPan(
svg: SVGSVGElement,
plotLayer: SVGGElement,
widthPx: number,
heightPx: number,
/** 이전 카드의 줌·팬 상태 — 조정창 조작으로 카드가 다시 그려져도 배율 유지. */
initial?: ZoomPanState,
/** 상태가 바뀔 때마다 부른다 — 카드 밖(뷰)이 담아 뒀다가 다음 렌더에 되돌린다. */
onChange?: (state: ZoomPanState) => void,
): ZoomPanHandle {
// 플롯 영역(축 안쪽) — 확대·축소의 중심이자 이동 한계의 기준이다.
const plot = {
@@ -32,11 +43,13 @@ export function attachZoomPan(
height: Math.max(heightPx - CROSS_PAD.top - CROSS_PAD.bottom, 1),
};
const maxScale = 8;
let scale = 1;
let tx = 0;
let ty = 0;
const applyTransform = (): void =>
let scale = initial?.scale ?? 1;
let tx = initial?.tx ?? 0;
let ty = initial?.ty ?? 0;
const applyTransform = (): void => {
plotLayer.setAttribute("transform", `translate(${tx} ${ty}) scale(${scale})`);
onChange?.({ scale, tx, ty });
};
// 확대한 도형이 플롯 영역을 항상 덮게 이동량을 가둔다 — 원배율에서는 이동량이 0으로 묶인다.
const clampPan = (): void => {
tx = Math.min(Math.max(tx, (plot.x + plot.width) * (1 - scale)), plot.x * (1 - scale));
@@ -100,6 +113,10 @@ export function attachZoomPan(
/* 이미 해제됨 */
}
};
if (initial && (scale !== 1 || tx !== 0 || ty !== 0)) {
clampPan();
applyTransform();
}
svg.addEventListener("pointerup", endPan);
svg.addEventListener("pointercancel", endPan);
// 드래그(팬)로 끝난 클릭은 카드 선택으로 전파하지 않는다.