B05 세월교 옵션에 BOX암거 날개벽 한 벌(설치·짧은쪽 높이 1m·길이 2m·각도 45°)을 그대로 붙이고, B06 횡단도에 세월교 구체를 그린다. 구체는 양측 ㄴ형 집수정 측벽 + 그 사이를 잇는 바닥판 + 바닥판 상면에 안힌 관 + 관 위 성토(노체) 채움이다. - 바닥판 편측 연장 = 날개벽 길이 × cos(각도) — 각도는 관축 기준 벌어짐각 - 측벽은 buildBasin 재사용이라 1:0.3 기움·좌우·상하 대각 이동·계류측 성토부선이 집수정과 같은 규칙으로 따라온다 (기슭막이 다단·재질과는 연계하지 않음) - 구체는 월류 폭만큼 도로 방향으로 이어져 기준 측점 전후 절반까지 붙는다 - 조정창: 측벽 높이·좌우·상하, 관경·수량(정본 pipe_points 되쓰기). 높이·좌우·상하는 세션 + design.ford_adjust에 저장 - 3D 예상형상: 구체 부재는 월류 폭 구간 스윕, 관은 련수만큼 폭 안 등간격 - 부재 두께: 바닥판 0.3m(교본 물넘이 물받이 최소 30cm), 측벽 0.2m(집수정 승계) 700줄 제한으로 카드 렌더러·측점 제어기·페이지에서 배선을 분리했다 (_Cross_View_Ford, _Page_Ford_Controls, _Page_Patches, Culvert_Const 구간값 헬퍼). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
266 lines
13 KiB
TypeScript
266 lines
13 KiB
TypeScript
/* =============================================================================
|
||
* B06_Section_UI_Cross_Culvert_Const.ts
|
||
* 배수관 세트 **치수 상수·형태별 한계** — 기하 계산(`_Cross_Culvert_Geom.ts`)에서
|
||
* 분리했다(700줄 제한). 값의 근거는 각 상수 주석에 지식DB 출처로 남긴다.
|
||
* ========================================================================== */
|
||
|
||
import type { CrossSection, CulvertSideSpec } from "./B06_Section_Api_Fetch";
|
||
import type { SpanRole, SpanValues } from "./B06_Section_UI_Cross_Culvert_Wire";
|
||
|
||
/** 관 위 최소 토피(m) — 프론트 정의처(백엔드는 `B06_Section_Engine_Culvert.MIN_PIPE_COVER_M`).
|
||
* ⚠ 임도 배수관 토피 규정이 지식DB에 없어 별표2 교량·암거 "복토 50㎝ 이상"을 교차
|
||
* 참조로 채택(2026-08-19 사용자 확정). B05 배수관 측점 계획고 하향 차단 기준이자,
|
||
* 절토측 유입 벽에서 관 invert의 상한(노면 − 관경 − 토피)을 정한다. */
|
||
export const MIN_PIPE_COVER_M = 0.5;
|
||
|
||
/** 기슭막이 벽 두께(m) — 실무 견치돌 뒷길이 관측치 45㎝(울진 L3=45). 표시용 형상 값. */
|
||
export const REVET_THICKNESS_M = 0.45;
|
||
|
||
/** 기슭막이 전면 기울기(1:n) — 돌쌓기 전면 1:0.3(교본 7-3). 벽이 사면 쪽으로 기운다. */
|
||
export const REVET_LEAN_RATIO = 0.3;
|
||
|
||
/** 돌붙임 두께(m) — 실무 돌붙임 L3=45(울진 1공구 수량집계표). */
|
||
export const PITCHING_THICKNESS_M = 0.45;
|
||
|
||
/** 사다리꼴(배면부) 상단 폭(m) — 띠 두께의 절반(2026-08-20 사용자 확정). */
|
||
export const REVET_TRAP_TOP_M = REVET_THICKNESS_M / 2;
|
||
|
||
/**
|
||
* 기슭막이 여유고(m) — 관 상단에서 벽 상단까지의 간격. 유입·유출 **공통**이다.
|
||
*
|
||
* ⚠ 확정값 아님(2026-08-21 사용자 지정). 지식DB에 임도 배수관 기슭막이 여유고
|
||
* 규정이 없다(`통수단면.md` §여유고 — "임도·KDS 원문에 수치 없음"). 관 상단 최소
|
||
* 토피 0.5m와 같은 값을 잠정 채택했고, 추후 **사용자 입력값**으로 교체 예정이다.
|
||
*
|
||
* 이 값 때문에 벽 높이는 좌·우가 같은 규칙으로 정해진다 — 관이 경사져 벽 밑(invert)
|
||
* 이 서로 달라도 관 상단 위 간격은 항상 같다.
|
||
*/
|
||
export const REVET_FREEBOARD_M = 0.5;
|
||
|
||
/** 기슭막이 높이 눈금(m) — 계산 높이를 이 단위로 올린다(2026-08-21 사용자 확정). */
|
||
export const REVET_HEIGHT_STEP_M = 0.1;
|
||
|
||
/** 관경에 여유고를 더해 눈금(0.1m)으로 올린 기슭막이 높이(m). */
|
||
export function revetTargetHeight(diameterM: number): number {
|
||
const raw = diameterM + REVET_FREEBOARD_M;
|
||
return Math.ceil(raw / REVET_HEIGHT_STEP_M - 1e-9) * REVET_HEIGHT_STEP_M;
|
||
}
|
||
|
||
/**
|
||
* 관 벽 두께(m) — 관종별. 지식DB 조회(2026-08-20):
|
||
* · 파형강관: 강판 두께 **1.6T~3.2T**(교본 부록 4-6 §2.5.2, 표-1 표준형 강관 치수,
|
||
* KS D 3590). Ø1000 이상은 1.6~3.2 전 규격 사용 — 중간값 2.7T 채택.
|
||
* · 흄관·VR관(원심력 철근콘크리트관): 관경별 벽 두께 표가 지식DB에 없어
|
||
* 관경의 **1/12**을 적용한다(⚠ 근거 미확정 — KS F 4403 확보 시 교체).
|
||
* 도면상 내경·외경 2줄을 긋기 위한 값이다.
|
||
*/
|
||
export function pipeWallThicknessM(kind: string | null, diameterM: number): number {
|
||
if (kind && kind.includes("파형강관")) return 0.0027;
|
||
return Math.max(diameterM / 12, 0.05);
|
||
}
|
||
|
||
/**
|
||
* 기슭막이 근입 깊이(m) — 벽 바닥이 원지반 아래로 묻히는 깊이.
|
||
*
|
||
* 지식DB 조회 결과(2026-08-20):
|
||
* · 기슭막이 자체의 근입 깊이 **규정 없음** — 사방교본은 "기초 세굴 위험 시
|
||
* 밑다짐·바닥막이·수제 병행"만 정한다(`00_구조물_선정기준.md` §4).
|
||
* · 흙막이: "하부 땅밀림 방지 위해 **본바닥(원지반)에 기초**"(`흙막이.md`) —
|
||
* 원지반에 묻어야 한다는 원칙만 있고 수치는 없다.
|
||
* · 실무(울진소광 구조도): 돌기슭막이 **기초형 = 기초콘크리트 H=0.5**,
|
||
* 깬돌 ℓ3=45·1:0.3 (`_원단위라이브러리_울진소광.md` §4).
|
||
* → 실무 기초콘크리트 높이 0.5m를 근입 깊이로 채택한다. 임도 법정 확정값이 아니라
|
||
* **실무 관측 기반**이므로 확정 기준 확보 시 교체한다.
|
||
*/
|
||
export const REVET_EMBED_DEPTH_M = 0.5;
|
||
|
||
/**
|
||
* 기슭막이 형태별 높이 한계(m) — 지식DB `돌쌓기.md` §1 / `흙막이.md` §2:
|
||
* 돌 **찰쌓기 3.0m 이하**, 돌 **메쌓기 2.0m 이하**(교본 7-3 돌흙막이 기준),
|
||
* 돌망태 2m 정도. 큰돌쌓기는 총높이 한계 없음(1일 시공 1.5m 제한만).
|
||
* 초과 시 콘크리트 옹벽 등 상위 구조물 검토 대상이다.
|
||
*/
|
||
export function revetHeightLimit(form: string | null | undefined): number {
|
||
if (!form) return 3.0;
|
||
if (form.includes("메")) return 2.0;
|
||
if (form.includes("찰")) return 3.0;
|
||
if (form.includes("돌망태")) return 2.0;
|
||
if (form.includes("콘크리트")) return 4.0; // 콘크리트 흙막이 산복기초 4.0m 이하
|
||
return 3.0;
|
||
}
|
||
|
||
/* ── 기슭막이 재질·높이 조작(2026-08-22 사용자 확정) ─────────────────────
|
||
* 높이 조작이 교본 형태별 한계와 부딪히면 **재질을 바꿔야** 더 올릴 수 있다:
|
||
* 메쌓기 2.0 / 찰쌓기 3.0(돌쌓기.md §1) / 콘크리트 5.0(프로젝트 기본값,
|
||
* 외부 기준 미확인). 배관 기슭막이의 높이 제어·표기·한계 기준은 **순수 높이**
|
||
* (벽 바닥~상단, 관 아래 0.5m 포함 — 2026-08-23 사용자 확정). 일반(추가)
|
||
* 기슭막이는 종전 계산용 높이(근입 0.5 위 기준선~상단) 기준을 유지한다. */
|
||
export type RevetMaterial = "dry" | "wet" | "concrete";
|
||
|
||
export const REVET_MATERIALS: RevetMaterial[] = ["dry", "wet", "concrete"];
|
||
|
||
export function materialLimit(material: RevetMaterial): number {
|
||
return material === "dry" ? 2.0 : material === "wet" ? 3.0 : 5.0;
|
||
}
|
||
|
||
export function materialLabel(material: RevetMaterial): string {
|
||
return material === "dry" ? "메쌓기" : material === "wet" ? "찰쌓기" : "콘크리트";
|
||
}
|
||
|
||
/** 백엔드 spec 형식 문자열("돌쌓기(메)" 등) → 재질 기본값. 기본은 메쌓기. */
|
||
export function materialFromForm(form: string | null | undefined): RevetMaterial {
|
||
if (form?.includes("찰")) return "wet";
|
||
if (form?.includes("콘크리트")) return "concrete";
|
||
return "dry";
|
||
}
|
||
|
||
/** 관 접속 절·성토선 물매 — 0도 대신 3도(2026-08-22 사용자). 방향은 물 흐름을
|
||
* 따른다: 유입측은 계류 쪽이 높고(물이 관으로), 유출측은 계류 쪽이 낮다(관에서 밖으로). */
|
||
export const PIPE_CONNECT_GRADE = Math.tan((3 * Math.PI) / 180);
|
||
|
||
/**
|
||
* 배관 기슭막이 관 하부 여유(m) — 관 invert 아래 벽 바닥까지의 깊이(2026-08-23
|
||
* 사용자 확정: 배관 기준 아래 0.5·위 0.5 여유). 값은 근입 깊이와 같은 0.5라
|
||
* 형상은 종전과 동일하고, **순수 높이 하한 = 관경 + 위 0.5 + 아래 0.5**가 된다.
|
||
* 종전 고정 하한·기본 2.0m(관경 무관)는 폐기.
|
||
*/
|
||
export function pipeWallMinPureHeight(diameterM: number): number {
|
||
return revetTargetHeight(diameterM) + REVET_EMBED_DEPTH_M;
|
||
}
|
||
/** 일반(추가) 기슭막이 높이 조작 하한(m) — 프로젝트 기본값 0.5m. */
|
||
export const EXTRA_WALL_MIN_HEIGHT_M = 0.5;
|
||
/** 일반(추가) 기슭막이 기본 높이(m). */
|
||
export const EXTRA_WALL_DEFAULT_HEIGHT_M = 1.5;
|
||
|
||
/**
|
||
* 성토 비탈 기울기(1:n) 허용 범위 — 지식DB `성토_비탈면.md` §1:
|
||
* "**1:1.2~2.0** 범위에서 토질·용수 등 지형여건을 종합 고려해 설정".
|
||
* 1.2는 **범위의 급한 끝**이지 고정값이 아니다 — 배수관 구조물이 서는 측점에서는
|
||
* 사면선이 기슭막이 이음선 상단점을 지나도록 이 범위 안에서 물매를 정한다
|
||
* (2026-08-21 사용자 지시).
|
||
*/
|
||
export const FILL_SLOPE_RATIO_MIN = 1.2;
|
||
export const FILL_SLOPE_RATIO_MAX = 2.0;
|
||
|
||
/** 성토사면 길이 한계(m) — `성토_비탈면.md` §2 "5m 이내". 초과 시 옹벽·석축 등 의무. */
|
||
export const FILL_SLOPE_MAX_LENGTH_M = 5.0;
|
||
|
||
/**
|
||
* 배관 기슭막이 기본 자리 — 노견에서 **성토사면 경사길이 4m 지점**(2026-08-23 사용자
|
||
* 확정). 상하(d) 조작의 0점은 성토선 길이 0 지점(벽 상단 = 노견)이고, d는 그 점에서
|
||
* 1:1.2 성토선을 타고 내려간 수평 성분(m)이다. 기본 자리가 안 되는 지형(매몰·역경사·
|
||
* 토피)은 배치 풀이의 기존 한계 논리가 안쪽으로 되돌린다.
|
||
*/
|
||
export const PIPE_WALL_DEFAULT_SLOPE_M = 4.0;
|
||
/** 기본 자리(경사길이 4m)의 수평 성분(m) — d 기본값. */
|
||
export const PIPE_WALL_DEFAULT_RUN_M =
|
||
(PIPE_WALL_DEFAULT_SLOPE_M * FILL_SLOPE_RATIO_MIN) / Math.hypot(1, FILL_SLOPE_RATIO_MIN);
|
||
|
||
/**
|
||
* 성토 전체고(노견 − 원지반) 기준(m) — 이 높이를 넘으면 1:1.2로도 사면이 5m를 넘어
|
||
* 구조물이 필요해진다(3m × √(1+1.2²) = 4.69m, 3.2m에서 5.0m). 기슭막이를 자연 사면
|
||
* 끝이 아니라 **사면길이 5m 지점**으로 끌어올리는 판정에 쓴다(2026-08-21 사용자 지시).
|
||
*/
|
||
export const FILL_STRUCTURE_HEIGHT_M = 3.0;
|
||
|
||
/**
|
||
* 성토 사면이 성립하는 **최소 성토고**(m) — 노견 − 벽 이음선 상단.
|
||
*
|
||
* 이보다 낮은 자리에 벽을 세우면 사면선이 노견에서 벽 상단으로 **올라가** 역전된다
|
||
* (2026-08-21 사용자 지적). 자동 배치는 물매 조건이 이미 막지만, 조정창으로 손수
|
||
* 미는 경우는 이 하한으로 막는다.
|
||
*/
|
||
export const FILL_MIN_RISE_M = 0.3;
|
||
|
||
/**
|
||
* 집수정 바닥 여유(m) — **관 하단 아래**로 두는 깊이. 상부 여유는 기슭막이와 같은
|
||
* `REVET_FREEBOARD_M`(0.5)을 쓴다. 집수정 깊이 = 바닥 여유 + 관경 + 상부 여유
|
||
* (2026-08-21 사용자 확정 — 종전에는 성토 경사선과의 교점으로 높이를 잡았다).
|
||
*/
|
||
export const BASIN_BOTTOM_CLEARANCE_M = 0.5;
|
||
|
||
/**
|
||
* 집수정 내부 높이(m) — 바닥판이 있는 ㄴ(L)·ㄷ형의 **바닥 윗면~벽 상단** 깊이.
|
||
* 프로젝트 기본값 1.2m(2026-08-22 사용자 확정). 외부 기준은 확인되지 않았다.
|
||
* I형(공유벽뿐)은 이 값을 쓰지 않고 노견 끝 접점까지 벽을 키운다.
|
||
*/
|
||
export const BASIN_INNER_HEIGHT_M = 1.2;
|
||
|
||
/**
|
||
* 집수정 전환 기준 성토사면 길이(m) — 유입측 성토사면이 이 길이 **이하**면 기슭막이
|
||
* +배관을 두기 어려워 집수정이 기본이 된다(2026-08-22 사용자 확정). 절토측 유입은
|
||
* 성토사면이 없으므로 자동 포함. 사용자 선택(기슭막이 유지)은 향후 추가 예정.
|
||
*/
|
||
export const BASIN_MAX_FILL_SLOPE_M = 3.0;
|
||
|
||
/** 집수정 부재(벽·바닥판) 두께(m) — 2026-08-22 사용자 지정 0.2. */
|
||
export const BASIN_MEMBER_THICKNESS_M = 0.2;
|
||
|
||
/* ---------------------------------------------------------------------------
|
||
* 구조물의 종방향 점유 구간(기준측점 전/후 m)
|
||
*
|
||
* 기슭막이 연장(길이·전/후)과 집수정 연장을 **한 곳에서** 정한다 — B06 링크 판정
|
||
* (`_Cross_Culvert_Wire.ts`)과 3D 스윕(`B05_Profile_UI_Corridor_Structures.ts`)이
|
||
* 서로 다른 값을 쓰면 2D와 3D가 어긋난다(2026-08-24).
|
||
* ------------------------------------------------------------------------- */
|
||
|
||
/** 기준측점 전/후 몫(m). */
|
||
export interface StructureSpan {
|
||
beforeM: number;
|
||
afterM: number;
|
||
}
|
||
|
||
/** 길이 + 전/후 저장분에서 구간을 확정 — 전/후가 없으면 길이 절반씩. */
|
||
function spanFrom(
|
||
lengthM: number,
|
||
beforeM: number | null | undefined,
|
||
afterM: number | null | undefined,
|
||
): StructureSpan {
|
||
const before = Math.max(beforeM ?? lengthM / 2, 0);
|
||
const after = Math.max(afterM ?? Math.max(lengthM - before, 0), 0);
|
||
return { beforeM: before, afterM: after };
|
||
}
|
||
|
||
/** 기슭막이 한 벽의 종방향 구간 — 기본 10m(전 5·후 5). */
|
||
export function revetSpanOfSpec(spec: CulvertSideSpec | undefined): StructureSpan {
|
||
return spanFrom(spec?.revet_length_m ?? 10, spec?.revet_before_m, spec?.revet_after_m);
|
||
}
|
||
|
||
/** 집수정의 종방향 구간 — 기본 길이 2m, 전/후 각 1m(2026-08-24 사용자). */
|
||
export function basinSpanOfSpec(spec: CulvertSideSpec | undefined): StructureSpan {
|
||
const length = Math.max(spec?.basin_length_m ?? 2, 0.1);
|
||
return spanFrom(length, spec?.basin_before_m, spec?.basin_after_m);
|
||
}
|
||
|
||
/** 역할별 옵션 키 — 배수관 레지스트리 옵션 이름 그대로다. */
|
||
export const SPAN_OPTION_KEYS: Record<SpanRole, { length: string; before: string; after: string }> =
|
||
{
|
||
inlet: {
|
||
length: "inlet_revet_length_m",
|
||
before: "inlet_revet_before_m",
|
||
after: "inlet_revet_after_m",
|
||
},
|
||
outlet: {
|
||
length: "outlet_revet_length_m",
|
||
before: "outlet_revet_before_m",
|
||
after: "outlet_revet_after_m",
|
||
},
|
||
basin: {
|
||
length: "inlet_basin_length_m",
|
||
before: "inlet_basin_before_m",
|
||
after: "inlet_basin_after_m",
|
||
},
|
||
};
|
||
|
||
/** 소유 측점의 구간값(길이·전/후) — 저장 옵션과 기본값을 함께 푼 결과. */
|
||
export const spanValuesOf = (owner: CrossSection, role: SpanRole): SpanValues | null => {
|
||
const spec = role === "outlet" ? owner.culvert?.outlet : owner.culvert?.inlet;
|
||
if (!spec) return null;
|
||
const span = role === "basin" ? basinSpanOfSpec(spec) : revetSpanOfSpec(spec);
|
||
return {
|
||
lengthM: Math.round((span.beforeM + span.afterM) * 10) / 10,
|
||
beforeM: Math.round(span.beforeM * 10) / 10,
|
||
afterM: Math.round(span.afterM * 10) / 10,
|
||
};
|
||
};
|