refactor(472-3 B06 구조물): 관 · BOX · 세월교 · 물넘이 모양 계산을 logic 으로 옮김(518-2)

- logic 새 파일 9 — B06_Section_Cross_Culvert · _Culvert_Geom · _Culvert_Extra · _Culvert_Tiers ·
  _Culvert_Types · _Culvert_Wire · _Box_Geom · _Ford_Geom · _Ford_Pavement
- 그림 파일(UI_Cross_Culvert · _Culvert_Types · _Box · _Ford · _Ford_Pavement)은 받은 좌표로 그리기만 ·
  관 겹 꼭짓점(pipeShellCorners) · 물넘이 노면 끝 · 월류선 · 유출 성토선은 logic 으로
- 옛 순수 기하 6 파일(UI_Cross_Culvert_Geom · _Extra · _Tiers · _Wire · _Box_Geom · _Ford_Geom) 지움 ·
  부르는 쪽(B05 Corridor · Structure_Layouts · Route_Carry_Patch · Wall 등)은 import 줄만
- test_472_3 GEOMETRY_PULLS 자기 11 줄 + 더는 안 끌리는 Design_Surface 1 줄 걷음
- 시험 · helper 경로를 새 logic 파일로 — 옮긴 파일을 가리키던 낡은 경로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 19:17:23 +09:00
co-authored by Claude Opus 5.5
parent 1d5d63875b
commit e4befd158a
79 changed files with 685 additions and 639 deletions
+1 -1
View File
@@ -122,7 +122,7 @@ const resolve = (base) =>
const B06 = (f) => path.join(ROOT, "B06_Section", `${f}.ts`);
const { computeStoredLayouts } = loadTs(B06("B06_Section_Structure_Layouts"));
const { appendCulvertOverlay } = loadTs(B06("B06_Section_UI_Cross_Culvert"));
const { wallStandsAt } = loadTs(B06("B06_Section_UI_Cross_Culvert_Wire"));
const { wallStandsAt } = loadTs(B06("logic/B06_Section_Cross_Culvert_Wire"));
const { applyStructurePick } = loadTs(B06("B06_Section_UI_Page_Structure_Pick"));
const fixture = JSON.parse(
@@ -33,9 +33,9 @@ const { buildCorridorStructures } = loadTs(B05("B05_Profile_UI_Corridor_Structur
const { structureHashParts } = loadTs(B05("logic/B05_Profile_UI_Corridor_Structures_Hash"));
const { structureTopLoops } = loadTs(B05("logic/B05_Profile_UI_Corridor_Tin_Design"));
const { chuteBandOutline, isChuteSegment } = loadTs(B06("B06_Section_UI_Cross_Wall_Hatch"));
const { computeBoxLayout } = loadTs(B06("B06_Section_UI_Cross_Box_Geom"));
const { computeBoxLayout } = loadTs(B06("logic/B06_Section_Cross_Box_Geom"));
const { computeFordLayout } = loadTs(B06("B06_Section_UI_Cross_Ford"));
const { outletFillLine } = loadTs(B06("B06_Section_UI_Cross_Ford_Pavement"));
const { outletFillLine } = loadTs(B06("logic/B06_Section_Cross_Ford_Pavement"));
const fixture = JSON.parse(
fs.readFileSync(path.join(__dirname, "fixtures", "b06_73_4_body_guard_sections.json"), "utf8"),
@@ -27,7 +27,9 @@ const resolve = (base) =>
const B06 = (f) => path.join(ROOT, "B06_Section", f);
const { freezeLinkedWalls } = loadTs(B06("B06_Section_UI_Page_Link_Session.ts"));
const { computeCardCulvert, culvertLinkFor } = loadTs(B06("B06_Section_UI_Cross_Culvert_Wire.ts"));
const { computeCardCulvert, culvertLinkFor } = loadTs(
B06("logic/B06_Section_Cross_Culvert_Wire.ts"),
);
const sections = JSON.parse(
fs.readFileSync(path.join(__dirname, "fixtures", "b06_73_4_body_guard_sections.json"), "utf8"),
+1 -1
View File
@@ -19,7 +19,7 @@ const { loadTs } = makeLoader({
});
const { computeFordLayout, fordAdjustOf } = loadTs(
path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Ford_Geom.ts"),
path.join(ROOT, "B06_Section", "logic/B06_Section_Cross_Ford_Geom.ts"),
);
const sections = JSON.parse(
fs.readFileSync(path.join(__dirname, "fixtures", "b06_73_4_body_guard_sections.json"), "utf8"),
+1 -1
View File
@@ -30,7 +30,7 @@ const { computeCrossDesign } = loadTs(
path.join(ROOT, "common_util", "common_util_cross_design.ts"),
);
const { computeCulvertLayout, REVET_EMBED_DEPTH_M } = loadTs(
path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Culvert_Geom.ts"),
path.join(ROOT, "B06_Section", "logic/B06_Section_Cross_Culvert_Geom.ts"),
);
const { cutLength } = loadTs(
path.join(ROOT, "B06_Section", "logic", "B06_Section_UI_Cross_Culvert_Solve.ts"),
@@ -31,7 +31,7 @@ const resolve = (base) =>
const B06 = (f) => path.join(ROOT, "B06_Section", f);
const { computeStoredLayouts, structureAreaRows } = loadTs(B06("B06_Section_Structure_Layouts.ts"));
const { computeCulvertLayout } = loadTs(B06("B06_Section_UI_Cross_Culvert_Geom.ts"));
const { computeCulvertLayout } = loadTs(B06("logic/B06_Section_Cross_Culvert_Geom.ts"));
const read = (name) =>
JSON.parse(fs.readFileSync(path.join(__dirname, "fixtures", name), "utf8")).cross_sections;
const clone = (v) => JSON.parse(JSON.stringify(v));
+1 -1
View File
@@ -82,7 +82,7 @@ const resolve = (base) =>
const B06 = (f) => path.join(ROOT, "B06_Section", `${f}.ts`);
const { computeStoredLayouts } = loadTs(B06("B06_Section_Structure_Layouts"));
const { appendCulvertOverlay } = loadTs(B06("B06_Section_UI_Cross_Culvert"));
const { outlineBox } = loadTs(B06("B06_Section_UI_Cross_Culvert_Types"));
const { outlineBox } = loadTs(B06("logic/B06_Section_Cross_Culvert_Types"));
const fixture = JSON.parse(
fs.readFileSync(path.join(__dirname, "fixtures", "b07_cross_overlay_sections.json"), "utf8"),
+1 -1
View File
@@ -62,7 +62,7 @@ const { loadTs } = makeLoader({
});
const B06 = (name) => loadTs(path.join(ROOT, "B06_Section", `${name}.ts`));
const { computeCulvertLayout } = B06("B06_Section_UI_Cross_Culvert_Geom");
const { computeCulvertLayout } = B06("logic/B06_Section_Cross_Culvert_Geom");
const { cutLength } = B06("logic/B06_Section_UI_Cross_Culvert_Solve");
const { pipeLengthText, pipeLengthWhole } = B06("logic/B06_Section_UI_Cross_Culvert_Const");
const { appendPipeInfoText } = B06("B06_Section_UI_Cross_Culvert_Types");
@@ -115,9 +115,9 @@ const STUBS = {
"B06_Section_Cross_Design_Session.ts": anyModule(),
"common_util_cross_design.ts": anyModule(),
"B06_Section_Structure_Layouts.ts": anyModule({ computeStoredLayouts: () => null }),
"B06_Section_UI_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_UI_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_UI_Cross_Box_Geom.ts": anyModule(),
"B06_Section_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_Cross_Box_Geom.ts": anyModule(),
"B06_Section_UI_Cross_View.ts": anyModule(),
"B06_Section_UI_Section_Common.ts": anyModule({ designElevationAt: () => undefined }),
"B06_Section_Display_Width.ts": anyModule({ displayHalfWidth: () => 10 }),
@@ -29,6 +29,9 @@ const ford = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Ford_Pa
{ "./logic/B06_Section_UI_Cross_Culvert_Const": {} },
),
"./B06_Section_UI_Cross_Design_Surface": { appendPavementOverlay() {} },
"./logic/B06_Section_Cross_Ford_Pavement": loadTs(
path.join(ROOT, "B06_Section", "logic", "B06_Section_Cross_Ford_Pavement.ts"),
),
// 관 신원표(323-5 · 472-2 에서 common_util 로) — 진짜 · 장부 안 꽂음 = 옛 자리 이름
"../common_util/common_util_pick_tag": loadTs(
path.join(ROOT, "common_util", "common_util_pick_tag.ts"),
@@ -73,6 +73,7 @@ class Node {
}
globalThis.document = { createElementNS: (_ns, tag) => new Node(tag) };
const pave = load(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Ford_Pavement.ts"));
const paveGeo = load(path.join(ROOT, "B06_Section", "logic", "B06_Section_Cross_Ford_Pavement.ts"));
// 서버가 지대 낮은 쪽(우)으로 5 % 기울인 노면 · 노견 끝 ±3 · 차도 끝 ±2.5
const tilted = {
section_mode: "left_cut",
@@ -244,7 +245,7 @@ const out = {
drew,
pavePoints: ofClass("b06-chart__ford-pavement").map((k) => k.attrs.points),
planPoints: ofClass("b06-chart__ford-deck-plan").map((k) => k.attrs.points),
inflowLeft: pave.inflowLeft(tilted),
inflowLeft: paveGeo.inflowLeft(tilted),
// 287-3 월류 폭 → 높이 = 세월교와 같은 풀이(유량 1.2 ㎥/s · 폭 10 m) · 유량 없으면 null(손 입력)
heightByWidth: calc.fordHeightM(1.2, 10),
heightNoFlow: calc.fordHeightM(null, 10),
@@ -44,6 +44,7 @@ globalThis.document = { createElementNS: (_ns, tag) => new Node(tag) };
const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const pave = load(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Ford_Pavement.ts"));
const paveGeo = load(path.join(ROOT, "B06_Section", "logic", "B06_Section_Cross_Ford_Pavement.ts"));
const design = input.design; // 서버가 낸 양측 절토 · 낮은 쪽 연장 설계
const svg = new Node("g");
pave.appendFordPavementOverlay(
@@ -70,18 +71,18 @@ const fordPave = load(
);
const out = {
edges: pave.fordEdges(design),
edges: paveGeo.fordEdges(design),
pavePoints: paveLayer?.attrs.points ?? null,
runNone: pts(hatch.chuteRun(slope)),
runShort: pts(hatch.chuteRun(slope, 2)),
runLong: pts(hatch.chuteRun(slope, 7, flat)),
runPointOnly: pts(hatch.chuteRun([{ points: [P(3, -4)] }])),
runPointLength: pts(hatch.chuteRun([{ points: [P(3, -4)] }], 2, flat)),
dipLine: pave.outletFillLine(design, dipGround),
dipLine: paveGeo.outletFillLine(design, dipGround),
dipChute: pave.fordChuteLine(design, {}, dipGround),
dipChuteLength: pave.fordChuteLine(design, { length_m: 1.5 }, dipGround),
fillLine: pave.outletFillLine(input.fillDesign, fillGround),
fillLineOld: pave.outletFillLine(input.fillDesign),
fillLine: paveGeo.outletFillLine(input.fillDesign, fillGround),
fillLineOld: paveGeo.outletFillLine(input.fillDesign),
fillChuteLength: pave.fordChuteLine(input.fillDesign, { length_m: 1 }, fillGround),
chuteItems: guard.CHUTE_ITEMS.map((i) => i.id),
fordPaveChuteSame:
@@ -44,6 +44,7 @@ globalThis.document = { createElementNS: (_ns, tag) => new Node(tag) };
const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const pave = load(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Ford_Pavement.ts"));
const paveGeo = load(path.join(ROOT, "B06_Section", "logic", "B06_Section_Cross_Ford_Pavement.ts"));
const hatch = load(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Wall_Hatch.ts"));
const fillGround = hatch.groundLine(input.fillSamples);
const dipGround = hatch.groundLine(input.dipSamples);
@@ -73,10 +74,10 @@ const out = {
fillLong: chute(input.fillDesign, fillGround, 6),
dipNone: chute(input.dipDesign, dipGround),
dipLong: chute(input.dipDesign, dipGround, 2),
fillOverflow: pave.overflowLine(input.fillDesign, 0.3),
dipOverflow: pave.overflowLine(input.dipDesign, 0.3),
fillEdges: pave.fordEdges(input.fillDesign),
dipEdges: pave.fordEdges(input.dipDesign),
fillOverflow: paveGeo.overflowLine(input.fillDesign, 0.3),
dipOverflow: paveGeo.overflowLine(input.dipDesign, 0.3),
fillEdges: paveGeo.fordEdges(input.fillDesign),
dipEdges: paveGeo.fordEdges(input.dipDesign),
deckPoints: deck?.attrs.points ?? null,
bandPoints: band?.attrs.points ?? null,
};
+1 -1
View File
@@ -62,7 +62,7 @@ const { loadTs } = makeLoader({
});
const { computeCulvertLayout } = loadTs(
path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Culvert_Geom.ts"),
path.join(ROOT, "B06_Section", "logic/B06_Section_Cross_Culvert_Geom.ts"),
);
const { appendCulvertOverlay } = loadTs(
path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Culvert.ts"),
@@ -46,7 +46,7 @@ const B06 = (f) => path.join(ROOT, "B06_Section", `${f}.ts`);
const { structureAreaRows, computeStoredLayouts } = loadTs(B06("B06_Section_Structure_Layouts"));
const { layPipeOptions } = loadTs(B06("B06_Section_Section_Adjusts"));
const { applyPipeOptionsToCache } = loadTs(B06("B06_Section_UI_Page_Pipe_Options"));
const { ZERO_ADJUST } = loadTs(B06("B06_Section_UI_Cross_Culvert_Types"));
const { ZERO_ADJUST } = loadTs(B06("logic/B06_Section_Cross_Culvert_Types"));
const fresh = () =>
JSON.parse(fs.readFileSync(path.join(__dirname, "fixtures", "b06_186_1_pipe_292.json"), "utf8"))
@@ -87,9 +87,9 @@ const STUBS = {
"B06_Section_UI_Standard_Panel.ts": anyModule(),
"B06_Section_Cross_Design_Session.ts": anyModule(),
"common_util_cross_design.ts": anyModule(),
"B06_Section_UI_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_UI_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_UI_Cross_Box_Geom.ts": anyModule(),
"B06_Section_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_Cross_Box_Geom.ts": anyModule(),
"B06_Section_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
"B05_Profile_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
"config_frontend.ts": anyModule({ PIPE_TYPES: [] }),
+3 -3
View File
@@ -82,9 +82,9 @@ const STUBS = {
"B06_Section_Cross_Design_Session.ts": anyModule(),
"common_util_cross_design.ts": anyModule(),
"B06_Section_Structure_Layouts.ts": anyModule({ computeStoredLayouts: () => null }),
"B06_Section_UI_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_UI_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_UI_Cross_Box_Geom.ts": anyModule(),
"B06_Section_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_Cross_Box_Geom.ts": anyModule(),
"B06_Section_UI_Cross_View.ts": anyModule(),
"B06_Section_UI_Section_Common.ts": anyModule({ designElevationAt: () => undefined }),
"B06_Section_Display_Width.ts": anyModule({ displayHalfWidth: () => 10 }),
+1 -1
View File
@@ -71,7 +71,7 @@ const load = makeLoader(
B06("B06_Section_UI_Page_Station_Controls"),
B06("B06_Section_UI_Page_Link_Session"),
B06("B06_Section_Session_Cells"), // 조정창 칸 창구(472-5 5-D · 메모리 맵 없음)
B06("B06_Section_UI_Cross_Culvert_Types"),
B06("logic/B06_Section_Cross_Culvert_Types"),
B06("logic/B06_Section_Station_Swap"),
B06("logic/B06_Section_UI_Page_Patches"),
B06("logic/B06_Section_Design_Keys"), // 칸 목록 · 얹기 · patch 꼴 한 벌(472-6)
+3 -3
View File
@@ -71,9 +71,9 @@ const STUBS = {
"B06_Section_Cross_Design_Session.ts": anyModule({ crossDesignChoices: () => new Map() }),
"B06_Section_Section_Adjusts.ts": anyModule({ withPipeOptions: async () => undefined }),
"common_util_cross_design.ts": anyModule(),
"B06_Section_UI_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_UI_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_UI_Cross_Box_Geom.ts": anyModule(),
"B06_Section_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_Cross_Box_Geom.ts": anyModule(),
"B06_Section_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
"B05_Profile_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
"config_frontend.ts": anyModule({ PIPE_TYPES: [] }),
@@ -69,9 +69,9 @@ const STUBS = {
"B06_Section_Cross_Design_Session.ts": anyModule({ crossDesignChoices: () => new Map() }),
"B06_Section_Section_Adjusts.ts": anyModule({ withPipeOptions: async () => undefined }),
"common_util_cross_design.ts": anyModule(),
"B06_Section_UI_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_UI_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_UI_Cross_Box_Geom.ts": anyModule(),
"B06_Section_Cross_Culvert_Wire.ts": anyModule(),
"B06_Section_Cross_Ford_Geom.ts": anyModule(),
"B06_Section_Cross_Box_Geom.ts": anyModule(),
"B06_Section_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
"B05_Profile_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
"config_frontend.ts": anyModule({ PIPE_TYPES: [] }),
+2 -2
View File
@@ -31,8 +31,8 @@ const resolve = (base) =>
const B06 = (f) => path.join(ROOT, "B06_Section", f);
const { computeStoredLayouts, structureAreaRows } = loadTs(B06("B06_Section_Structure_Layouts.ts"));
const { bodyGuardBase } = loadTs(B06("B06_Section_UI_Cross_Body_Guard.ts"));
const { computeBoxLayout } = loadTs(B06("B06_Section_UI_Cross_Box_Geom.ts"));
const { computeCulvertLayout } = loadTs(B06("B06_Section_UI_Cross_Culvert_Geom.ts"));
const { computeBoxLayout } = loadTs(B06("logic/B06_Section_Cross_Box_Geom.ts"));
const { computeCulvertLayout } = loadTs(B06("logic/B06_Section_Cross_Culvert_Geom.ts"));
const { computeRevetmentLayout } = loadTs(B06("B06_Section_UI_Cross_Revetment.ts"));
const fixture = JSON.parse(
+3 -3
View File
@@ -80,7 +80,7 @@ out.fordHandBedDown = fordFloor((s) => {
shiftGround(s, -2, -2, 2);
});
// ② 91-3 다단 자동 자리 — 관 180 유출 흙막이 + 2단 · 원지반을 옮겨도 단 자리 같음 · 사면 1.0 m
const { computeCulvertLayout } = loadTs(B06("B06_Section_UI_Cross_Culvert_Geom.ts"));
const { computeCulvertLayout } = loadTs(B06("logic/B06_Section_Cross_Culvert_Geom.ts"));
function tiers(dz) {
const s = clone(at(180));
shiftGround(s, dz);
@@ -139,8 +139,8 @@ function guardTails(base, edit = () => undefined) {
}
out.guardTails = [...guardTails(at(352.14)), ...guardTails(FORD)];
// 박스 끝 · 세월교 측벽 끝(흙막이 없음)
const { computeBoxLayout } = loadTs(B06("B06_Section_UI_Cross_Box_Geom.ts"));
const { computeFordLayout } = loadTs(B06("B06_Section_UI_Cross_Ford_Geom.ts"));
const { computeBoxLayout } = loadTs(B06("logic/B06_Section_Cross_Box_Geom.ts"));
const { computeFordLayout } = loadTs(B06("logic/B06_Section_Cross_Ford_Geom.ts"));
function endCuts(base, compute) {
const s = clone(base);
delete s.culvert;
+2 -2
View File
@@ -1,6 +1,6 @@
"""PLAN 139-4 · 139-5 — 묻힌 흙막이 유출 끝 = 3도 수평 절토선(보라 · 절토 면적) · 못 만나면 복합경사.
짝: `B06_Section/B06_Section_UI_Cross_Culvert_Extra.ts`(끝 구간 규칙) · `_Culvert_Tiers.ts`(채널 입력) ·
짝: `B06_Section/logic/B06_Section_Cross_Culvert_Extra.ts`(끝 구간 규칙) · `_Culvert_Tiers.ts`(채널 입력) ·
`_Culvert_Geom.ts`(유출 절토선 면적에 넣음) · `_Culvert.ts`(그림 · 툴팁). 실제 TS 를 `helper_139_4_buried_outlet.cjs` 가 돌린다.
"""
@@ -68,7 +68,7 @@ def test_유출_절토선은_그리고_면적에_넣음() -> None:
culvert = _src("B06_Section/B06_Section_UI_Cross_Culvert.ts")
assert 'if (segment.kind === "cut" && !culvert.body) continue;' not in culvert
assert 'planLine(segment.points, cutLineTip("유출부", segment));' in culvert
geom = _src("B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts")
geom = _src("B06_Section/logic/B06_Section_Cross_Culvert_Geom.ts")
assert "extendTrimSlope(drawnFillPoints(extrasForArea, true), outletInfo.outward);" in geom
# 집수정 계류측 3도 선은 그림만(면적 밖) — 세월교 · 박스 계류측만 면적
assert (
+1 -1
View File
@@ -51,5 +51,5 @@ def test_풀린_카드_이_측점_높이_줄() -> None:
dock = _src("B06_Section/B06_Section_UI_Adjust_Dock.ts")
assert 'const OWN_HEIGHT_ROW = ".b06-structure-panel__struct--own-height";' in dock
assert "return OWN_HEIGHT_ROW;" in dock # 세월교 · 박스 날개벽 칸도 받음
wire = _src("B06_Section/B06_Section_UI_Cross_Culvert_Wire.ts")
wire = _src("B06_Section/logic/B06_Section_Cross_Culvert_Wire.ts")
assert "h: shift.inlet.h ?? heights.inlet" in wire
+2 -2
View File
@@ -1,7 +1,7 @@
"""PLAN 190-1 — B06 집수정 고르기 판정면 = 부재 외곽 가상 사각형(세월교 측벽 ㄴ 꼴 · 79-6).
짝: `B06_Section/B06_Section_UI_Cross_Culvert.ts`(집수정 판정면) · `_Cross_Ford.ts`(측벽 판정면) ·
`_Cross_Culvert_Types.ts`(`outlineBox` 한 벌). [상세] 모달은 카드 그림 복제라 같은 판정면을 가짐.
`logic/B06_Section_Cross_Culvert_Types.ts`(`outlineBox` 한 벌). [상세] 모달은 카드 그림 복제라 같은 판정면을 가짐.
실제 TS 를 `helper_190_1_basin_pick.cjs` 가 Node 로 돌린다.
"""
@@ -76,7 +76,7 @@ def test_고름_없으면_판정면_없음(out) -> None:
def test_세월교_측벽과_같은_함수() -> None:
types = _src("B06_Section/B06_Section_UI_Cross_Culvert_Types.ts")
types = _src("B06_Section/logic/B06_Section_Cross_Culvert_Types.ts")
assert "export function outlineBox(points: OffsetPoint[]): OffsetPoint[] {" in types
ford = _src("B06_Section/B06_Section_UI_Cross_Ford.ts")
assert 'polygon(outlineBox(points), "b06-chart__culvert-revet-hit", "")' in ford
+1 -1
View File
@@ -107,7 +107,7 @@ def test_서버_한쪽_이동_반대측_고정_바깥으로() -> None:
def test_구조물은_노견_끝에_섬() -> None:
"""관 기슭막이 자리 = 설계 노견 끝(`road_edges` · 노견 확장 포함) — 사면과 한 벌로 감."""
src = (ROOT / "B06_Section" / "B06_Section_UI_Cross_Culvert_Geom.ts").read_text(
src = (ROOT / "B06_Section" / "logic/B06_Section_Cross_Culvert_Geom.ts").read_text(
encoding="utf-8"
)
assert "const edges = section.design?.road_edges;" in src
+1 -1
View File
@@ -21,7 +21,7 @@ def test_구간값_제어에_culvertopt_큐_쓰기_없음():
span = _read("B06_Section_UI_Page_Span_Control.ts")
assert "culvertOptions" not in span
assert "update: (section, role" not in span
wire = _read("B06_Section_UI_Cross_Culvert_Wire.ts")
wire = _read("logic/B06_Section_Cross_Culvert_Wire.ts")
assert not re.search(r"^\s*update: \(section: CrossSection, role", wire, re.M)
view = _read("B06_Section_UI_Cross_View_Structure.ts")
assert "structureSpan?.update(" not in view
@@ -97,19 +97,7 @@ GEOMETRY_PULLS = (
"B05_Profile/B05_Profile_UI_Corridor_Structures_Path.ts",
"B06_Section/B06_Section_Structure_Layouts.ts",
"B06_Section/B06_Section_UI_Cross_Body_Guard.ts",
"B06_Section/B06_Section_UI_Cross_Box.ts",
"B06_Section/B06_Section_UI_Cross_Box_Geom.ts",
"B06_Section/B06_Section_UI_Cross_Culvert.ts",
"B06_Section/B06_Section_UI_Cross_Culvert_Extra.ts",
"B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts",
"B06_Section/B06_Section_UI_Cross_Culvert_Tiers.ts",
"B06_Section/B06_Section_UI_Cross_Culvert_Types.ts",
"B06_Section/B06_Section_UI_Cross_Culvert_Wire.ts",
"B06_Section/B06_Section_UI_Cross_Design_Surface.ts",
"B06_Section/B06_Section_UI_Cross_Excavation.ts",
"B06_Section/B06_Section_UI_Cross_Ford.ts",
"B06_Section/B06_Section_UI_Cross_Ford_Geom.ts",
"B06_Section/B06_Section_UI_Cross_Ford_Pavement.ts",
"B06_Section/B06_Section_UI_Cross_Revetment.ts",
"B06_Section/B06_Section_UI_Cross_Wall.ts",
"B06_Section/B06_Section_UI_Cross_Wall_Hatch.ts",
@@ -162,6 +162,7 @@ class Node {
}
globalThis.document = { createElementNS: (_ns, tag) => new Node(tag) };
const pave = load(path.join(ROOT, "B06_Section/B06_Section_UI_Cross_Ford_Pavement.ts"));
const paveGeo = load(path.join(ROOT, "B06_Section/logic/B06_Section_Cross_Ford_Pavement.ts"));
// +offset = 좌측 · 좌 성토 1:1.5 · 우 절토 오름 — 유입 = 표고 높은 쪽(설계선 끝 · 286-2) = 우 → 유출 = 좌
// `rightEnd` = 우 끝 표고 — 좌 끝(97)과 같으면 단면유형으로 가름(left_cut = 유입 좌 → 유출 우)
const design = (mode, rightEnd = 99) => ({
@@ -181,7 +182,7 @@ const run = (mode, withChute = true) => {
return band ? band.attrs.points : null;
};
process.stdout.write(JSON.stringify({
outLeft: pave.outletFillLine(design("left_cut", 97)), outRight: pave.outletFillLine(design("left_cut")),
outLeft: paveGeo.outletFillLine(design("left_cut", 97)), outRight: paveGeo.outletFillLine(design("left_cut")),
band: run("left_cut"), none: run("left_cut", false),
}));
"""
+2 -2
View File
@@ -78,9 +78,9 @@ def test_지형에_막힘_잔재_없음() -> None:
for name in (
"B06_Section_UI_Cross_Culvert.ts",
"B06_Section_UI_Cross_Revetment.ts",
"B06_Section_UI_Cross_Culvert_Wire.ts",
"logic/B06_Section_Cross_Culvert_Wire.ts",
"B06_Section_UI_Cross_Culvert_Types.ts",
"B06_Section_UI_Cross_Culvert_Extra.ts",
"logic/B06_Section_Cross_Culvert_Extra.ts",
):
source = (ROOT / "B06_Section" / name).read_text(encoding="utf-8")
assert "shiftBlockedM" not in source and "지형에 막힘" not in source, name
@@ -22,7 +22,7 @@ if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
MODULE = PROJECT_ROOT / "B06_Section" / "B06_Section_UI_Cross_Culvert_Extra.ts"
MODULE = PROJECT_ROOT / "B06_Section" / "logic/B06_Section_Cross_Culvert_Extra.ts"
# 상하 내림 2.0m → 하한 2.4m. 좌우 0.5m 를 요청해도 2.4m 로 밀려 나가야 한다.
REQUESTED_X = 0.5
@@ -79,7 +79,7 @@ def _run(tmp_path: Path, requested_x: float = REQUESTED_X) -> dict:
check=False,
capture_output=True,
)
compiled = next(out.rglob("B06_Section_UI_Cross_Culvert_Extra.js"), None)
compiled = next(out.rglob("B06_Section_Cross_Culvert_Extra.js"), None)
assert compiled is not None, "화면 코드가 JS 로 안 나옴 — tsc 실패"
(out / "runner.cjs").write_text(
_RUNNER % {"x": requested_x, "d": REQUESTED_D}, encoding="utf-8"
+9 -6
View File
@@ -18,8 +18,8 @@ BUNDLE = PROJECT_ROOT / "config" / "design_calc_node" / "design_calc_node.js"
_RUNNER = """
import { writeFileSync } from "node:fs";
import { placePipeWall } from "./logic/B06_Section_UI_Cross_Culvert_Solve.js";
import { buildOutletExtras } from "./B06_Section_UI_Cross_Culvert_Extra.js";
import { placePipeWall } from "./B06_Section_UI_Cross_Culvert_Solve.js";
import { buildOutletExtras } from "./B06_Section_Cross_Culvert_Extra.js";
const pipe = (d, x) => placePipeWall({
autoOffset: 10, outward: 1, height: 2, baseElevation0: 98, edgeElevation: 100,
invertCap: Number.POSITIVE_INFINITY, adjust: { x, d, h: null, m: null }, defaultD: 4.8,
@@ -42,7 +42,10 @@ writeFileSync(process.argv[2], JSON.stringify({
@pytest.mark.skipif(not TSC.is_file(), reason="프론트엔드 의존성 미설치")
def test_원지반이_자리_다단을_막지_않고_노면_위만_막음(tmp_path: Path) -> None:
out = tmp_path / "js"
for name in ("logic/B06_Section_UI_Cross_Culvert_Solve", "B06_Section_UI_Cross_Culvert_Extra"):
for name in (
"logic/B06_Section_UI_Cross_Culvert_Solve",
"logic/B06_Section_Cross_Culvert_Extra",
):
subprocess.run( # noqa: S603 — 고정 실행 파일
[
"node",
@@ -67,7 +70,7 @@ def test_원지반이_자리_다단을_막지_않고_노면_위만_막음(tmp_pa
re.sub(r'(from\s+"\.{1,2}/[^"]+)"', r'\1.js"', emitted.read_text(encoding="utf-8")),
encoding="utf-8",
)
base = next(path.parent for path in out.rglob("B06_Section_UI_Cross_Culvert_Extra.js"))
base = next(path.parent for path in out.rglob("B06_Section_Cross_Culvert_Extra.js"))
(base / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
result = tmp_path / "out.json"
subprocess.run( # noqa: S603
@@ -87,7 +90,7 @@ def test_원지반이_자리_다단을_막지_않고_노면_위만_막음(tmp_pa
def test_화면_서버_같은_판() -> None:
for name in (
"logic/B06_Section_UI_Cross_Culvert_Solve",
"B06_Section_UI_Cross_Culvert_Geom",
"logic/B06_Section_Cross_Culvert_Geom",
"B06_Section_UI_Cross_Revetment",
):
source = (B06 / f"{name}.ts").read_text(encoding="utf-8")
@@ -96,7 +99,7 @@ def test_화면_서버_같은_판() -> None:
assert "const placeable" not in (B06 / "B06_Section_UI_Cross_Revetment.ts").read_text(
encoding="utf-8"
), "사면안정 흙막이 · 옹벽 매몰 자리 금지가 남음"
assert "const placeable" not in (B06 / "B06_Section_UI_Cross_Culvert_Extra.ts").read_text(
assert "const placeable" not in (B06 / "logic/B06_Section_Cross_Culvert_Extra.ts").read_text(
encoding="utf-8"
), "추가 흙막이 매몰 자리 금지가 남음"
bundle = BUNDLE.read_text(encoding="utf-8")
+9 -6
View File
@@ -18,8 +18,8 @@ BUNDLE = PROJECT_ROOT / "config" / "design_calc_node" / "design_calc_node.js"
_RUNNER = """
import { writeFileSync } from "node:fs";
import { revetWallSpec } from "./logic/B06_Section_UI_Cross_Culvert_Const.js";
import { buildOutletExtras } from "./B06_Section_UI_Cross_Culvert_Extra.js";
import { revetWallSpec } from "./B06_Section_UI_Cross_Culvert_Const.js";
import { buildOutletExtras } from "./B06_Section_Cross_Culvert_Extra.js";
const spec = { revet_form: "찰쌓기", revet_height_m: 1.5 };
const pipe = (h) => revetWallSpec(spec, { h, m: null }, false, 0.8).pureHeight;
const own = (h) => revetWallSpec(spec, { h, m: null }, true, 0).pureHeight;
@@ -41,7 +41,10 @@ writeFileSync(process.argv[2], JSON.stringify({
@pytest.mark.skipif(not TSC.is_file(), reason="프론트엔드 의존성 미설치")
def test_높이는_입력_그대로_0이하만_멈춤(tmp_path: Path) -> None:
out = tmp_path / "js"
for name in ("logic/B06_Section_UI_Cross_Culvert_Const", "B06_Section_UI_Cross_Culvert_Extra"):
for name in (
"logic/B06_Section_UI_Cross_Culvert_Const",
"logic/B06_Section_Cross_Culvert_Extra",
):
subprocess.run( # noqa: S603 — 고정 실행 파일
[
"node",
@@ -66,7 +69,7 @@ def test_높이는_입력_그대로_0이하만_멈춤(tmp_path: Path) -> None:
re.sub(r'(from\s+"\.{1,2}/[^"]+)"', r'\1.js"', emitted.read_text(encoding="utf-8")),
encoding="utf-8",
)
base = next(path.parent for path in out.rglob("B06_Section_UI_Cross_Culvert_Extra.js"))
base = next(path.parent for path in out.rglob("B06_Section_Cross_Culvert_Extra.js"))
(base / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
result = tmp_path / "out.json"
subprocess.run( # noqa: S603
@@ -85,8 +88,8 @@ def test_높이는_입력_그대로_0이하만_멈춤(tmp_path: Path) -> None:
def test_높이_토스트_없음_서버_같은_판() -> None:
for name in ("UI_Cross_Culvert_Wire", "UI_Page_Pipe_Options"):
source = (B06 / f"B06_Section_{name}.ts").read_text(encoding="utf-8")
for name in ("logic/B06_Section_Cross_Culvert_Wire", "B06_Section_UI_Page_Pipe_Options"):
source = (B06 / f"{name}.ts").read_text(encoding="utf-8")
assert "B06_Cross_Height_Floor" not in source, f"{name} 에 최소 높이 토스트가 남음"
assert "높이는 최소" not in source, f"{name} 에 최소 높이 토스트가 남음"
panel = (B06 / "B06_Section_UI_Cross_Structure_Panel.ts").read_text(encoding="utf-8")
+2 -2
View File
@@ -19,9 +19,9 @@ if str(ROOT) not in sys.path:
DATA_DIR = ROOT / "resources" / "data_masonry"
GEOMETRY = [
"B06_Section_UI_Cross_Culvert_Geom.ts",
"logic/B06_Section_Cross_Culvert_Geom.ts",
"B06_Section_UI_Cross_Wall.ts",
"B06_Section_UI_Cross_Culvert_Extra.ts",
"logic/B06_Section_Cross_Culvert_Extra.ts",
"logic/B06_Section_UI_Cross_Culvert_Solve.ts",
]
@@ -95,12 +95,16 @@ def test_벽_외곽_한_벌_높이_입력_그대로_노면_멈춤(tmp_path: Path
def test_네_벽이_공용_함수로_서고_형태_한도가_없다() -> None:
for name in ("Culvert_Geom", "Culvert_Extra", "Revetment"):
source = (B06 / f"B06_Section_UI_Cross_{name}.ts").read_text(encoding="utf-8")
for name in (
"logic/B06_Section_Cross_Culvert_Geom",
"logic/B06_Section_Cross_Culvert_Extra",
"B06_Section_UI_Cross_Revetment",
):
source = (B06 / f"{name}.ts").read_text(encoding="utf-8")
assert "buildRevetWallGeometry(" in source, f"{name} 가 공용 벽 함수를 안 씀"
for path in B06.rglob("*.ts"): # logic/ 까지(348-1)
source = path.read_text(encoding="utf-8")
assert "revetHeightLimit" not in source, f"{path.name} 에 형태 높이 한도가 남음"
assert "materialLimit" not in source, f"{path.name} 에 재질 높이 한도가 남음"
extra = (B06 / "B06_Section_UI_Cross_Culvert_Extra.ts").read_text(encoding="utf-8")
extra = (B06 / "logic/B06_Section_Cross_Culvert_Extra.ts").read_text(encoding="utf-8")
assert "fitted" not in extra, "추가 흙막이가 원지반에 맞춰 늘어남"
@@ -151,7 +151,7 @@
.map((e) => e.name)
.filter((n) => n.includes(part))
.pop();
const geom = await import(modUrl("/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts"));
const geom = await import(modUrl("/B06_Section/logic/B06_Section_Cross_Culvert_Geom.ts"));
const key = Object.keys(sessionStorage).find((k) => k.includes("section-detail"));
const detail = JSON.parse(sessionStorage.getItem(key));
const forms = geom.REVET_FORMS;