2026-08-28 사용자 확정 스펙. 물넘이포장 — 노면을 판 자리로 그린다 - 서버가 _ford_pavement_set(월류 폭·월류 높이·바닥 경사)을 만들고 물넘이만 span 연동을 켜 **범위 안 측점 전부**에 얹는다. 깊이가 없으면 None으로 두어 화면이 그리지 않는다(수치를 지어내지 않는다). - 횡단도: 기존 계획고 점선 + 물넘이 바닥 실선 + 진한 회색 빗금 포장. 깊이는 노선 중심 기준, 바닥은 유입(상단측)이 높게 기운다. 경사를 비우면 그 측점의 노면 횡단경사를 쓴다. - 물넘이 폼에 "바닥 경사 유입→유출(%)" 칸을 추가하고, 월류 폭 기본값 리터럴을 config_frontend 상수로 모아 서버 값과 짝지었다. 포장 — 사용자가 구간으로 지정한다 - 구조물 레지스트리 G군 pavement_concrete 를 되살려 기준측점 + 길이 + 전/후로 받는다(기슭막이와 같은 폼). 길이 기본값은 0 = 미지정. - pavement_ranges/paved_at 이 구간을 판정하고, enforce_pavement_ranges 가 저장분이 비포장이어도 구간 안이면 포장으로 다시 계산한다(사용자 조작값 승계). - **종단경사 자동 포장 적용을 없앴다** — paved=suggested 3곳 제거. 별표1-2 상한 초과 경고(pavement_suggested 배지·근거 문구)는 그대로 남는다. - 포장 구간이 물넘이를 통째로 품으면 모달로 알리고 앞/뒤로 나눠 저장하고, 끝만 걸치면 값을 고치라고 안내하고 멈춘다. 700줄 제한: _compute_default_designs 를 Router_Design 으로 옮겼다(657/306줄). 검증: pytest 238 passed / 7 skipped(신규 9건). 공용 브라우저 실측 — 물넘이 임시 투입 시 240m 카드에만 파임 3요소가 그려지고(일반 포장 박스 0), 바닥이 계획고보다 0.4m 아래·노면 전폭 4.0m에 1.5% 기울기, 220·260은 비포장 유지. 겹침 규칙은 브라우저에서 모듈을 직접 불러 3분할·안내·취소를 확인했다. 실측용 임시 관 지점은 매번 원래 정본 4건으로 복구했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
173 lines
7.4 KiB
TypeScript
173 lines
7.4 KiB
TypeScript
/* =============================================================================
|
|
* config_frontend.ts
|
|
* 프론트엔드 제어 및 웹 환경 설정 (API 주소, 업로드 제한, 라우트 등)
|
|
*
|
|
* 규칙:
|
|
* - 프론트엔드의 모든 제어 상수는 여기서 관리. 컴포넌트 내 하드코딩 금지.
|
|
* - API 주소, 파일 제한, WebCAD 렌더 옵션 등을 중앙 집중.
|
|
* ========================================================================== */
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* 1. API 통신
|
|
* -------------------------------------------------------------------------- */
|
|
/** 백엔드 API 기본 경로. vite proxy(/api → :8000)를 통해 FastAPI로 전달. */
|
|
export const API_BASE_URL = "/api";
|
|
|
|
/** API 요청 타임아웃 (ms) */
|
|
export const API_TIMEOUT_MS = 30_000;
|
|
|
|
/** 격자 해석처럼 수십 초가 걸리는 분석 요청용 타임아웃 (ms).
|
|
* 일반 요청에 이 값을 쓰면 장애 시 화면이 오래 멈추므로 분석 엔드포인트에만 쓴다. */
|
|
export const API_ANALYSIS_TIMEOUT_MS = 60_000;
|
|
|
|
/** B03~B09 워크플로우에서 사용할 현재 프로젝트 UUID 저장 키 */
|
|
export const CURRENT_PROJECT_ID_KEY = "frd_current_project_id";
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* 2. 파일 업로드 제한 (B03_FileInput)
|
|
* -------------------------------------------------------------------------- */
|
|
/** 단일 파일 최대 크기 (MB) */
|
|
export const UPLOAD_MAX_MB = 30 * 1024;
|
|
|
|
/** 한 요청에서 선택 가능한 최대 파일 수 */
|
|
export const UPLOAD_MAX_FILES = 5;
|
|
|
|
/** 청크 업로드 단위 (MB) */
|
|
export const UPLOAD_CHUNK_SIZE_MB = 1024;
|
|
|
|
/** 업로드 진행 UI 갱신 주기 (ms) */
|
|
export const PROGRESS_UPDATE_INTERVAL_MS = 10_000;
|
|
|
|
/** B03 업로드 Service Worker 번들 경로 */
|
|
export const SERVICE_WORKER_PATH = "/assets/B03_FileInput_ServiceWorker.js";
|
|
|
|
/** 허용 확장자 (계획노선/지형/포인트클라우드/도면) */
|
|
export const UPLOAD_ALLOWED_EXT = [".csv", ".las", ".laz", ".tif", ".tfw", ".prj", ".dxf"] as const;
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* 3. WebCAD / 3D 렌더링 옵션
|
|
* -------------------------------------------------------------------------- */
|
|
export const RENDER_OPTIONS = {
|
|
/** Three.js 배경색 (theme.css 변수와 별개로 캔버스 clear color) */
|
|
canvasClearColorLight: 0xf6f7fa,
|
|
canvasClearColorDark: 0x16121f,
|
|
pointColor: 0x3e0079,
|
|
gridCenterColor: 0x9491a1,
|
|
gridLineColor: 0xe6e2e3,
|
|
/** 카메라 기본 시야각 */
|
|
cameraFov: 60,
|
|
/** 카메라 near/far */
|
|
cameraNear: 0.1,
|
|
cameraFar: 100_000,
|
|
/** 안티앨리어싱 사용 여부 */
|
|
antialias: true,
|
|
/** 픽셀 비율 상한 (고DPI 성능 보호) */
|
|
maxPixelRatio: 2,
|
|
} as const;
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* 4. 라우트 상수 (해시 기반 SPA 라우팅)
|
|
* 각 페이지 폴더명과 1:1 매핑.
|
|
* -------------------------------------------------------------------------- */
|
|
export const ROUTES = {
|
|
// 로그인 전
|
|
A01_HOME: "a01-home",
|
|
A02_PROG_DETAIL: "a02-prog-detail",
|
|
A03_COMP_DETAIL: "a03-comp-detail",
|
|
A04_NEWS_HISTORY: "a04-news-history",
|
|
A05_EDU_DETAIL: "a05-edu-detail",
|
|
A06_LOGIN: "a06-login",
|
|
A07_REGISTER: "a07-register",
|
|
A08_SUPPORT: "a08-support",
|
|
A09_SECURITY: "a09-security",
|
|
// 로그인 후
|
|
B01_ACCOUNT: "b01-account",
|
|
B02_PROJ_REGISTER: "b02-proj-register",
|
|
B03_FILE_INPUT: "b03-file-input",
|
|
B04_PREPROCESS: "b04-preprocess",
|
|
B05_PROFILE: "b05-profile",
|
|
B06_SECTION: "b06-section",
|
|
B07_QUANTITY: "b07-quantity",
|
|
B08_DESIGN_DETAIL: "b08-design-detail",
|
|
B09_ESTIMATION: "b09-estimation",
|
|
B10_PAYMENT: "b10-payment",
|
|
B11_STATUS: "b11-status",
|
|
// 대시보드에서 B그룹으로 처음 들어갈 때 3D·등고선을 미리 받아 두는 준비 화면.
|
|
B11_LOADING: "b11-loading",
|
|
} as const;
|
|
|
|
export type RouteKey = keyof typeof ROUTES;
|
|
export type RoutePath = (typeof ROUTES)[RouteKey];
|
|
|
|
/** 기본 진입 라우트 */
|
|
export const DEFAULT_ROUTE: RoutePath = ROUTES.A01_HOME;
|
|
|
|
/** 로그인이 필요한 라우트 (인증 가드용) */
|
|
export const PROTECTED_ROUTES: readonly RoutePath[] = [
|
|
ROUTES.B01_ACCOUNT,
|
|
ROUTES.B02_PROJ_REGISTER,
|
|
ROUTES.B03_FILE_INPUT,
|
|
ROUTES.B04_PREPROCESS,
|
|
ROUTES.B05_PROFILE,
|
|
ROUTES.B06_SECTION,
|
|
ROUTES.B07_QUANTITY,
|
|
ROUTES.B08_DESIGN_DETAIL,
|
|
ROUTES.B09_ESTIMATION,
|
|
ROUTES.B10_PAYMENT,
|
|
ROUTES.B11_STATUS,
|
|
ROUTES.B11_LOADING,
|
|
];
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* 5. 테마 / 언어 저장 키
|
|
* -------------------------------------------------------------------------- */
|
|
export const THEME_STORAGE_KEY = "frd_theme";
|
|
export const LANG_STORAGE_KEY = "frd_lang";
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* 6. B05 구조물 옵션 (드롭다운 목록·기본값)
|
|
* 정본은 config/config_system.py의 STRUCTURE_* / PIPE_* 상수 — 값을 바꿀 때는
|
|
* 양쪽을 함께 고친다(2026-08-05 사용자 확정. 계산은 백엔드, 표시는 프론트).
|
|
* -------------------------------------------------------------------------- */
|
|
export const STRUCTURE_TYPES = ["배관", "기성막이", "대피로", "기타"] as const;
|
|
export type StructureType = (typeof STRUCTURE_TYPES)[number];
|
|
export const STRUCTURE_DEFAULT_TYPE: StructureType = "배관";
|
|
|
|
/** 관종별 직경(mm) 목록. */
|
|
export const PIPE_DIAMETERS_MM: Record<string, readonly number[]> = {
|
|
이중벽관: [150, 200, 250, 300, 400, 450, 500, 600, 700, 800, 900, 1000, 1200, 1500],
|
|
삼중벽관: [200, 250, 300, 400, 450, 500, 600, 700, 800, 900, 1000, 1200, 1500],
|
|
파형강관: [
|
|
150, 200, 250, 300, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1200, 1350, 1500, 1650, 1800,
|
|
2000,
|
|
],
|
|
} as const;
|
|
export const PIPE_TYPES = Object.keys(PIPE_DIAMETERS_MM);
|
|
export const PIPE_DEFAULT_TYPE = "파형강관";
|
|
/** 자동 지정 기본 관경 — 별표2 (나) 예외 하한 800mm. 유효직경이 더 크면 바로 위 규격. */
|
|
export const PIPE_DEFAULT_DIAMETER_MM = 800;
|
|
|
|
/** 월류 폭 기본값(m) — 세월교 10m·물넘이포장 5m (2026-08-18 사용자 확정, 지식DB 근거 없음).
|
|
* 백엔드 `B06_Section_Engine_Culvert.FORD_DEFAULT_WIDTH_M`·`FORD_PAVEMENT_DEFAULT_WIDTH_M`과
|
|
* 같은 값이어야 한다. */
|
|
export const FORD_BRIDGE_DEFAULT_WIDTH_M = 10;
|
|
export const FORD_PAVEMENT_DEFAULT_WIDTH_M = 5;
|
|
|
|
/** 물넘이 개수로 조도계수 — KDS 표 2.7-1 콘크리트 수로 "보통"값이자 실무 물넘이 관측치.
|
|
* 백엔드 `config_system.FORD_MANNING_N`과 같은 값이어야 한다. */
|
|
export const FORD_MANNING_N = 0.017;
|
|
/** 물넘이 수리계산 경사 — 실무 수리계산서 역산값 10%(2026-08-17 확인).
|
|
* 백엔드 `config_system.FORD_SLOPE`와 같은 값이어야 한다. */
|
|
export const FORD_SLOPE = 0.1;
|
|
|
|
export const ESCAPE_ROUTE_WIDTHS_M = [1.5, 2.0, 2.5, 3.0] as const;
|
|
export const ESCAPE_ROUTE_DEFAULT_WIDTH_M = 2.0;
|
|
export const STRUCTURE_ETC_DEFAULT_NAME = "기타 구조물";
|
|
|
|
/** 유효직경(mm) 이상인 가장 작은 규격을 고른다. 목록을 넘으면 최대 규격. */
|
|
export function pickPipeDiameter(pipeType: string, effectiveDiameterMm: number | null): number {
|
|
const sizes = PIPE_DIAMETERS_MM[pipeType] ?? PIPE_DIAMETERS_MM[PIPE_DEFAULT_TYPE];
|
|
const need = Math.max(PIPE_DEFAULT_DIAMETER_MM, effectiveDiameterMm ?? 0);
|
|
return sizes.find((size) => size >= need) ?? sizes[sizes.length - 1];
|
|
}
|