feat(structure): 54-36 배관 기본값 — 유입 집수정 L형 1.5 · 흙막이 돌 쌓기(찰) 10 · 1.5 · 5 · 5 · 추가 흙막이 같음 + [연동][경사] · 사면안정 흙막이 같음

- 구조물정의 판 16(판_15 이력): 유입 구조 기본 집수정 · 유출 형태 찰 · 유입 · 유출 높이 1.5 · 집수정 높이 1.5 새 칸 · 사면안정 흙막이 형태 찰 · 높이 1.5
- 엔진 짝(파이썬 · TS): 집수정 높이 빈 값 = 정의 기본값
- 횡단 기하: 자동 집수정 모양 = L형(DEFAULT_BASIN_SHAPE 한 곳) · ㄴ·ㄷ형을 못 세우는 지형만 I형
- 명세 기본값 칸(default) — 추가 흙막이 새 상자 = 찰 · 10 · 1.5 · 5 · 5 실제 값 · 이미 있는 상자 값 그대로
- B05 폼: 빈 숫자 칸 「값 (기본)」 · 빈 유입 구조 = 집수정 (기본) · 집수정 모양 L형 (기본) · 추가 흙막이 상자에도 [연동][경사]
- [연동][경사] 단추 안 여백 줄임 · 한 줄(높이 31.7 그대로)
- 시험: 기본값 바뀐 기대값 맞춤(culvert_set · structure_span · new_structure_defaults)

