feat(B06): BOX암거 좌측 폼을 세월교와 같은 규칙으로 연결한다
세월교와 같은 문제가 BOX암거에도 있었다 — 좌측 「구조물 배치」의 본체 규격·날개벽을 바꿔도 도면이 그대로였고, 폼 기본값이 스펙에서 오지 않았으며, 구체를 골라도 좌측 칸이 서지 않았다. `applyPipeOptionsToCache`가 `section.box`를 모르고 즉시 return하던 것이 뿌리다(세월교와 동일). - `applyBoxFormOptions()` — 폼 옵션(`body_*`·`wing_*`)을 제어기로 보낸다. `BoxControl`에 `setBody`·`setWing`을 더했다: 캐시(`section.box`)를 먼저 고치고 (`span_m`은 백엔드 `_box_set`과 같은 식으로 다시 잡는다) 정본 예약 + 카드 갱신. - 날개벽 옵션 조립·해석을 `wingOptions()`·`wingPatchFrom()`으로 묶어 세월교와 나눠 쓴다 — 세월교 쪽 중복 코드가 사라졌다. - `withBoxSpec()` — 폼 기본값을 `section.box`(내공 폭·높이, 날개벽)에서 읽는다. - 좌·우 끝을 고르면 폼의 **날개벽(유입)/(유출)** 칸이 선다. 좌·우 ↔ 유입/유출 매핑은 기하가 날개벽을 고르는 기준(`uphill_side`)과 같다. 조정창은 결국 지우고 폼 이름(유입/유출)만 쓰기로 한 사용자 확정(2026-08-30)을 따랐다. 구체 길이·표고는 폼에 대응 칸이 없어 조정창 축으로 남는다(본체 높이는 규격이 이미 정한다 — 2026-08-30 사용자). 자체검증(공용 브라우저 5174 + 워크트리 백엔드 8001, 측점 10+0.9 BOX암거): - tsc --noEmit 통과. - 구체 선택 시 좌측 `날개벽(유입) (BOX암거)` 칸 강조(수정 전 강조 없음). - 본체 규격 2.0×2.0 → 3.0×3.0 → 도면 라벨 `BOX 3.0×3.0`, 다시 2.0×2.0 → `BOX 2.0×2.0`. 한 번 고른 상태에서 연속 조작도 그대로 먹는다. 수정 전에는 라벨이 5.34m 구체와 함께 전혀 움직이지 않았다. - 회귀: 세월교 날개 길이 2→4→2에서 조정창 표시가 4.0m→2.0m로 따라온다 (`wingOptions`/`wingPatchFrom` 공용화 뒤에도 동일). - 조작값은 원래대로(BOX 2.0×2.0 · 세월교 날개 길이 2m) 되돌려 놓았다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,30 @@ function withFordSpec(
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* BOX암거 폼 옵션을 **지금 그려진 스펙**(`section.box`)으로 덮는다 — 세월교와 같은
|
||||
* 규칙이다(2026-08-30 사용자). 본체 규격은 내공 폭·높이가 정본이고, 구체 길이·표고는
|
||||
* 조정 채널이라 여기 오지 않는다.
|
||||
*/
|
||||
function withBoxSpec(
|
||||
options: Record<string, string | number> | undefined,
|
||||
box: NonNullable<SectionDetailResponse["cross_sections"][number]["box"]>,
|
||||
): Record<string, string | number> {
|
||||
const merged: Record<string, string | number> = { ...(options ?? {}) };
|
||||
merged.body_width_m = box.inner_width_m;
|
||||
merged.body_height_m = box.inner_height_m;
|
||||
for (const [wing, prefix] of [
|
||||
[box.wing_in, "wing_in"],
|
||||
[box.wing_out, "wing_out"],
|
||||
] as const) {
|
||||
merged[prefix] = wing.installed ? "있음" : "없음";
|
||||
if (wing.height_m !== null) merged[`${prefix}_height_m`] = wing.height_m;
|
||||
if (wing.length_m !== null) merged[`${prefix}_length_m`] = wing.length_m;
|
||||
if (wing.angle_deg !== null) merged[`${prefix}_angle_deg`] = wing.angle_deg;
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* 관 지점 옵션에 **횡단 스펙 값**을 채워 넣는다 — 조정창이 보여주던 값과 폼이
|
||||
* 어긋나지 않게 한다(2026-08-29 사용자). 옵션에 이미 값이 있으면 그대로 두고,
|
||||
@@ -147,6 +171,7 @@ function withSpecDefaults(
|
||||
// 그 스펙이 지금 그려진 값이라 저장된 옵션보다 앞선다(2026-08-30 사용자: 프론트는
|
||||
// 구조물 배치 상세 UI, 로직·값은 조정창 것).
|
||||
if (owner?.ford) return withFordSpec(options, owner.ford);
|
||||
if (owner?.box) return withBoxSpec(options, owner.box);
|
||||
const culvert = owner?.culvert;
|
||||
if (!culvert) return options;
|
||||
const merged: Record<string, string | number> = { ...(options ?? {}) };
|
||||
@@ -458,6 +483,8 @@ export function wireStructureSelection(
|
||||
markWallSelecting();
|
||||
origBox(chainageM, role);
|
||||
panel.showPipeAt(role ? chainageM : null);
|
||||
// 폼이 이 시설로 갈아 끼워진 뒤에 이식을 다시 돌린다 — 날개벽 칸이 그제야 선다.
|
||||
refreshAdjustSlots();
|
||||
};
|
||||
return { syncInletStructure };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user