fix(B06): 독립 기슭막이 좌우 로직을 성토 경로 하나로 통일한다
이관 뒤 독립 기슭막이 벽 역할이 uphill_side로 정해져, 고른 쪽이 상단이면 유입 역할(집수정 후보·placeInletWall·다단 없음), 하단이면 유출 역할 (성토부선+buildExtrasAt)을 타서 좌우 동작이 갈렸다. 새 기하 코드를 만들지 않고 이미 있는 유출 경로 인자를 유입 쪽에도 넘겨 재사용한다. - Const: revetHeightLimit 콘크리트 4.0 → 5.0 (materialLimit과 통일) - Geom: hidden_pipe면 basinReason null, 유입 배치에 requireCrossing 전달, basinExtras 채널을 유입 벽 성토부선·다단으로 사용, inletFill 생략 - Solve: placeInletWall이 requireCrossing을 통과시킨다(기본 false 유지) - Wire: 링크 카드에서 hidden_pipe면 유입측 다단·성토부선을 벽 연장에 맞춰 유지 - Panel/View_Structure: hiddenPipe deps 추가 — 집수정 형식 숨김, 유입 벽에도 단 수 행 표시 - Culvert: hidden_pipe의 basinFill 툴팁 이름을 "유입부"로 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -120,12 +120,15 @@ export function appendCulvertOverlay(
|
||||
);
|
||||
}
|
||||
// 집수정(ㄴ·ㄷ형) 계류측 성토부선 — 유출측과 같은 체계(2026-08-22 사용자 확정).
|
||||
// 독립 기슭막이(관 숨김)에는 집수정이 없다 — 이 채널이 **유입측 벽**의 성토부선이라
|
||||
// 이름도 그렇게 적는다(2026-08-29 좌우 통일).
|
||||
const basinFillName = layout.culvert.hidden_pipe ? "유입부" : "집수정 계류측";
|
||||
for (const segment of layout.basinFill.segments) {
|
||||
planLine(
|
||||
segment.points,
|
||||
segment.kind === "cut"
|
||||
? `집수정 계류측 3도 절토선 — 구조물이 원지반에 묻혀 성토 불필요, 원지반 교차까지 ${segment.lengthM.toFixed(2)}m`
|
||||
: `집수정 계류측 성토부선 — 사면길이 ${segment.lengthM.toFixed(2)}m` +
|
||||
? `${basinFillName} 3도 절토선 — 구조물이 원지반에 묻혀 성토 불필요, 원지반 교차까지 ${segment.lengthM.toFixed(2)}m`
|
||||
: `${basinFillName} 성토부선 — 사면길이 ${segment.lengthM.toFixed(2)}m` +
|
||||
(segment.overLimit
|
||||
? " (법정 5m 이상 — 기슭막이 의무 구간, 조정창에서 추가)"
|
||||
: " (법정 5m 이내)") +
|
||||
|
||||
@@ -85,7 +85,7 @@ export function revetHeightLimit(form: string | null | undefined): number {
|
||||
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 이하
|
||||
if (form.includes("콘크리트")) return 5.0; // 프로젝트 확정값 — materialLimit과 동일
|
||||
return 3.0;
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,10 @@ export function computeCulvertLayout(
|
||||
? "short"
|
||||
: null;
|
||||
const choice: InletStructureChoice = inletStructure ?? "auto";
|
||||
const { reason: basinReason, shape: basinShape } = resolveBasinChoice(choice, ruleReason);
|
||||
// 독립 기슭막이(관 숨김)는 집수정을 세우지 않는다 — 설치 측이 상단(유입 역할)일 때만
|
||||
// 집수정이 서서 좌·우가 갈리던 원인(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 - basinAdjust.innerHeightM;
|
||||
@@ -229,13 +232,20 @@ export function computeCulvertLayout(
|
||||
defaultD: PIPE_WALL_DEFAULT_RUN_M,
|
||||
groundAt,
|
||||
limitOffset: inletInfo.limit,
|
||||
// 독립 기슭막이는 유출 벽과 같은 자리 규칙 — 매몰-무교차 자리 금지(2026-08-29).
|
||||
requireCrossing: culvert.hidden_pipe === true,
|
||||
outletGuard: culvert.hidden_pipe
|
||||
? undefined
|
||||
: {
|
||||
edge: outletInfo.edge,
|
||||
outward: outletInfo.outward,
|
||||
height: outletWallSpec.height,
|
||||
toeOffset: slopeToeOffset(designAt, groundAt, outletInfo.edge.offset_m, outletInfo.limit),
|
||||
toeOffset: slopeToeOffset(
|
||||
designAt,
|
||||
groundAt,
|
||||
outletInfo.edge.offset_m,
|
||||
outletInfo.limit,
|
||||
),
|
||||
limitOffset: outletInfo.limit,
|
||||
},
|
||||
});
|
||||
@@ -400,7 +410,7 @@ export function computeCulvertLayout(
|
||||
};
|
||||
return wall;
|
||||
};
|
||||
buildWall(
|
||||
const inletWall = buildWall(
|
||||
culvert.inlet,
|
||||
inlet,
|
||||
inletInfo.outward,
|
||||
@@ -583,15 +593,16 @@ export function computeCulvertLayout(
|
||||
}
|
||||
// 유입 기슭막이의 관 시작 접속선 — 위 0도 성토선 / 아래 0도 1m 공간 확보에서 끝
|
||||
// (2026-08-23 사용자 — 절토 경사 연장 삭제).
|
||||
const inletFill = !basin
|
||||
? inletGroundConnector(
|
||||
pipeCorners.inlet.bottom,
|
||||
inletInfo.outward,
|
||||
groundAt,
|
||||
section.design?.cut_slope_ratio ?? 1.0,
|
||||
inletInfo.limit,
|
||||
)
|
||||
: null;
|
||||
const inletFill =
|
||||
!basin && !culvert.hidden_pipe
|
||||
? inletGroundConnector(
|
||||
pipeCorners.inlet.bottom,
|
||||
inletInfo.outward,
|
||||
groundAt,
|
||||
section.design?.cut_slope_ratio ?? 1.0,
|
||||
inletInfo.limit,
|
||||
)
|
||||
: null;
|
||||
|
||||
// 성토부선 + 다단 기슭막이(2026-08-22 — 보호공 삭제, 윗면 선만 성토부선으로).
|
||||
// 유출측과 **집수정 계류측**이 같은 체계를 쓴다: 5m 넘으면 다단을 둘 수 있다.
|
||||
@@ -603,14 +614,21 @@ export function computeCulvertLayout(
|
||||
adjusts: (revetShift?.extras ?? []).map(adjustOf),
|
||||
equalize: equalizeExtras === true,
|
||||
});
|
||||
const basinExtras = buildExtrasAt(basinFillStart, {
|
||||
startBottomElevation: basinFillBottom,
|
||||
outward: inletInfo.outward,
|
||||
groundAt,
|
||||
limitOffset: inletInfo.limit,
|
||||
adjusts: (revetShift?.basinExtras ?? []).map(adjustOf),
|
||||
equalize: false,
|
||||
});
|
||||
// 독립 기슭막이(관 숨김)는 집수정이 없어 이 채널이 **유입측 벽의 성토부선·다단**이다 —
|
||||
// 유출측(`extras`)과 같은 시작점(관 하단 꼭짓점)·같은 `bextra` 키(2026-08-29 사용자).
|
||||
const basinExtras = buildExtrasAt(
|
||||
culvert.hidden_pipe ? (inletWall ? pipeCorners.inlet.bottom : null) : basinFillStart,
|
||||
{
|
||||
startBottomElevation: culvert.hidden_pipe
|
||||
? (inletWall?.bottomBack.elevation ?? 0)
|
||||
: basinFillBottom,
|
||||
outward: inletInfo.outward,
|
||||
groundAt,
|
||||
limitOffset: inletInfo.limit,
|
||||
adjusts: (revetShift?.basinExtras ?? []).map(adjustOf),
|
||||
equalize: false,
|
||||
},
|
||||
);
|
||||
|
||||
// ── 성토 사면 구간 확정. 벽 자리가 굳은 뒤에 물매를 역산해야 관 길이 맞춤(벽 이동)이
|
||||
// 접점을 다시 깨뜨리지 않는다 — 종전 어긋남의 직접 원인이 이 순서였다.
|
||||
|
||||
@@ -487,9 +487,11 @@ export function placeInletWall(input: {
|
||||
toeOffset: number;
|
||||
limitOffset: number;
|
||||
};
|
||||
/** 매몰-무교차 자리 금지(유출 벽 규칙) — 독립 기슭막이는 유입도 같은 규칙을 탄다. */
|
||||
requireCrossing?: boolean;
|
||||
}): PipeWallPlacement {
|
||||
const place = (adjust: WallAdjust): PipeWallPlacement =>
|
||||
placePipeWall({ ...input, adjust, requireCrossing: false });
|
||||
placePipeWall({ ...input, adjust, requireCrossing: input.requireCrossing === true });
|
||||
let placed = place(input.adjust);
|
||||
const guard = input.outletGuard;
|
||||
if (!guard) return placed;
|
||||
|
||||
@@ -253,8 +253,10 @@ function trimLinkedLayout(layout: CulvertLayout, link: CulvertLink): CulvertLayo
|
||||
// 집수정은 **소유 측점 횡단도 하나에만** 선다(2026-08-24 사용자 확정) — 기슭막이와
|
||||
// 달리 옆 측점으로 이어지는 구조물이 아니다. 계류측 다단·성토부선도 집수정에 딸린
|
||||
// 것이라 함께 뺀다. 3D에서는 소유 측점 하나가 자기 길이(기본 2m)만큼 스윕한다.
|
||||
basinExtras: [],
|
||||
basinFill: empty,
|
||||
// 단 독립 기슭막이(관 숨김)는 집수정이 없고 이 채널이 **유입측 벽의 다단·성토부선**
|
||||
// 이라, 유출측과 같은 규칙으로 그 벽의 연장을 따른다(2026-08-29 좌우 통일).
|
||||
basinExtras: spec.hidden_pipe && inletIn ? layout.basinExtras : [],
|
||||
basinFill: spec.hidden_pipe && inletIn ? layout.basinFill : empty,
|
||||
basin: null,
|
||||
designTrim: keepTrimSides(
|
||||
layout.designTrim,
|
||||
|
||||
@@ -47,9 +47,13 @@ export interface StructurePanelDeps {
|
||||
/** 집수정 좌우 — **화면 기준**(+ = 화면 왼쪽). 부호 환산·한계 토스트는 카드가 한다. */
|
||||
nudgeBasinLateral: (screenDeltaM: number) => void;
|
||||
resetBasin: () => void;
|
||||
/** 집수정 계류측 다단 상태·단 수 지정(2026-08-22 — 유출측과 같은 체계). */
|
||||
/** 집수정 계류측 다단 상태·단 수 지정(2026-08-22 — 유출측과 같은 체계).
|
||||
* 독립 기슭막이(관 숨김)에서는 집수정이 없으므로 이 채널이 **유입측 벽의 다단**이다. */
|
||||
basinExtraState: () => { canAdd: boolean; count: number };
|
||||
setBasinExtraCount: (count: number) => void;
|
||||
/** 독립 기슭막이(관 숨김) 카드인가 — 유입·유출 벽이 같은 성토 로직이라 조정창도
|
||||
* 좌우 같은 행을 준다(집수정 형식 숨김 · 유입측 단 수 행 표시 — 2026-08-29). */
|
||||
hiddenPipe: () => boolean;
|
||||
/** 이동 조작 가능 여부 — 집수정(자리 고정)은 숨긴다. */
|
||||
canNudge: (key: RevetKey) => boolean;
|
||||
/** 좌우·상하 걸음이 0.1m인가(독립·추가 기슭막이). 배관 기준벽은 false = 1m. */
|
||||
@@ -505,9 +509,12 @@ export function buildStructurePanel(deps: StructurePanelDeps): StructurePanelHan
|
||||
: movable
|
||||
? L("B06_Cross_Revet_Inlet")
|
||||
: L("B06_Cross_Struct_InletBasin");
|
||||
// 독립 기슭막이(관 숨김)는 유입·유출이 같은 성토 벽이다 — 집수정 형식 선택을
|
||||
// 숨기고 유입측에도 단 수 행을 준다(2026-08-29 사용자: 좌우 조작 통일).
|
||||
const ownTiers = deps.hiddenPipe() && key === "inlet";
|
||||
// 형식 선택은 유입측에서만, 이동·높이 조작은 기슭막이(집수정 제외)만.
|
||||
structureRow.classList.toggle("is-hidden", key !== "inlet");
|
||||
if (key === "inlet") {
|
||||
structureRow.classList.toggle("is-hidden", key !== "inlet" || deps.hiddenPipe());
|
||||
if (key === "inlet" && !deps.hiddenPipe()) {
|
||||
// 상황에 안 맞는 선택지는 숨긴다(2026-08-22 사용자). 단 지금 선택된 값은
|
||||
// 남긴다 — 숨기면 셀렉트가 빈 값이 된다.
|
||||
const allow = deps.optionsFor();
|
||||
@@ -528,10 +535,14 @@ export function buildStructurePanel(deps: StructurePanelDeps): StructurePanelHan
|
||||
basinRow.row.classList.toggle("is-hidden", !isBasin);
|
||||
basinReset.classList.toggle("is-hidden", !isBasin);
|
||||
const basinExtra = deps.basinExtraState();
|
||||
// 독립 기슭막이 유입측은 유출측과 같은 규칙 — 단 수 행이 **항상** 보인다.
|
||||
basinExtraRow.classList.toggle(
|
||||
"is-hidden",
|
||||
!(isBasin && (basinExtra.canAdd || basinExtra.count > 0)),
|
||||
!(ownTiers || (isBasin && (basinExtra.canAdd || basinExtra.count > 0))),
|
||||
);
|
||||
const basinExtraLabel = basinExtraRow.querySelector(".b06-structure-panel__label");
|
||||
if (basinExtraLabel)
|
||||
basinExtraLabel.textContent = ownTiers ? "다단 기슭막이(단)" : "집수정 계류측 기슭막이(단)";
|
||||
basinCount = basinExtra.count;
|
||||
basinCountValue.textContent = `${basinExtra.count}단`;
|
||||
// I형은 내공(높이·폭) 개념이 없다 — 9키의 **네 모서리**만 숨긴다.
|
||||
|
||||
@@ -138,11 +138,11 @@ export function structurePanelDeps(ctx: StructurePanelContext): StructurePanelDe
|
||||
canNudge: (key) => key !== "inlet" || !ctx.inletIsBasin(),
|
||||
// 걸음 분류(2026-08-29 사용자): **배관 기준벽만 1m**, 나머지(독립 기슭막이 기준벽·
|
||||
// 배관/독립 종속 추가 벽·옛 D경로 독립 벽)는 0.1m.
|
||||
fineMove: (key) =>
|
||||
(key !== "inlet" && key !== "outlet") || ctx.hiddenPipe(),
|
||||
fineMove: (key) => (key !== "inlet" && key !== "outlet") || ctx.hiddenPipe(),
|
||||
optionsFor: () => ctx.inletOptions(),
|
||||
extraState: () => ctx.extraState(),
|
||||
basinExtraState: () => ctx.basinExtraState(),
|
||||
hiddenPipe: () => ctx.hiddenPipe(),
|
||||
setExtraCount: (count) => {
|
||||
// 줄어들며 사라지는 벽이 선택 중이면 선택을 유출 벽으로 옮긴다(빈 대상 방지).
|
||||
const active = ctx.activeRevet() ?? "";
|
||||
|
||||
Reference in New Issue
Block a user