fix(abut-rebar): 벽체 W9 · W10 구간별 ⊓형 (양끝 다리 l_d)

- 사용자 도면(2026-11-04): 구간마다 독립된 ⊓, 양끝 계단 경계 − c (끝면 쪽은 W11 안쪽)에서 아래로 다리 l_d 735
- 낮은 이웃 구간으로의 직선 연장과 RB-AB06 피복 경고 삭제
- 계단 단면 삽도 · 안내 문구 반영

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-04 16:45:20 +09:00
1 parent 34959f5b4e
commit 74de932eb9
4 files changed
+25 -29

No files matched your search

+7 -11
View File
@@ -242,20 +242,16 @@ export function abutWallRebar(g: AbutWallGeom, footing: AbutFootingRebar, change
// 끝 여유 (§6.1): W5 · W6 · W9 · W10 끝은 W11 안쪽.
const endIn = (size: BarSize) => (c + (d(SZ.W11) + d(size)) / 2) / sin;
// W9 · W10 (§5.2): W2 바로 아래, 낮은 이웃 구간으로 l_d 연장, 높은 이웃 쪽은 경계에서 끝.
// 연장 부분이 그 이웃 교좌면 아래 피복 c를 지키지 못하면 경고만 한다 (RB-AB06, 사용자 결정 2026-11-04).
let thinCover = Infinity;
// W9 · W10 (§5.2, 사용자 도면 2026-11-04): W2 바로 아래, 구간마다 독립된 ⊓형. 양끝은 계단 경계 − c (끝면 쪽은 W11 안쪽)에서
// 아래로 l_d 꺾는다. 이웃 구간으로는 넘어가지 않는다.
segs.forEach((sg, k) => {
const mark: WallMark = k === 0 || k === last ? 'W9' : 'W10';
const z = g.HF + sg.H - c - (d(SZ.W2) + d(SZ.W9)) / 2;
for (const nb of [segs[k - 1], segs[k + 1]]) if (nb && nb.H < sg.H) thinCover = Math.min(thinCover, g.HF + nb.H - z);
const a = k === 0 ? endIn(SZ.W9) : segs[k - 1].H < sg.H ? sg.from - ld.H16 : sg.from;
const b = k === last ? g.L - endIn(SZ.W9) : segs[k + 1].H < sg.H ? sg.to + ld.H16 : sg.to;
for (const x of xW9) put(mark, [[a, x, z], [b, x, z]], b - a, sg === one || mark === 'W10');
const a = k === 0 ? endIn(SZ.W9) : sg.from + c;
const b = k === last ? g.L - endIn(SZ.W9) : sg.to - c;
for (const x of xW9) put(mark, [[a, x, z - ld.H16], [a, x, z], [b, x, z], [b, x, z - ld.H16]], b - a + 2 * ld.H16, sg === one || mark === 'W10');
});
if (thinCover < c - 1e-6) add('RB-AB06', 'W', `W9 · W10이 낮은 이웃 구간으로 l_d 연장되는 부분의 피복이 ${lenText(thinCover)}로 ${lenText(c)}보다 작습니다 (계단 높이 차 확인).`);
// W5 · W6 (§5.3): 단 z_k = H_F + c + k·s_v, 들어가는 구간이 이어진 범위마다 한 줄. 계단 쪽 끝 = 경계 − c.
const levelsOf = (H: number) => Math.floor((H - 2 * c) / sHoriz + 1e-9) + 1;
const zMax = g.HF + Math.max(...segs.map((sg) => sg.H)) - c;
@@ -338,8 +334,8 @@ export function abutWallRebar(g: AbutWallGeom, footing: AbutFootingRebar, change
push({ mark: 'W7', size: SZ.W7, role: '간격재', spacing: `${lenText(s.k7L * sVert)} × ${lenText(s.k7V * sHoriz)}`, actual: '—', shape: `양끝 90° 갈고리 ${h13}`, position: `내측 간격재 · W3 선 ~ W5 선`, note: `${s.k7L}·s × ${s.k7V}·s · 철근표 H13 · skew 방향 (1/sin θ)`, basis: '규칙', modified: s.k7L !== 8 || s.k7V !== 4 });
push({ mark: 'W8', size: SZ.W8, role: '수평', spacing: hSp, actual: hSp, shape: `ㄷ형 · 다리 l_d ${ld.H13}`, position: `양 끝면 (skew) · W11 안쪽 ${lenText(in8 * sin)}`, note: `낮은 끝 · 높은 끝 구간 높이로 단 수`, basis: '규칙', modified: hMod });
const tSp = lenText(sTop);
push({ mark: 'W9', size: SZ.W9, role: '계단 상면', spacing: tSp, actual: tSp, shape: `직선 · 낮은 쪽 이웃 구간으로 l_d ${ld.H16}`, position: `끝 계단 상면 · 교대축 · 폭 ${xW9.length}개`, note: `W2 바로 아래 · 첫 줄 ${lenText(xW9[0] ?? 0)} (W1 선 피함)`, basis: '규칙', modified: tMod });
push({ mark: 'W10', size: SZ.W10, role: '계단 상면', spacing: tSp, actual: tSp, shape: `직선 · 낮은 쪽 이웃 구간으로 l_d ${ld.H16}`, position: `중간 계단 상면 · 교대축 · 폭 ${xW9.length}개`, note: '결정: W9 끝 계단 / W10 중간 계단', basis: '규칙', modified: tMod });
push({ mark: 'W9', size: SZ.W9, role: '계단 상면', spacing: tSp, actual: tSp, shape: `⊓ · 양끝 다리 l_d ${ld.H16} (경계 − ${lenText(c)})`, position: `끝 계단 상면 · 교대축 · 폭 ${xW9.length}개`, note: `W2 바로 아래 · 첫 줄 ${lenText(xW9[0] ?? 0)} (W1 선 피함)`, basis: '규칙', modified: tMod });
push({ mark: 'W10', size: SZ.W10, role: '계단 상면', spacing: tSp, actual: tSp, shape: `⊓ · 양끝 다리 l_d ${ld.H16} (경계 − ${lenText(c)})`, position: `중간 계단 상면 · 교대축 · 폭 ${xW9.length}개`, note: '결정: W9 끝 계단 / W10 중간 계단', basis: '규칙', modified: tMod });
push({ mark: 'W11', size: SZ.W11, role: '연직', spacing: `${lenText(s.sW)} / 300`, actual: '—', shape: `ㄷ형 · 아래 · 위 90° 갈고리 ${h16} (안쪽)`, position: `끝면 최외측 연직 · x 0 … ${lenText(xW5)} @${lenText(s.sW)} + ${xs11.map(lenText).join(' · ')}`, note: '규격 H16 제안 (삽도 표기 없음) · 흉벽 P11로 이어짐', basis: '가정', modified: false });
return {
+2 -2
View File
@@ -3265,7 +3265,7 @@ function fillAbutWallRebarCard(box: HTMLDivElement, rec: AbutRecord, built: { ge
'figcaption',
state.abutWallView === 'sec'
? '교대축 직각 단면 · 구간 1 (IP3 끝) · 왼쪽 배면 → 오른쪽 전면 · 선 = 연직 · W2 · W7, 점 = W5 · W6 · W9 · W10'
: '교대축 단면 · 전면 철근 · 왼쪽 IP3 = 구간 1 → 오른쪽 IP1 · W5는 계단에서 끊김, W9 · W10은 낮은 쪽 이웃 구간으로 l_d 연장',
: '교대축 단면 · 전면 철근 · 왼쪽 IP3 = 구간 1 → 오른쪽 IP1 · W5는 계단에서 끊김, W9 · W10은 구간마다 ⊓ (양끝 경계 − 피복에서 다리 l_d)',
'muted small',
),
);
@@ -3274,7 +3274,7 @@ function fillAbutWallRebarCard(box: HTMLDivElement, rec: AbutRecord, built: { ge
box.append(
el(
'p',
`벽체 높이 구간 ${[...geom.segments].sort((a, b) => a.no - b.no).map((sg) => `${sg.no} ${Math.round(sg.H)}`).join(' · ')} · 수평 W5는 높이가 들어가는 구간까지만 (위 계단 면 − ${lenText(s.c)}에서 끊음) · W9 · W10은 낮은 쪽 이웃 구간으로 l_d ${r.ld.H16} 연장`,
`벽체 높이 구간 ${[...geom.segments].sort((a, b) => a.no - b.no).map((sg) => `${sg.no} ${Math.round(sg.H)}`).join(' · ')} · 수평 W5는 높이가 들어가는 구간까지만 (위 계단 면 − ${lenText(s.c)}에서 끊음) · W9 · W10은 구간마다 ⊓, 양끝 다리 l_d ${r.ld.H16}`,
'muted small',
),
);
+3 -1
View File
@@ -96,7 +96,9 @@ export function abutWallElevationSvg(g: AbutWallGeom, r: AbutWallRebar, pick: Ab
const [u1, , z1] = p.pts[p.pts.length - 1];
if (p.mark === 'W1') parts.push(seg('W1', [p.pts[1][0], p.pts[1][2]], [u1, z1], 0.7));
else if (p.mark === 'W5') parts.push(seg('W5', [u0, z0], [u1, z1], 0.8));
else if ((p.mark === 'W9' || p.mark === 'W10') && Math.abs(t0 - r.x.W9[0]) < 1e-6) parts.push(seg(p.mark, [u0, z0], [u1, z1], 2));
else if ((p.mark === 'W9' || p.mark === 'W10') && Math.abs(t0 - r.x.W9[0]) < 1e-6) {
for (let i = 1; i < p.pts.length; i++) parts.push(seg(p.mark, [p.pts[i - 1][0], p.pts[i - 1][2]], [p.pts[i][0], p.pts[i][2]], 2));
}
else if (p.mark === 'W11' && Math.abs(t0 - w11Front) < 1e-6) {
// ㄷ형: 연직 + 위 갈고리 (아래 갈고리는 저판 안).
parts.push(seg('W11', [p.pts[1][0], p.pts[1][2]], [p.pts[2][0], p.pts[2][2]], 2), seg('W11', [p.pts[2][0], p.pts[2][2]], [u1, z1], 2));