Merge remote-tracking branch 'origin/sub_laptop_7' into sub_laptop_1
This commit is contained in:
@@ -71,26 +71,42 @@ CHUTE_THICKNESS_CM = 30.0
|
||||
# 1:0.3~0.5의 급한 쪽. 지식DB 근거가 있는 값이라 교차 참조 표시가 붙지 않는다.
|
||||
REVET_FACE_SLOPE = 0.3
|
||||
|
||||
# ── 관 벽 최소 높이 — ⚠ TS 짝: `B06_Section_UI_Cross_Culvert_Const.ts`
|
||||
# (`REVET_FREEBOARD_M`·`REVET_HEIGHT_STEP_M`·`REVET_EMBED_DEPTH_M`·`pipeWallMinPureHeight`).
|
||||
# ── 관 벽 기본 높이 — ⚠ TS 짝: `B06_Section_UI_Cross_Culvert_Const.ts`
|
||||
# (`REVET_HEIGHT_STEP_M`·`REVET_EMBED_DEPTH_M`·`revetTargetHeight`·`pipeWallMinPureHeight`).
|
||||
# 횡단도가 관 벽을 그리는 그 높이 — 구조물 표·구조물도 그림도 이 값을 씀(2026-09-14 브레인 판정
|
||||
# 「그림 둘이 같은 벽을 다르게 그리면 결함 · 근거 있는 관경 기준으로 맞춤」). 거울 시험
|
||||
# `test_b06_pipe_wall_height_mirror`.
|
||||
REVET_FREEBOARD_M = 0.5 # 관 상단 ~ 벽 상단 여유고(잠정 · 토피 0.5 와 같은 값)
|
||||
REVET_HEIGHT_STEP_M = 0.1 # 높이 눈금
|
||||
REVET_EMBED_DEPTH_M = 0.5 # 근입 깊이
|
||||
# 옛 정의 기본 높이(10-6 표 · 관경 무관) — 관경 > 1.0 관에 이 값이면 관 윗면보다 낮은 벽이라
|
||||
# 읽을 때 관경 기준 값으로 봄(139-1).
|
||||
OLD_PIPE_WALL_DEFAULT_M = 1.5
|
||||
|
||||
|
||||
def pipe_wall_min_height_m(diameter_m: float) -> float:
|
||||
"""관 벽 **순수 높이**(근입 포함) 최소값 — 관경 + 여유고를 0.1m 로 올린 뒤 근입을 더함.
|
||||
"""관 벽 **순수 높이**(근입 포함) 기본값 = 관경(0.1m 로 올림) + 근입 0.5 — 벽 윗면 = 관 윗면(139-1).
|
||||
|
||||
⚠ 「최소」다 — 실제 설계값은 더 클 수 있어 이 값으로 선 수량은 미확정으로 둠(금액 밖).
|
||||
관 · 유입 집수정 높이 칸의 새 기본값이자 빈 칸 값.
|
||||
"""
|
||||
raw = float(diameter_m) + REVET_FREEBOARD_M
|
||||
raw = float(diameter_m)
|
||||
steps = math.ceil(raw / REVET_HEIGHT_STEP_M - 1e-9)
|
||||
return round(steps * REVET_HEIGHT_STEP_M + REVET_EMBED_DEPTH_M, 3)
|
||||
|
||||
|
||||
def pipe_wall_height_m(stored: float | None, diameter_m: float) -> float | None:
|
||||
"""관 벽 · 유입 집수정 높이 칸 읽기 — 옛 기본 1.5 이고 관경 > 1.0 이면 관경 기준 값(139-1).
|
||||
|
||||
빈 칸 = None(부르는 쪽이 빈 칸 규칙). ⚠ TS 짝 `common_util_culvert_sets.pipeWallHeightM`.
|
||||
"""
|
||||
if (
|
||||
stored is not None
|
||||
and abs(stored - OLD_PIPE_WALL_DEFAULT_M) < 1e-9
|
||||
and float(diameter_m) > 1.0 + 1e-9
|
||||
):
|
||||
return pipe_wall_min_height_m(diameter_m)
|
||||
return stored
|
||||
|
||||
|
||||
# (2026-08-20 사용자 확정) 배관은 **수평도 가능** — 그림에서 경사를 강제로 만들지
|
||||
# 않는다. config `DRAINAGE_PIPE_SLOPE_DEG`(10도)는 유효직경 수리계산 전용으로 남는다.
|
||||
|
||||
@@ -188,16 +204,17 @@ def _side_spec(
|
||||
None,
|
||||
)
|
||||
# 집수정 높이(좌측 폼 · 비면 정의 기본값) — 횡단도 집수정 벽 높이.
|
||||
spec["basin_height_m"] = _number(options.get("inlet_basin_height_m"), None)
|
||||
spec["basin_height_m"] = pipe_wall_height_m(
|
||||
_number(options.get("inlet_basin_height_m"), None), diameter_m
|
||||
)
|
||||
# 집수정 모양(54-39) — 빈 값 = 안 고름
|
||||
spec["basin_form"] = str(options.get("inlet_structure") or "") or None
|
||||
return spec
|
||||
|
||||
# 높이를 안 적었으면 **횡단도가 그리는 관경 기준 최소 높이**(2026-09-14 · 옛 기본 2.5 걷음).
|
||||
height = _number(
|
||||
options.get(f"{side}_revet_height_m"),
|
||||
pipe_wall_min_height_m(diameter_m),
|
||||
)
|
||||
# 높이를 안 적었으면 **관경 기준 높이**(관경 + 0.5 · 139-1) · 옛 1.5 도 관경 > 1.0 이면 그 값.
|
||||
height = pipe_wall_height_m(_number(options.get(f"{side}_revet_height_m"), None), diameter_m)
|
||||
if height is None:
|
||||
height = pipe_wall_min_height_m(diameter_m)
|
||||
length = _number(
|
||||
options.get(f"{side}_revet_length_m"),
|
||||
None,
|
||||
|
||||
@@ -24,23 +24,22 @@ export const REVET_LEAN_RATIO = 0.3;
|
||||
export const REVET_TRAP_TOP_M = REVET_THICKNESS_M / 2;
|
||||
|
||||
/**
|
||||
* 기슭막이 여유고(m) — 관 상단에서 벽 상단까지의 간격. 유입·유출 **공통**이다.
|
||||
* 집수정 상부 여유(m) — 관 상단에서 집수정 벽 상단까지의 간격. 관 벽(유입·유출)은
|
||||
* 여유 없이 벽 윗면 = 관 윗면(139-1 · `revetTargetHeight`).
|
||||
*
|
||||
* ⚠ 확정값 아님(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). */
|
||||
/** 관 벽 계산용 높이(m · invert~상단) — 관경을 눈금(0.1m)으로 올림 → 벽 윗면 = 관 윗면(139-1).
|
||||
* ⚠ 파이썬 짝 `pipe_wall_min_height_m` · 세트 짝 `pipeWallMinHeightM`(거울 시험). */
|
||||
export function revetTargetHeight(diameterM: number): number {
|
||||
const raw = diameterM + REVET_FREEBOARD_M;
|
||||
const raw = diameterM;
|
||||
return Math.ceil(raw / REVET_HEIGHT_STEP_M - 1e-9) * REVET_HEIGHT_STEP_M;
|
||||
}
|
||||
|
||||
@@ -139,7 +138,7 @@ export function revetWallSpec(
|
||||
// 관경 기준 높이는 기본값만 — 하한은 그림이 서는 값뿐(54-51 · 사용자가 고름).
|
||||
const preferred = hiddenPipe
|
||||
? (spec.revet_height_m ?? autoHeightM ?? REVET_EMBED_DEPTH_M)
|
||||
: pipeWallMinPureHeight(diameterM);
|
||||
: (spec.revet_height_m ?? pipeWallMinPureHeight(diameterM)); // 칸 값 → 그림(139-1)
|
||||
const pureHeight = Math.max(adjust.h ?? preferred, WALL_MIN_PURE_HEIGHT_M);
|
||||
return {
|
||||
form,
|
||||
@@ -154,10 +153,9 @@ export function revetWallSpec(
|
||||
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(관경 무관)는 폐기.
|
||||
* 배관 기슭막이 관 하부 여유(m) — 관 invert 아래 벽 바닥까지의 깊이 = 근입 0.5.
|
||||
* **순수 높이 기본 = 관경 + 0.5**(땅속 근입 0.5 포함 벽 전체 높이 · 벽 윗면 = 관 윗면 ·
|
||||
* 10-6 표 Ø1000 = 1.5 · 139-1). 칸을 비우면 이 값.
|
||||
*/
|
||||
export function pipeWallMinPureHeight(diameterM: number): number {
|
||||
return revetTargetHeight(diameterM) + REVET_EMBED_DEPTH_M;
|
||||
|
||||
@@ -409,16 +409,7 @@ export function computeCardCulvert(
|
||||
// 연동이면 소유 측점이 **실제로 적용한 4축**(자동 자리 포함)을 명시값으로 물려준다.
|
||||
// 조작값만 넘기면 손대기 전(전부 자동)에는 이 측점이 자기 지형으로 자동 자리를 다시
|
||||
// 잡아 소유 측점과 어긋난다 — "한 번 움직이면 맞는다"던 증상의 원인(2026-08-24 사용자).
|
||||
const ownerInput = adjustsInput(link.source, revetOffset, extraWalls);
|
||||
// 세월교 · 박스 흙막이는 본체 기준 x 를 절대값으로 바꿔 넘김 — 옆 측점엔 본체가 없음(73-4).
|
||||
const ownerLayout = computeCulvertLayout(
|
||||
link.source,
|
||||
link.source.samples,
|
||||
ownerInput,
|
||||
link.source.culvert?.hidden_pipe ? "revet" : inletStructure?.valueFor(link.source),
|
||||
inletStructure?.adjustFor(link.source),
|
||||
);
|
||||
const ownerApplied = ownerLayout ? bodyAbsolute(ownerLayout) : ownerInput;
|
||||
const ownerApplied = ownerAppliedAdjusts(link.source, revetOffset, inletStructure, extraWalls);
|
||||
// 연동을 풀어도 **같은 기슭막이**다 — 단 수는 소유 측점 것이고, 이 측점이 손댄
|
||||
// 벽만 자기 값으로 선다(2026-08-30 사용자).
|
||||
const merged = detached ? detachedAdjusts(section, ownerApplied, revetOffset) : ownerApplied;
|
||||
@@ -599,6 +590,25 @@ function detachedAdjusts(
|
||||
};
|
||||
}
|
||||
|
||||
/** 소유 측점이 실제로 적용한 4축 — 연동 카드가 물려받는 값 · 연동을 푸는 순간 굳히는 값(139-2). */
|
||||
export function ownerAppliedAdjusts(
|
||||
owner: CrossSection,
|
||||
revetOffset?: RevetOffsetControl,
|
||||
inletStructure?: InletStructureControl,
|
||||
extraWalls?: ExtraWallControl,
|
||||
): WallAdjustSet {
|
||||
const ownerInput = adjustsInput(owner, revetOffset, extraWalls);
|
||||
// 세월교 · 박스 흙막이는 본체 기준 x 를 절대값으로 바꿔 넘김 — 옆 측점엔 본체가 없음(73-4).
|
||||
const ownerLayout = computeCulvertLayout(
|
||||
owner,
|
||||
owner.samples,
|
||||
ownerInput,
|
||||
owner.culvert?.hidden_pipe ? "revet" : inletStructure?.valueFor(owner),
|
||||
inletStructure?.adjustFor(owner),
|
||||
);
|
||||
return ownerLayout ? bodyAbsolute(ownerLayout) : ownerInput;
|
||||
}
|
||||
|
||||
function adjustsInput(
|
||||
section: CrossSection,
|
||||
revetOffset?: RevetOffsetControl,
|
||||
|
||||
@@ -150,8 +150,11 @@ export function structurePanelDeps(ctx: StructurePanelContext): StructurePanelDe
|
||||
canNudge: (key) => key !== "inlet" || !ctx.inletIsBasin(),
|
||||
// 걸음 분류(2026-08-29 사용자): **배관 기준벽만 1m**, 나머지(독립 기슭막이 기준벽·
|
||||
// 배관/독립 종속 추가 벽·옛 D경로 독립 벽)는 0.1m. 세월교 흙막이는 안이 관이라 1m(80).
|
||||
// 연동을 푼 옆 측점 카드는 자기 자리를 잡는 벽이라 0.1m(139-2).
|
||||
fineMove: (key) =>
|
||||
(key !== "inlet" && key !== "outlet") || (ctx.hiddenPipe() && !ctx.fordGuard?.()),
|
||||
(key !== "inlet" && key !== "outlet") ||
|
||||
(ctx.hiddenPipe() && !ctx.fordGuard?.()) ||
|
||||
(ctx.isLinked && ctx.revetLink?.linkedFor(ctx.section) === false),
|
||||
dragAxes: (key, basin) => wallDragAxes(ctx, key, basin),
|
||||
extraState: () => ctx.extraState(),
|
||||
isLinkedCard: () => ctx.isLinked,
|
||||
@@ -203,20 +206,9 @@ export function structurePanelDeps(ctx: StructurePanelContext): StructurePanelDe
|
||||
ctx.isLinked && ctx.drawnWallKeys().length
|
||||
? { linked: ctx.revetLink?.linkedFor(ctx.section) ?? true }
|
||||
: null,
|
||||
setLinked: (linked) => {
|
||||
// 연동을 **푸는 순간** 지금 그려진 높이를 이 측점 값으로 굳힌다(2026-09-06 사용자
|
||||
// 지시). 예전에는 위치(4축)만 갈리고 높이는 소유 측점 값을 계속 따라가, 소유
|
||||
// 측점 높이를 바꾸면 푼 측점까지 같이 움직였다. 굳혀 두면 값이 튀지 않으면서
|
||||
// 이후에는 따로 논다. 단 수·형태는 소유 측점을 그대로 따른다(2026-08-30 확정 유지).
|
||||
if (!linked) {
|
||||
ctx.drawnWallKeys().forEach((key) => {
|
||||
if (ctx.adjustOf(key).h !== null && ctx.adjustOf(key).h !== undefined) return;
|
||||
const height = ctx.heightOfWall(key);
|
||||
if (height > 0) ctx.revetOffset?.update(ctx.section.chainage_m, key, { h: height });
|
||||
});
|
||||
}
|
||||
ctx.revetLink?.setLinked(ctx.section.chainage_m, linked);
|
||||
},
|
||||
// 푸는 순간 4축(x · d · h · m) 굳힘 · 다시 연동 = 지움 — 제어기 `setLinked` 한 길(139-2 ·
|
||||
// 좌측 관 보호공 [연동]과 같음). 단 수 · 형식은 소유 측점을 그대로 따름(2026-08-30).
|
||||
setLinked: (linked) => ctx.revetLink?.setLinked(ctx.section.chainage_m, linked),
|
||||
followGrade: () =>
|
||||
ctx.revetLink?.followGradeFor(ctx.structureSpan?.ownerOf(ctx.section) ?? ctx.section) ?? true,
|
||||
setFollowGrade: (follow) => {
|
||||
|
||||
@@ -10,12 +10,21 @@
|
||||
* ========================================================================== */
|
||||
|
||||
import type { CrossSection } from "./B06_Section_Api_Fetch";
|
||||
import type { RevetKey } from "./B06_Section_UI_Cross_Culvert";
|
||||
import type { WallAdjust } from "./B06_Section_UI_Cross_Culvert_Types";
|
||||
import {
|
||||
computeCardCulvert,
|
||||
culvertLinkFor,
|
||||
culvertOwnerFor,
|
||||
culvertReach,
|
||||
culvertWallsStandAt,
|
||||
ownerAppliedAdjusts,
|
||||
type ExtraWallControl,
|
||||
type InletStructureControl,
|
||||
type RevetLinkControl,
|
||||
type RevetOffsetControl,
|
||||
} from "./B06_Section_UI_Cross_Culvert_Wire";
|
||||
import { culvertCardState } from "./B06_Section_UI_Cross_View_Structure";
|
||||
import type { PipeRevetChoice } from "../A00_Common/design/design_structure_pipe_calc";
|
||||
|
||||
/** 세션에 담기는 측점별 조작값 한 겹 맵. */
|
||||
@@ -111,10 +120,59 @@ export function createLinkFlagSession(sessionKey: () => string | null): LinkFlag
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 연동을 푸는 순간(카드 [연동] · 좌측 [연동] 둘 다 · 139-2) — 그 카드가 그리던 벽마다 4축을 이
|
||||
* 측점 값으로 굳힘: 위치(x · d) = 소유 측점 적용값(물려받던 값) · 높이 · 형태 = 그 카드가 그린 값.
|
||||
* d 가 차서 이 측점 지형으로 자동 자리 · 관 정수 맞춤을 다시 타지 않음(튐 없음). 연동 중에 부름.
|
||||
*/
|
||||
export function freezeLinkedWalls(
|
||||
section: CrossSection,
|
||||
sections: readonly CrossSection[],
|
||||
c: {
|
||||
revetOffset: RevetOffsetControl;
|
||||
inletStructure?: InletStructureControl;
|
||||
extraWalls?: ExtraWallControl;
|
||||
revetLink: RevetLinkControl;
|
||||
},
|
||||
): void {
|
||||
const link = section.culvert ? undefined : culvertLinkFor(section, sections, () => null);
|
||||
const owner =
|
||||
link && ownerAppliedAdjusts(link.source, c.revetOffset, c.inletStructure, c.extraWalls);
|
||||
if (!link || !owner) return;
|
||||
const card = computeCardCulvert(
|
||||
section,
|
||||
section.samples,
|
||||
null,
|
||||
c.revetOffset,
|
||||
c.inletStructure,
|
||||
c.extraWalls,
|
||||
link,
|
||||
c.revetLink,
|
||||
);
|
||||
const drawn = card ? culvertCardState(card).wallSpecs : new Map();
|
||||
const roles: Array<[RevetKey, WallAdjust]> = [
|
||||
["inlet", owner.inlet],
|
||||
["outlet", owner.outlet],
|
||||
...owner.extras.map((a, i): [RevetKey, WallAdjust] => [`extra${i}` as RevetKey, a]),
|
||||
...owner.basinExtras.map((a, i): [RevetKey, WallAdjust] => [`bextra${i}` as RevetKey, a]),
|
||||
];
|
||||
for (const [role, applied] of roles) {
|
||||
const wall = drawn.get(role);
|
||||
if (!wall) continue;
|
||||
const round3 = (v: number): number => Math.round(v * 1000) / 1000;
|
||||
c.revetOffset.update(section.chainage_m, role, {
|
||||
x: round3(applied.x),
|
||||
d: applied.d == null ? null : round3(applied.d),
|
||||
h: wall.height,
|
||||
m: wall.form,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 좌측 관 보호공 [연동] · [경사](54-30) — 옛 연동 제어 그대로 읽고 씀. 연동 = 벽이 서는 옆 측점이
|
||||
* 있을 때만 · 켬 = 그 측점이 모두 연동 · 끄면 모두 따로. 경사 = 보호공 구간이 있을 때만.
|
||||
* ponytail: 연동을 풀 때 카드 단추가 하던 높이 굳히기는 안 함 — 필요하면 카드 높이 셈을 여기로.
|
||||
* 풀 때 굳히기 · 다시 걸 때 지우기는 `setLinked` 가 함(139-2 · 카드 단추와 한 길).
|
||||
*/
|
||||
export function pipeRevetChoiceBridge(revetLink: RevetLinkControl) {
|
||||
const linkCards = (owner: CrossSection, sections: readonly CrossSection[]) =>
|
||||
|
||||
@@ -26,7 +26,11 @@ import type {
|
||||
import { culvertOwnerFor, culvertReach, wallStandsAt } from "./B06_Section_UI_Cross_Culvert_Wire";
|
||||
import { createCulvertOptionWriter } from "./B06_Section_Api_Culvert_Options";
|
||||
import { createBodyControls } from "./B06_Section_UI_Page_Ford_Controls";
|
||||
import { createLinkFlagSession, createSessionMap } from "./B06_Section_UI_Page_Link_Session";
|
||||
import {
|
||||
createLinkFlagSession,
|
||||
createSessionMap,
|
||||
freezeLinkedWalls,
|
||||
} from "./B06_Section_UI_Page_Link_Session";
|
||||
import { createSpanControl } from "./B06_Section_UI_Page_Span_Control";
|
||||
import type { FordAdjust } from "./B06_Section_UI_Cross_Ford";
|
||||
import type { FordControl } from "./B06_Section_UI_Cross_Ford_Panel";
|
||||
@@ -528,6 +532,16 @@ export function createStationControls(deps: StationControlDeps): StationControls
|
||||
return section.design?.revet_link_detached !== true;
|
||||
},
|
||||
setLinked: (chainageM, linked) => {
|
||||
// 푸는 순간 그린 4축을 이 측점 값으로 굳힘 · 다시 연동 = 굳힌 값 지움(139-2)
|
||||
const section = sectionAt(chainageM);
|
||||
if (linked) dropLinkedAdjusts(chainageM);
|
||||
else if (section && revetLinkControl.linkedFor(section))
|
||||
freezeLinkedWalls(section, deps.detail()?.cross_sections ?? [], {
|
||||
revetOffset: revetOffsetControl,
|
||||
inletStructure: inletStructureControl,
|
||||
extraWalls: extraWallControl,
|
||||
revetLink: revetLinkControl,
|
||||
});
|
||||
linkDetached.set(chainageKey(chainageM), !linked);
|
||||
persistLinkFlags();
|
||||
patchCachedDesign(chainageM, { revet_link_detached: !linked });
|
||||
|
||||
Reference in New Issue
Block a user