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
@@ -126,7 +126,31 @@ export function buildStructurePanel(deps: StructurePanelDeps): StructurePanelHan
),
);
// 높이·재질 행(2026-08-22 사용자) — 높이 ±0.1m, 재질이 한계를 정한다.
// 재질 행(2026-08-22 사용자 — 높이 행 **위**, 폭 축소: 한계는 툴팁으로).
const materialRow = document.createElement("label");
materialRow.className = "b06-structure-panel__struct";
const materialLabelEl = document.createElement("span");
materialLabelEl.textContent = L("B06_Cross_Mat_Label");
const materialSelect = document.createElement("select");
materialSelect.className = "b06-structure-panel__select";
const MATERIAL_LABEL: Record<RevetMaterial, string> = {
dry: L("B06_Cross_Mat_Dry"),
wet: L("B06_Cross_Mat_Wet"),
concrete: L("B06_Cross_Mat_Concrete"),
};
for (const material of REVET_MATERIALS) {
const option = document.createElement("option");
option.value = material;
option.textContent = MATERIAL_LABEL[material];
option.title = `${L("B06_Cross_Height_Label")} ~${materialLimit(material).toFixed(1)}m`;
materialSelect.append(option);
}
materialSelect.addEventListener("change", () => {
if (current) deps.setMaterial(current, materialSelect.value as RevetMaterial);
});
materialRow.append(materialLabelEl, materialSelect);
// 높이 행 — ±0.1m, 한계는 재질이 정한다.
const heightRow = document.createElement("div");
heightRow.className = "b06-structure-panel__struct";
const heightLabel = document.createElement("span");
@@ -143,23 +167,7 @@ export function buildStructurePanel(deps: StructurePanelDeps): StructurePanelHan
L("B06_Cross_Height_Plus"),
act((key) => deps.nudgeHeight(key, HEIGHT_STEP_M)),
);
const materialSelect = document.createElement("select");
materialSelect.className = "b06-structure-panel__select";
const MATERIAL_LABEL: Record<RevetMaterial, string> = {
dry: L("B06_Cross_Mat_Dry"),
wet: L("B06_Cross_Mat_Wet"),
concrete: L("B06_Cross_Mat_Concrete"),
};
for (const material of REVET_MATERIALS) {
const option = document.createElement("option");
option.value = material;
option.textContent = `${MATERIAL_LABEL[material]}(~${materialLimit(material).toFixed(1)}m)`;
materialSelect.append(option);
}
materialSelect.addEventListener("change", () => {
if (current) deps.setMaterial(current, materialSelect.value as RevetMaterial);
});
heightRow.append(heightLabel, heightMinus, heightValue, heightPlus, materialSelect);
heightRow.append(heightLabel, heightMinus, heightValue, heightPlus);
// 유입측 구조물 형식 드롭다운(2026-08-22 사용자) — 자동/기슭막이/집수정 I·ㄴ·ㄷ.
const structureRow = document.createElement("label");
@@ -218,7 +226,7 @@ export function buildStructurePanel(deps: StructurePanelDeps): StructurePanelHan
const head = document.createElement("div");
head.className = "b06-structure-panel__head";
head.append(title, closeButton);
root.append(head, value, structureRow, heightRow, extraRow, buttons);
root.append(head, value, structureRow, materialRow, heightRow, extraRow, buttons);
return {
root,
@@ -252,17 +260,16 @@ export function buildStructurePanel(deps: StructurePanelDeps): StructurePanelHan
select.value = structure;
}
buttons.classList.toggle("is-hidden", !movable);
materialRow.classList.toggle("is-hidden", !movable);
heightRow.classList.toggle("is-hidden", !movable);
if (movable) {
heightValue.textContent = `${deps.heightFor(key).toFixed(1)}m`;
materialSelect.value = deps.materialFor(key);
}
// 단 수 행은 **유출 벽에서** — 성토부 5m 이상(의무)이거나 이미 단이 있을 때.
// 단 수 행은 유출 벽에서 **항상** 보인다(2026-08-22 사용자 ⑤ — 상태 따라
// 나타났다 사라지면 레이아웃이 널뛴다).
const extra = deps.extraState();
extraRow.classList.toggle(
"is-hidden",
!(key === "outlet" && (extra.canAdd || extra.count > 0)),
);
extraRow.classList.toggle("is-hidden", key !== "outlet");
countInput.value = String(extra.count);
const adjust = deps.adjustFor(key);
// 조작 요약 — 0이면 "자동". 좌우는 안/바깥, 상하는 위/아래로 적는다.