feat(B06): 횡단 줌 조작 일원화 + 교차점 맞춤 자동 확대
- 카드 하단 ◀/▶/↺(개별 표시 반폭) 삭제. 조작구는 우측 상단 줌 버튼 하나로. - 줌 버튼 역할 확장(원배율에서만 반폭 조작): − 배율>1이면 축소, 원배율이면 표시 반폭 +1m + 배율>1이면 확대, 원배율이면 표시 반폭 −1m(하한 2m) ⤢ 배율 원복 + 절·성토 교차점이 다 보이는 폭으로 맞춤 - 교차점 맞춤(_UI_Cross_Fit): 보유 샘플(계산 반폭 20m) 안에서 교차점을 찾고, 샘플 끝까지 안 만나면 끝단 사면·지반 기울기 차로 만나는 자리를 추세 외삽해 올림 +1m을 요청 반폭으로 쓴다(상한 60m). - 요청 반폭 > 보유 샘플이면 그 폭으로 재생성(regenerate) 후 해당 측점만 세션 반폭으로 넓게 표시. 계산 기본 20m·캐시는 그대로, 확정 시 기존 경로대로 display_half_width_m에 영구 저장. - 재생성 로직을 _UI_Page_Common.createSampleWidener로 분리(700줄 제한), [전체 측점 반영]도 같은 경로 사용. 연타 중복 요청 차단. 자체검증: node tmp/tests/test_toe_extrapolate.mjs (외삽 좌/우·발산 지형·+1m 올림), tsc --noEmit 통과. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,8 @@ import type {
|
||||
} from "./B06_Section_UI_Cross_Culvert_Wire";
|
||||
import { buildStructurePanel } from "./B06_Section_UI_Cross_Structure_Panel";
|
||||
import { attachZoomPan, buildZoomControls } from "./B06_Section_UI_Cross_View_Zoom";
|
||||
import type { ZoomPanState } from "./B06_Section_UI_Cross_View_Zoom";
|
||||
import type { CrossWidthActions, ZoomPanState } from "./B06_Section_UI_Cross_View_Zoom";
|
||||
import { toeFitHalfWidth } from "./B06_Section_UI_Cross_Fit";
|
||||
import type { RevetHighlightSetter, RevetKey } from "./B06_Section_UI_Cross_Culvert";
|
||||
import type { RevetMaterial } from "./B06_Section_UI_Cross_Culvert_Const";
|
||||
import type { WallAdjust } from "./B06_Section_UI_Cross_Culvert_Types";
|
||||
@@ -76,8 +77,11 @@ export interface CrossCardElement extends HTMLElement {
|
||||
export interface StationWidthControl {
|
||||
/** 개별값(세션→저장값) 우선. 없으면 undefined — 카드가 전역 반폭으로 그린다. */
|
||||
widthFor: (section: CrossSection) => number | undefined;
|
||||
adjust: (chainageM: number, deltaM: number) => void;
|
||||
reset: (chainageM: number) => void;
|
||||
/**
|
||||
* 이 측점 표시 반폭을 지정한다(2026-08-23 개편). 계산 반폭(기본 20m)을 넘는 값이면
|
||||
* 페이지가 백엔드 재생성으로 샘플을 넓힌 뒤 이 카드만 그 폭으로 다시 그린다.
|
||||
*/
|
||||
request: (chainageM: number, widthM: number) => void;
|
||||
}
|
||||
|
||||
// 기슭막이·유입 구조물·다단 제어 인터페이스는 Wire에 있다(700줄 제한) — 재수출.
|
||||
@@ -106,7 +110,7 @@ export function createCrossSectionCard(
|
||||
initialAreaKey?: CrossAreaKey | null,
|
||||
/** 면적 값을 눌렀을 때. 선택되지 않은 카드에서도 눌릴 수 있어 측점 id를 함께 넘긴다. */
|
||||
onAreaSelect?: (stationId: string, key: CrossAreaKey | null) => void,
|
||||
/** 개별 표시 반폭 제어 — 있으면 카드 하단에 ◀/▶/↺ 버튼 그룹을 우측 맞춤으로 단다. */
|
||||
/** 개별 표시 반폭 제어 — 있으면 우측 상단 줌 버튼이 원배율에서 반폭을 조절한다. */
|
||||
stationWidth?: StationWidthControl,
|
||||
/** 기슭막이 X 자리 제어 — 있으면 벽이 선택 가능해지고 선택 시 ◀/▶/↺가 뜬다. */
|
||||
revetOffset?: RevetOffsetControl,
|
||||
@@ -628,7 +632,19 @@ export function createCrossSectionCard(
|
||||
});
|
||||
showRevetControl = (visible) => panel.show(visible ? activeRevet : null);
|
||||
showRevetControl(activeRevet !== null);
|
||||
chartWrap.append(svg, readout.root, buildZoomControls(zoomPan), panel.root);
|
||||
// 우측 상단 줌 버튼이 원배율에서 표시 반폭까지 다룬다(하단 ◀/▶/↺ 폐지, 2026-08-23).
|
||||
const widthActions: CrossWidthActions | undefined = stationWidth && {
|
||||
step: (deltaM) =>
|
||||
stationWidth.request(
|
||||
section.chainage_m,
|
||||
Math.round((effectiveHalfWidth ?? maxOffset) + deltaM),
|
||||
),
|
||||
fit: () => {
|
||||
const fitted = toeFitHalfWidth(section);
|
||||
if (fitted !== null) stationWidth.request(section.chainage_m, fitted);
|
||||
},
|
||||
};
|
||||
chartWrap.append(svg, readout.root, buildZoomControls(zoomPan, widthActions), panel.root);
|
||||
if (activeArea) {
|
||||
setBandActive(activeArea);
|
||||
setChipActive(activeArea);
|
||||
@@ -646,30 +662,6 @@ export function createCrossSectionCard(
|
||||
rockControl.classList.add("b06-cross-card__rockb");
|
||||
footer.append(rockControl);
|
||||
}
|
||||
// 개별 표시 반폭 ◀/▶/↺ — 우측 맞춤, 초기화는 전역 반폭 복귀(2026-08-06).
|
||||
if (stationWidth) {
|
||||
const widthControl = document.createElement("div");
|
||||
widthControl.className = "b06-cross-card__widthctl";
|
||||
const makeButton = (label: string, title: string, onClick: () => void): HTMLButtonElement => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "b06-design__rockb-btn";
|
||||
button.textContent = label;
|
||||
button.title = title;
|
||||
button.addEventListener("click", (event) => {
|
||||
// 카드 선택 클릭으로 번지면 재렌더로 줌·팬이 초기화된다.
|
||||
event.stopPropagation();
|
||||
onClick();
|
||||
});
|
||||
return button;
|
||||
};
|
||||
widthControl.append(
|
||||
makeButton("◀", L("B06_Cross_Width_Dec"), () => stationWidth.adjust(section.chainage_m, -1)),
|
||||
makeButton("▶", L("B06_Cross_Width_Inc"), () => stationWidth.adjust(section.chainage_m, 1)),
|
||||
makeButton("↺", L("B06_Cross_Width_Reset"), () => stationWidth.reset(section.chainage_m)),
|
||||
);
|
||||
footer.append(widthControl);
|
||||
}
|
||||
card.append(footer);
|
||||
return card;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user