fix(B06): ㄷ형 막음벽 미러·절/성토선, I형 관 시작 접지, 집수정 강조
2026-08-22 사용자 3건: - ㄷ형 막음벽 = I형 벽을 바닥부 중심 기준 수직 반전(대칭오차 0.000). 구조물 계류측 끝이 원지반 안이면 절토선(1:n), 밖이면 성토(되메움)선 5°(임시값)로 지반 교차점까지(fillLine 추가). - I형 관 시작 invert = 시작점 x(내공 1.0m)의 원지반(토피 상한 이내). 종전 노견 x 평가로 13+15.7에서 관이 지반에 1.07m 파묻히던 문제 해소 (현재 차 -0.016 = 토피 캡). 기슭막이 유입은 접점 0.000 정상 확인. - 집수정 선택 강조(is-active) — 기슭막이와 같은 규칙, 부재 전체. - 검증: 13+15.7 실조작 — I형/기슭막이 화면·수치, ㄷ형 미러·성토선, 강조 토글, 형식 전환 세션 왕복. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,8 @@ export function appendCulvertOverlay(
|
||||
...layout.walls.filter((w) => w.role === "inlet"),
|
||||
];
|
||||
const revetShapes = new Map<RevetKey, SVGPolygonElement>();
|
||||
/** 집수정 부재 도형 — 유입 선택 강조에 쓴다(2026-08-22 사용자 ③). */
|
||||
const basinShapes: SVGPolygonElement[] = [];
|
||||
for (const wall of wallsInOrder) {
|
||||
// 합성 단면(하부 사다리꼴 + 상부 평행사변형) — 상단 배면이 사면선 접점(사용자 ①·②).
|
||||
const revetShape = polygon(
|
||||
@@ -204,14 +206,14 @@ export function appendCulvertOverlay(
|
||||
? "사용자 선택 집수정(규칙상 기슭막이 자리)"
|
||||
: "유입측 성토사면 3m 이하 — 기슭막이 대신 집수정 기본";
|
||||
for (const part of basin.parts) {
|
||||
layer.append(
|
||||
polygon(
|
||||
part.points.map((p) => [x(p.offset), toDisplayY(p.elevation)] as [number, number]),
|
||||
"b06-chart__culvert-basin",
|
||||
`유입 집수정(${shapeName}) ${part.kind === "floor" ? "바닥" : "벽"}` +
|
||||
` — ${reasonText} · 내공 1.0m(실무 돌집수정), 벽은 기슭막이와 동일 단면`,
|
||||
),
|
||||
const shape = polygon(
|
||||
part.points.map((p) => [x(p.offset), toDisplayY(p.elevation)] as [number, number]),
|
||||
"b06-chart__culvert-basin",
|
||||
`유입 집수정(${shapeName}) ${part.kind === "floor" ? "바닥" : "벽"}` +
|
||||
` — ${reasonText} · 내공 1.0m(실무 돌집수정) · 부재 두께 0.2m`,
|
||||
);
|
||||
basinShapes.push(shape);
|
||||
layer.append(shape);
|
||||
}
|
||||
if (basin.cutLine) {
|
||||
const cut = document.createElementNS(SVG_NS, "line");
|
||||
@@ -227,6 +229,20 @@ export function appendCulvertOverlay(
|
||||
cut.append(cutTitle);
|
||||
layer.append(cut);
|
||||
}
|
||||
if (basin.fillLine) {
|
||||
// 구조물이 원지반 밖으로 나온 경우의 성토(되메움) 계획선 — 경사 5도(임시값,
|
||||
// 2026-08-22 사용자 지정).
|
||||
const fill = document.createElementNS(SVG_NS, "line");
|
||||
fill.setAttribute("x1", String(x(basin.fillLine.from.offset)));
|
||||
fill.setAttribute("y1", String(toDisplayY(basin.fillLine.from.elevation)));
|
||||
fill.setAttribute("x2", String(x(basin.fillLine.to.offset)));
|
||||
fill.setAttribute("y2", String(toDisplayY(basin.fillLine.to.elevation)));
|
||||
fill.setAttribute("class", "b06-chart__design-cross");
|
||||
const fillTitle = document.createElementNS(SVG_NS, "title");
|
||||
fillTitle.textContent = `집수정(${shapeName}) 성토(되메움)선 — 구조물이 원지반 밖으로 나와 5° 경사로 채움`;
|
||||
fill.append(fillTitle);
|
||||
layer.append(fill);
|
||||
}
|
||||
const label = document.createElementNS(SVG_NS, "text");
|
||||
label.setAttribute("x", String(x(basin.label.offset)));
|
||||
// 라벨은 I형 벽 **하단** 바깥에 붙인다(2026-08-20 사용자) — 글자 높이만큼 내린다.
|
||||
@@ -333,5 +349,7 @@ export function appendCulvertOverlay(
|
||||
// 강조는 클래스만 갈아 끼운다 — 카드를 다시 그리면 휠 줌·팬이 초기화된다.
|
||||
return (key) => {
|
||||
for (const [role, shape] of revetShapes) shape.classList.toggle("is-active", role === key);
|
||||
// 유입이 집수정이면 부재 전체를 함께 강조한다(2026-08-22 사용자 ③).
|
||||
for (const shape of basinShapes) shape.classList.toggle("is-active", key === "inlet");
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,8 +54,12 @@ export interface BasinBuildResult {
|
||||
trimElevation: number;
|
||||
}
|
||||
|
||||
/** 실무 내공 폭(m) — 울진 돌집수정. */
|
||||
const INNER_WIDTH_M = 1.0;
|
||||
/** 실무 내공 폭(m) — 울진 돌집수정. I형 관 시작점(노견+1.0m) 계산에도 쓴다. */
|
||||
export const BASIN_INNER_WIDTH_M = 1.0;
|
||||
const INNER_WIDTH_M = BASIN_INNER_WIDTH_M;
|
||||
|
||||
/** 집수정 주변 성토(되메움) 표면 경사(도) — 2026-08-22 사용자 지정 "일단 5도". */
|
||||
const BASIN_FILL_ANGLE_DEG = 5;
|
||||
|
||||
/** 집수정 단면을 만든다. 형태별 규칙은 파일 머리말 참조. */
|
||||
export function buildBasin(input: BasinBuildInput): BasinBuildResult {
|
||||
@@ -112,19 +116,43 @@ export function buildBasin(input: BasinBuildInput): BasinBuildResult {
|
||||
],
|
||||
});
|
||||
}
|
||||
// ㄷ형 = ㄴ형 + 반대측(도로측) 막음벽(I형 수직반전 — 2026-08-22 사용자 확정).
|
||||
// ㄷ형 = ㄴ형 + 막음벽 — I형 벽을 **바닥부 중심 기준 수직 반전**한 자리·형상
|
||||
// (2026-08-22 사용자 ① — 종전에는 노견 쪽에 세워 방향이 틀렸다).
|
||||
const floorCenter = (floorTopInner + floorOuter) / 2;
|
||||
const mirroredWallBase = 2 * floorCenter - wallBase;
|
||||
if (shape === "U") {
|
||||
parts.push({ kind: "wall", points: wallOf(anchor.offset, -outward) });
|
||||
parts.push({ kind: "wall", points: wallOf(mirroredWallBase, -outward) });
|
||||
}
|
||||
// ㄴ·ㄷ형이 원지반 **안쪽**에 박히면 그만큼 절토가 필요하다(2026-08-20 사용자 ①).
|
||||
// 구조물 계류측 끝(ㄴ형 = 바닥 바깥 끝 상단, ㄷ형 = 막음벽 계류측 상단 꼭짓점)과
|
||||
// 원지반의 관계(2026-08-22 사용자 ①):
|
||||
// · 원지반 안쪽에 박히면 → 표준단면 절토경사(1:n)로 **절토선**
|
||||
// · 원지반 밖으로 나오면 → **성토(되메움)선** — 경사 5도(임시값)로 지반까지.
|
||||
let basinCut: BasinLayout["cutLine"] = null;
|
||||
let basinFill: BasinLayout["cutLine"] = null;
|
||||
if (shape !== "I") {
|
||||
const outerTop = { offset: floorOuter, elevation: anchor.elevation };
|
||||
if (groundAt(floorOuter) > anchor.elevation + 0.05) {
|
||||
const refTop =
|
||||
shape === "U"
|
||||
? {
|
||||
offset: mirroredWallBase + outward * REVET_LEAN_RATIO * wallHeight,
|
||||
elevation: anchor.elevation + wallHeight,
|
||||
}
|
||||
: { offset: floorOuter, elevation: anchor.elevation };
|
||||
const groundHere = groundAt(refTop.offset);
|
||||
if (groundHere > refTop.elevation + 0.05) {
|
||||
for (let h = 0.05; h <= 20; h += 0.05) {
|
||||
const probe = floorOuter + outward * cutSlopeRatio * h;
|
||||
if (groundAt(probe) <= anchor.elevation + h) {
|
||||
basinCut = { from: outerTop, to: { offset: probe, elevation: anchor.elevation + h } };
|
||||
const probe = refTop.offset + outward * cutSlopeRatio * h;
|
||||
if (groundAt(probe) <= refTop.elevation + h) {
|
||||
basinCut = { from: refTop, to: { offset: probe, elevation: refTop.elevation + h } };
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (groundHere < refTop.elevation - 0.05) {
|
||||
const dropPerM = Math.tan((BASIN_FILL_ANGLE_DEG * Math.PI) / 180);
|
||||
for (let run = 0.05; run <= 30; run += 0.05) {
|
||||
const probe = refTop.offset + outward * run;
|
||||
const elevation = refTop.elevation - dropPerM * run;
|
||||
if (elevation <= groundAt(probe)) {
|
||||
basinFill = { from: refTop, to: { offset: probe, elevation } };
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -141,6 +169,7 @@ export function buildBasin(input: BasinBuildInput): BasinBuildResult {
|
||||
},
|
||||
reason,
|
||||
cutLine: basinCut,
|
||||
fillLine: basinFill,
|
||||
},
|
||||
// 관 끝선 = I형 벽의 안쪽(물받는 쪽 = 내공측) 변 복사(2026-08-22 사용자 확정).
|
||||
endFace: {
|
||||
|
||||
@@ -45,7 +45,7 @@ import type {
|
||||
export * from "./B06_Section_UI_Cross_Culvert_Const";
|
||||
export * from "./B06_Section_UI_Cross_Culvert_Types";
|
||||
|
||||
import { buildBasin } from "./B06_Section_UI_Cross_Culvert_Basin";
|
||||
import { BASIN_INNER_WIDTH_M, buildBasin } from "./B06_Section_UI_Cross_Culvert_Basin";
|
||||
import type { FillSlopeSegment, WallVertical } from "./B06_Section_UI_Cross_Culvert_Solve";
|
||||
import {
|
||||
clampToFace,
|
||||
@@ -146,6 +146,12 @@ export function computeCulvertLayout(
|
||||
// 벽 높이로만 노견에 연결한다(buildWall).
|
||||
if (basinReason && basinShape !== "I") {
|
||||
inlet.elevation = inletInfo.edge.elevation_m - BASIN_INNER_HEIGHT_M;
|
||||
} else if (basinReason) {
|
||||
// I형 — 관 시작점(내공 1.0m 자리) invert는 **그 x의 원지반 위**에 앉힌다(토피
|
||||
// 상한 이내). 종전에는 노견 x의 원지반을 써서 지반이 오르내리는 자리에서 관이
|
||||
// 지반에 파묻히거나 떴다(2026-08-22 사용자 ② — 13+15.7 확인).
|
||||
const startOffset = inlet.offset + inletInfo.outward * BASIN_INNER_WIDTH_M;
|
||||
inlet.elevation = Math.min(groundAt(startOffset), invertCap(inletInfo.edge));
|
||||
}
|
||||
|
||||
// 성토측 유입 기슭막이는 노견에 붙여 두지 않는다 — 사면선이 벽 이음선 상단점을 지나야
|
||||
|
||||
@@ -77,6 +77,8 @@ export interface BasinLayout {
|
||||
reason: "cut" | "short" | "manual";
|
||||
/** 집수정이 원지반 안으로 들어갈 때의 절토선(구조물 바깥 끝 → 지반 교차점). */
|
||||
cutLine: { from: OffsetPoint; to: OffsetPoint } | null;
|
||||
/** 구조물이 원지반 밖으로 나올 때의 성토(되메움)선 — 경사 5도(임시), 지반 교차점까지. */
|
||||
fillLine: { from: OffsetPoint; to: OffsetPoint } | null;
|
||||
}
|
||||
|
||||
/** 관 끝단을 자를 마감면 — 구조물(기슭막이·집수정)의 계류측 변을 **그대로 복사**한다. */
|
||||
|
||||
@@ -233,7 +233,7 @@
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* 유입 집수정(ㄴ형) — 절토측 또는 원지반 30% 폐색 시 자동 반영되는 박스 단면 */
|
||||
/* 유입 집수정 — 성토사면 ≤3m(절토 포함) 자동 또는 사용자 선택 단면 */
|
||||
.b06-chart__culvert-basin {
|
||||
fill: color-mix(in srgb, #8a7a5c 32%, transparent);
|
||||
stroke: #6f6248;
|
||||
@@ -241,6 +241,13 @@
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
/* 집수정 선택 강조(2026-08-22 사용자) — 기슭막이와 같은 규칙. */
|
||||
.b06-chart__culvert-basin.is-active {
|
||||
fill: color-mix(in srgb, var(--color-danger) 45%, transparent);
|
||||
stroke: var(--color-danger);
|
||||
stroke-width: 2.2;
|
||||
}
|
||||
|
||||
/* 유출부 돌붙임 — 성토사면을 따라 원지반까지 덮는 돌붙임 띠(실무 도면의 돌 연쇄) */
|
||||
.b06-chart__culvert-pitching {
|
||||
fill: color-mix(in srgb, #3fa34d 18%, transparent);
|
||||
|
||||
Reference in New Issue
Block a user