화면 확인(sub5_검증): 새 배관 = 관경 1000 · 집수정 · L형 (기본) · 1.5 · 유출 흙막이 찰 10 · 1.5 · 5 · 5 · 단 0 / 36+0 단 0→1 새 흙막이 1 = 찰 10 · 1.5 · 5 · 5 · 연동 · 경사 켬 / 사면안정 새 흙막이 = 찰 10 · 1.5 · 5 · 5 / 단추 여섯 31.7 한 줄 · 세션 캐시 비워 되돌림

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
This commit is contained in:
2026-10-06 14:12:29 +09:00
co-authored by Claude Opus 5.5
parent b0f7873820
commit d5d9c5f66e
15 changed files with 3832 additions and 87 deletions
+8 -4
View File
@@ -145,7 +145,8 @@ export function createSpecForm(links: SpecLinks | null, hooks: SpecFormHooks = {
input.value = values[item.id] ?? "";
return input;
}
const input = numberInput(String(item.step ?? 0.1), "0", hooks.hint?.(c, item, state) ?? "");
const hint = hooks.hint?.(c, item, state) || (item.default ? `${item.default} (기본)` : "");
const input = numberInput(String(item.step ?? 0.1), "0", hint);
if (item.kind === "auto") {
// 보여 주는 칸(54-1) — 손 입력 칸 아님
const shown = document.createElement("output");
@@ -272,12 +273,15 @@ export function createSpecForm(links: SpecLinks | null, hooks: SpecFormHooks = {
return box.root;
};
/** 밖에서 정한 개수 — 모자라면 빈 인스턴스를 늘림(늘었으면 true) · 넘친 인스턴스 · 잇기는
// 새로 서는 상자 = 명세 기본값을 칸에 실제 값으로(54-36 · 이미 있는 상자 값은 그대로)
const fresh = (c: SpecContainer): Instance =>
Object.fromEntries(c.items.flatMap((i) => (i.default ? [[i.id, i.default]] : [])));
/** 밖에서 정한 개수 — 모자라면 새 인스턴스를 늘림(늘었으면 true) · 넘친 인스턴스 · 잇기는
* 가리기만 하고 값에 남김(54-10 · 파일에 그대로). */
const fit = (c: SpecContainer, list: Instance[]): boolean => {
const n = hooks.count?.(c);
if (c.required || n === undefined || n === null || n <= list.length) return false;
while (list.length < n) list.push({});
while (list.length < n) list.push(fresh(c));
return true;
};
const shown = (c: SpecContainer, list: Instance[]): Instance[] => {
@@ -301,7 +305,7 @@ export function createSpecForm(links: SpecLinks | null, hooks: SpecFormHooks = {
add.title = "추가";
add.dataset.specAdd = c.id;
add.addEventListener("click", () => {
list.push({});
list.push(fresh(c));
paint();
hooks.onChange?.(c, c.items[0]);
});
+11 -1
View File
@@ -34,6 +34,15 @@ const BASIN_FORMS: SpecChoice[] = [
{ value: "U", label: "ㄷ형" },
];
/** 추가 흙막이 기본값(54-36 · 사면안정 흙막이 정의 기본값과 같음) — 필수 보호공 기본값은 정의 파일. */
const SOIL_ADD_DEFAULTS: Record<string, string> = {
form: "돌쌓기(찰)",
length: "10",
height: "1.5",
before: "5",
after: "5",
};
/** 형태 · 길이 · 높이 · 전 · 후 — 필수 보호공은 구조 갈래마다 옛 키 · 선택 흙막이는 인스턴스 키. */
function guardItems(keys?: Record<string, Record<string, string>>): SpecItem[] {
const by = (field: string, choices?: Record<string, SpecChoice[]>) =>
@@ -51,7 +60,7 @@ function guardItems(keys?: Record<string, Record<string, string>>): SpecItem[] {
),
}
: undefined;
return [
const items: SpecItem[] = [
{
id: "form",
label: "형태",
@@ -78,6 +87,7 @@ function guardItems(keys?: Record<string, Record<string, string>>): SpecItem[] {
by: by("after"),
},
];
return keys ? items : items.map((item) => ({ ...item, default: SOIL_ADD_DEFAULTS[item.id] }));
}
const revetKeys = (side: "inlet" | "outlet"): Record<string, string> => ({
@@ -4,14 +4,14 @@
* 관 길이는 횡단 기하 `computeStoredLayouts`(B06 · 서버 Node 진입점이 같은 함수로 `pipe_length_m` 를
* 셈 · 시험 test_b06_pipe_length) 그대로 — 여기서 새로 셈하지 않음 · 화면에 보이기만(저장 안 함).
* 높이 = 손 입력 · 횡단도 조정 높이는 B06 `withSpecDefaults` 가 같은 키(`*_revet_height_m`)에 실음 ·
* 빈 칸 안내 = 관경 기준 최소 높이(`pipeWallMinHeightM` ↔ 파이썬 `pipe_wall_min_height_m` 짝).
* 빈 칸 안내 = 정의 기본값 · 집수정 모양 기본 = 횡단 기하 `DEFAULT_BASIN_SHAPE`(54-36).
* 흙막이 개수 = 주인 측점 유출 「추가 기슭막이(단)」(횡단 설계 patch `extra_wall_counts` · 54-1) 중 지형이 세운 단(54-10) ·
* 쓰기는 횡단 보기 단 수 제어로만(`setPipeExtraCount` · 54-27).
* ========================================================================== */
import { pipeWallMinHeightM } from "../../common_util/common_util_culvert_sets";
import type { CrossSection } from "../../B06_Section/B06_Section_Api_Fetch";
import { computeStoredLayouts } from "../../B06_Section/B06_Section_Structure_Layouts";
export { DEFAULT_BASIN_SHAPE } from "../../B06_Section/B06_Section_UI_Cross_Culvert_Basin";
import { PIPE_MATCH_M } from "./design_selection";
/** 횡단 측점 목록을 대는 곳 — 횡단 보기가 자료를 받으면 걸어 둠(종단만 연 동안은 없음). */
@@ -117,12 +117,6 @@ export function pipeDrawn(chainageM: number | null): {
};
}
/** 흙막이 높이 빈 칸 안내 — 관경 기준 최소 높이(엔진이 빈 칸을 이 값으로 그림). */
export function guardHeightHint(diameterMm: string): string {
const d = Number(diameterMm || "1000") / 1000;
return `비우면 관경 기준 ${pipeWallMinHeightM(d)}`;
}
/** 관 보호공 [연동] · [경사](54-30) — null = 그 쪽을 쓸 수 없음(벽이 서는 옆 측점 없음 · 구간 없음). */
export interface PipeRevetChoice {
linked: boolean | null;
@@ -43,6 +43,8 @@ export interface SpecItem {
/** 고르기 값을 수로 저장(관경 등 옛 저장이 수). */
asNumber?: boolean;
step?: number;
/** 기본값 — 선택 컨테이너가 새로 설 때 칸에 실제 값으로 · 빈 칸 안내 「값 (기본)」(54-36). */
default?: string;
/** 빈 칸 안내 — 함수면 지금 값으로 셈. */
hint?: string | ((values: Record<string, string>) => string);
}
@@ -60,7 +60,7 @@ import {
import { FORD_SPEC, withLegacyGuards } from "../A00_Common/design/design_structure_ford";
import { fordHeightHint, fordHeightM } from "../A00_Common/design/design_structure_ford_calc";
import {
guardHeightHint,
DEFAULT_BASIN_SHAPE,
pipeDrawn,
pipeExtraCount,
pipeLengthM,
@@ -337,9 +337,16 @@ export function createFacilityOptionsForm(
? [paintTiers(values), outletChoice.paint(false)]
: c.id === "inlet"
? [inletChoice.paint(values.structure === "집수정")]
: [],
: c.id === "soil_add" // 추가 흙막이 상자도 같은 줄(54-36 · 상자마다 한 줄 · 같은 값)
? [
revetChoiceRow(
() => chainage,
() => pipeForm.refresh(),
).paint(false),
]
: [],
shown: (c, item, values, index) => {
if (c.id === "inlet" && item.id === "structure" && (!values.structure || inletIsAuto())) {
if (c.id === "inlet" && item.id === "structure" && inletIsAuto()) {
const drawn = drawnNow();
return drawn
? { value: drawn.basin ? "집수정" : "기슭막이", tag: "자동" }
@@ -350,11 +357,11 @@ export function createFacilityOptionsForm(
const basin = c.id === "inlet" && values.structure === "집수정";
const drawn = drawnNow();
const form = basin
? drawn?.basin
? (drawn?.basin ?? DEFAULT_BASIN_SHAPE)
: c.id === "soil_add"
? drawn?.extras[index]
: drawn?.forms[c.id];
return form ? { value: form, tag: basin ? "자동" : "기본" } : null;
return form ? { value: form, tag: "기본" } : null;
},
auto: (_c, item) => {
if (item.id !== "length") return "";
@@ -364,10 +371,14 @@ export function createFacilityOptionsForm(
// 흙막이 개수 = 유출 「추가 기슭막이(단)」(54-1)
count: (c) => (c.id === "soil_add" ? pipeExtraCount(chainage) : undefined),
hint: (c, item, state) => {
const soil =
c.id !== "inlet" ||
(state.inlet?.[0]?.structure === "기슭막이" && !(inletIsAuto() && drawnNow()?.basin));
return item.id === "height" && soil ? guardHeightHint(state.pipe?.[0]?.diameter ?? "") : "";
if (item.default) return ""; // 명세 기본값 안내는 폼이 그림(추가 흙막이 · 54-36)
// 빈 칸 = 정의 기본값 「값 (기본)」(54-36) — 구조가 비었으면 기본 구조의 칸
const raw = state[c.id]?.[0] ?? {};
const top = c.items.find((i) => i.id === "structure");
const values =
raw.structure || !top ? raw : { ...raw, structure: byDefault(top.key)?.value ?? "" };
const fallback = item.kind === "number" ? byDefault(branchOf(item, values)?.key) : null;
return fallback ? `${fallback.value} (기본)` : "";
},
onChange: (c, item) => {
if (c.id === "inlet" && item.id === "structure") inletAuto = null;
@@ -554,3 +554,8 @@
grid-template-columns: 1fr 1fr;
gap: 4px;
}
/* [연동] [경사] 한 줄(54-36) — 안 여백만 줄임 · 높이 그대로 */
.b05-structure__half-pair > button {
padding-inline: 4px;
white-space: nowrap;
}
+4 -2
View File
@@ -185,8 +185,10 @@ def _side_spec(
options.get("inlet_basin_after_m"),
_number(defaults.get("inlet_basin_after_m"), None),
)
# 집수정 높이(좌측 폼 · 비면 횡단도 자동) — 횡단도 집수정 벽 높이.
spec["basin_height_m"] = _number(options.get("inlet_basin_height_m"), None)
# 집수정 높이(좌측 폼 · 비면 정의 기본값) — 횡단도 집수정 벽 높이.
spec["basin_height_m"] = _number(
options.get("inlet_basin_height_m"), _number(defaults.get("inlet_basin_height_m"), None)
)
return spec
# 높이를 안 적었으면 **횡단도가 그리는 관경 기준 최소 높이**(2026-09-14 · 옛 기본 2.5 걷음).
@@ -29,16 +29,20 @@ import type {
InletStructureChoice,
} from "./B06_Section_UI_Cross_Culvert_Types";
/** 자동(기본) 집수정 모양(54-36) — 좌측 폼 「(기본)」 표시도 이 값. */
export const DEFAULT_BASIN_SHAPE: BasinShape = "L";
export function resolveBasinChoice(
choice: InletStructureChoice,
ruleReason: BasinLayout["reason"] | null,
luAllowed = true,
): { reason: BasinLayout["reason"] | null; shape: BasinShape } {
const selected = choice === "I" || choice === "L" || choice === "U";
return {
reason: choice === "revet" ? null : selected ? (ruleReason ?? "manual") : ruleReason,
// 자동(기본) 집수정은 **I형**(2026-08-23 사용자) — 종전 L형 기본은 ㄴ·ㄷ형이
// 선택지에서 숨는 지형(basinLUAllowed=false)에서 선택지 밖 구조물이 그려졌다.
shape: selected ? choice : "I",
// 자동(기본) 집수정 = **L형**(54-36) · ㄴ·ㄷ형이 선택지에서 숨는 지형(basinLUAllowed=false)은
// I형 — 선택지 밖 구조물을 그리지 않음.
shape: selected ? choice : luAllowed ? DEFAULT_BASIN_SHAPE : "I",
};
}
@@ -130,43 +130,6 @@ export function computeCulvertLayout(
const inletInvert = Math.min(groundAt(inletInfo.edge.offset_m), invertCap(inletInfo.edge));
const inlet: OffsetPoint = { offset: inletInfo.edge.offset_m, elevation: inletInvert };
// 집수정 판정: 유입 성토사면 ≤3m면 기본(절토측 포함).
const mode = section.design?.section_mode;
const inletSideName: "left" | "right" = uphill === "left" ? "left" : "right";
const inletIsCut =
mode === "both_cut" ||
(mode === "left_cut" && inletSideName === "left") ||
(mode === "right_cut" && inletSideName === "right");
const inletFillSlopeLen =
!inletIsCut && designAt
? slopeLengthAlong(
designAt,
inletInfo.edge.offset_m,
slopeToeOffset(designAt, groundAt, inletInfo.edge.offset_m, inletInfo.limit),
)
: 0;
const ruleReason: BasinLayout["reason"] | null = inletIsCut
? "cut"
: culvert.inlet.structure === "집수정"
? "cut"
: inletFillSlopeLen <= BASIN_MAX_FILL_SLOPE_M + 1e-6
? "short"
: null;
const choice: InletStructureChoice = inletStructure ?? "auto";
// 독립 기슭막이(관 숨김)는 집수정을 세우지 않는다 — 설치 측이 상단(유입 역할)일 때만
// 집수정이 서서 좌·우가 갈리던 원인(2026-08-29 사용자: 양쪽 다 성토 로직).
const { shape: basinShape, reason: choiceReason } = resolveBasinChoice(choice, ruleReason);
const basinReason = culvert.hidden_pipe ? null : choiceReason;
// ㄴ·ㄷ형은 사이즈 유지한 채 통째로 노견 끝점 일치 — 바닥(=관 invert)이 따라 오른다.
if (basinReason && basinShape !== "I") {
inlet.elevation = inletInfo.edge.elevation_m - (basinHeightM ?? basinAdjust.innerHeightM);
} else if (basinReason) {
// I형 — 관 시작점(내공 1.0m 자리) invert = 그 x의 원지반(토피 상한 이내).
const startOffset =
inlet.offset + inletInfo.outward * (BASIN_INNER_WIDTH_M + basinAdjust.lateralM);
inlet.elevation = Math.min(groundAt(startOffset), invertCap(inletInfo.edge));
}
// 벽 제원(4축): 재질이 높이 한계(메2/찰3/콘5)를 정한다. 높이 기준 = **순수 높이**
// (바닥~상단 — 2026-08-23 사용자). 하한·기본 = 관경 + 아래 0.5 + 위 0.5 —
// 관이 벽 위로 삐져나오거나 바닥에 붙으면 안 된다. 기하는 종전대로
@@ -201,6 +164,47 @@ export function computeCulvertLayout(
diameterM: diameter,
cutSlopeRatio: section.design?.cut_slope_ratio ?? 1.0,
});
// 집수정 판정: 유입 성토사면 ≤3m면 기본(절토측 포함).
const mode = section.design?.section_mode;
const inletSideName: "left" | "right" = uphill === "left" ? "left" : "right";
const inletIsCut =
mode === "both_cut" ||
(mode === "left_cut" && inletSideName === "left") ||
(mode === "right_cut" && inletSideName === "right");
const inletFillSlopeLen =
!inletIsCut && designAt
? slopeLengthAlong(
designAt,
inletInfo.edge.offset_m,
slopeToeOffset(designAt, groundAt, inletInfo.edge.offset_m, inletInfo.limit),
)
: 0;
const ruleReason: BasinLayout["reason"] | null = inletIsCut
? "cut"
: culvert.inlet.structure === "집수정"
? "cut"
: inletFillSlopeLen <= BASIN_MAX_FILL_SLOPE_M + 1e-6
? "short"
: null;
const choice: InletStructureChoice = inletStructure ?? "auto";
// 독립 기슭막이(관 숨김)는 집수정을 세우지 않는다 — 설치 측이 상단(유입 역할)일 때만
// 집수정이 서서 좌·우가 갈리던 원인(2026-08-29 사용자: 양쪽 다 성토 로직).
const { shape: basinShape, reason: choiceReason } = resolveBasinChoice(
choice,
ruleReason,
inletOptions.basinLUAllowed,
);
const basinReason = culvert.hidden_pipe ? null : choiceReason;
// ㄴ·ㄷ형은 사이즈 유지한 채 통째로 노견 끝점 일치 — 바닥(=관 invert)이 따라 오른다.
if (basinReason && basinShape !== "I") {
inlet.elevation = inletInfo.edge.elevation_m - (basinHeightM ?? basinAdjust.innerHeightM);
} else if (basinReason) {
// I형 — 관 시작점(내공 1.0m 자리) invert = 그 x의 원지반(토피 상한 이내).
const startOffset =
inlet.offset + inletInfo.outward * (BASIN_INNER_WIDTH_M + basinAdjust.lateralM);
inlet.elevation = Math.min(groundAt(startOffset), invertCap(inletInfo.edge));
}
/** 한계에 잘린 뒤의 실제 조작값 — 조정창이 되받는다(높이는 순수 높이 기준). */
const appliedAdjust = {
inlet: { ...adjInlet, h: adjInlet.h != null ? inletWallSpec.pureHeight : null },
+4 -1
View File
@@ -108,7 +108,10 @@ function sideSpec(
num(defaults.inlet_basin_before_m, null),
);
spec.basin_after_m = num(options.inlet_basin_after_m, num(defaults.inlet_basin_after_m, null));
spec.basin_height_m = num(options.inlet_basin_height_m, null);
spec.basin_height_m = num(
options.inlet_basin_height_m,
num(defaults.inlet_basin_height_m, null),
);
return spec;
}
+16 -7
View File
@@ -1,5 +1,5 @@
{
"판": 15,
"판": 16,
"schema_version": 1,
"comment": "B05 구조물 타입 레지스트리 정본. 근거·옵션 상세는 PLAN.md 구조물 리스트와 resources/knowledge/technical_info/01_임도 참조. 2026-08-17 선택지 재편: B군(종단배수)·F군(생태/녹화)·G군(노면공)은 enabled:false 보존 — B05 선택지에서 빠지고 B06 개별 횡단도 옵션으로 재사용한다. 바닥막이(bed_sill)·생태연못(eco_pond)은 임도 미사용 확정으로 삭제. 배관·BOX암거·물넘이·세월교는 pipe_points.json 정본 관리(managed_by) — structures.json에 저장하지 않으며, 배관 부속(유형/집수정/기슭막이/돌붙임)·날개벽 옵션 정의는 B06/B07 필수 승격의 선언 근거다. ★기본값 원칙(2026-08-16 크로스체크 2차): default를 두는 것은 ①법정이 등급과 무관하게 못박은 단일 수치 ②사용자가 개발 단계에서 확정한 값 ③도메인 수치가 아닌 표시용 문자열, 이 셋뿐이다. 재료·형식 같은 설계자 판단 선택지와 미확정 제원은 default 없이 required로 두어 사용자가 직접 고르게 한다 (pipe_diameter_mm 1000 = 별표2 원칙값+사용자 확정 — 화이트리스트 등재). BOX암거 본체 2.0×2.0·날개벽 유입/유출 있음·짧은쪽 높이 1m·길이 2m·각도 45°는 2026-08-17 사용자 확정값이다 — 본체 규격은 실무 관측 규격(2.0×2.0·3.0×3.0)을 폼에서 프리셋 select로 받고 필요 시 사용자 지정으로 자유 입력하되, 저장 키는 body_width_m·body_height_m 숫자 그대로다(프리셋 전용 키를 만들지 않는다). 이 값들은 B05 서브폼이 실제로 받으므로 phase를 detail로 두지 않는다. ★보호공 개편(2026-08-17 사용자 지시): 구 `*_pitching`(있음/없음) + `*_pitching_finish`(찰/메) 두 축을 `*_protection` 한 축(돌붙임(찰)·돌붙임(메)·도수로)으로 합쳤다 — 도수로·산비탈수로(B4)는 유출부에서 돌붙임 대신 쓰는 공종이라 별도 구조물이 아니라 보호공 선택지로 받는다. \"없음\"은 삭제(물이 흐르는 자리라 보호공 필수). 치수는 배타 — 돌붙임이면 `*_protection_area_m2`(10㎡), 도수로면 `*_protection_width_m`(1m, 지식DB 수치 근거 없음·사용자 확정, 실무 확인 후 개선). 구 저장분은 `common_util_drainage_pipes.parse_pipe_points`가 새 키로 옮긴다.",
"구조물군": {
@@ -75,7 +75,7 @@
"기슭막이",
"집수정"
],
"default": "기슭막이",
"default": "집수정",
"required": false
},
{
@@ -147,6 +147,15 @@
"required": false,
"phase": "detail"
},
{
"key": "inlet_basin_height_m",
"label": "집수정 높이",
"input": "number",
"unit": "m",
"default": 1.5,
"required": false,
"phase": "detail"
},
{
"key": "inlet_revet_form",
"label": "유입 기슭막이 형태",
@@ -194,7 +203,7 @@
"label": "유입 기슭막이 높이",
"input": "number",
"unit": "m",
"default": null,
"default": 1.5,
"required": false,
"phase": "detail",
"empty_means": "비우면 관경 기준 최소 높이(관경 + 여유 0.5 를 0.1m 로 올림 + 근입 0.5)로 그리고 셈 — 횡단도·구조물도·표가 같은 값 · 최소라 수량은 미확정(금액 밖) · 2026-09-14 브레인 판정(옛 기본 2.5 걷음)"
@@ -221,7 +230,7 @@
"통나무·목재틀",
"바자"
],
"default": "돌쌓기(메)",
"default": "돌쌓기(찰)",
"required": false,
"phase": "detail"
},
@@ -256,7 +265,7 @@
"label": "유출 기슭막이 높이",
"input": "number",
"unit": "m",
"default": null,
"default": 1.5,
"required": false,
"phase": "detail",
"empty_means": "비우면 관경 기준 최소 높이(관경 + 여유 0.5 를 0.1m 로 올림 + 근입 0.5)로 그리고 셈 — 횡단도·구조물도·표가 같은 값 · 최소라 수량은 미확정(금액 밖) · 2026-09-14 브레인 판정(옛 기본 2.5 걷음)"
@@ -1547,7 +1556,7 @@
"통나무·목재틀",
"바자"
],
"default": null,
"default": "돌쌓기(찰)",
"required": false,
"empty_means": "비우면 통나무·목재틀로 그림(횡단 기본 형태) — B05 로 옮긴 칸이라 필수로 안 막음(옛 흙막이 저장 보호)"
},
@@ -1565,7 +1574,7 @@
"label": "높이",
"input": "number",
"unit": "m",
"default": 2.5,
"default": 1.5,
"required": false,
"phase": "b05"
},
File diff suppressed because it is too large Load Diff
@@ -35,7 +35,7 @@ def test_새_관만_기본값_빈_키에만():
basins._apply_new_defaults([fresh])
expected = {**_defaults("pipe"), "pipe_diameter_mm": 800} # 추천(먼저 들어간 값)이 이김
assert fresh.options == expected
assert fresh.options["pipe_kind"] == "파형강관" and fresh.options["inlet_type"] == "기슭막이"
assert fresh.options["pipe_kind"] == "파형강관" and fresh.options["inlet_type"] == "집수정"
box = PipePoint(chainage_m=20.0, source="stream", facility="box_culvert")
basins._apply_new_defaults([box])
assert box.options["body_width_m"] == 2.0 and box.options["wing_in"] == "날개벽"
+13 -13
View File
@@ -43,18 +43,18 @@ def test_empty_options_fall_back_to_registry_defaults():
assert spec["type"] == "pipe"
assert spec["diameter_m"] == 1.0
assert spec["min_cover_m"] == MIN_PIPE_COVER_M
for side in ("inlet", "outlet"):
part = spec[side]
assert part["structure"] == "기슭막이"
assert part["revet_height_m"] == 2.0
assert part["revet_length_m"] == 10
assert part["face_slope"] == REVET_FACE_SLOPE
# 보호공 = 낙차고(2.0) × 2 = 4.0m, 두께 0.45m(사용자 확정).
assert part["apron_length_m"] == 4.0
assert part["apron_thickness_m"] == 0.45
# 레지스트리 기본 형태: 유입 찰 / 유출 메.
assert spec["inlet"]["revet_form"] == "돌쌓기(찰)"
assert spec["outlet"]["revet_form"] == "돌쌓기(메)"
# 54-36 기본: 유입 = 집수정(높이 1.5) · 유출 = 흙막이 찰 · 높이 1.5
assert spec["inlet"]["structure"] == "집수정"
assert spec["inlet"]["basin_height_m"] == 1.5
part = spec["outlet"]
assert part["structure"] == "기슭막이"
assert part["revet_height_m"] == 1.5
assert part["revet_length_m"] == 10
assert part["face_slope"] == REVET_FACE_SLOPE
# 보호공 = 낙차고(1.5) × 2 = 3.0m, 두께 0.45m(사용자 확정).
assert part["apron_length_m"] == 3.0
assert part["apron_thickness_m"] == 0.45
assert part["revet_form"] == "돌쌓기(찰)"
def test_basin_inlet_skips_revet_and_apron():
@@ -65,7 +65,7 @@ def test_basin_inlet_skips_revet_and_apron():
assert "apron_length_m" not in spec["inlet"]
# 유출측은 그대로 세트를 갖춘다.
assert spec["outlet"]["structure"] == "기슭막이"
assert spec["outlet"]["apron_length_m"] == 4.0 # 관경 기준 높이 2.0 × 2
assert spec["outlet"]["apron_length_m"] == 3.0 # 정의 기본 높이 1.5 × 2(54-36)
def test_string_diameter_and_custom_height():
+1 -1
View File
@@ -61,7 +61,7 @@ def test_basin_before_after_follow_saved_options():
def test_revet_span_defaults_unchanged():
"""기슭막이 기본은 그대로 길이 10m·전/후 5·5다(이번 변경이 건드리지 않는다)."""
culvert = _culvert_set(None)
culvert = _culvert_set({"inlet_type": "기슭막이"})
for role in ("inlet", "outlet"):
spec = culvert[role]
assert spec["revet_length_m"] == 10.0