feat(abut): 벽체 높이 H_w = 받침 ① 교좌면 ~ 흉벽 상단 연직 (기본 3326)

- 사용자 도면(2026-11-04): 받침 ① 위치에서 받침 ① 교좌면부터 흉벽 상단까지 연직 거리. 모든 교대 기본값 3326 (QA-4 가정 2500 대체)
- 입력 이름 "벽체 높이 · 받침 ① 기준 연직", ② 정면도에 받침 ① 기준선과 H_w 치수
- ③ 흉벽 철근표 대표 단면 = 받침 ① 위치 (P1–P3는 그 단면에서 정확히 계산)
- 테스트: 흉벽 상단 IP1 48.006 / IP3 47.661 등 + 0.826 반영, 교대흉벽철근.md §11 재현은 2500을 넣어 유지, 3326 기본 확인 (P1 3945)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-04 17:20:05 +09:00
1 parent 51a36669a8
commit 5e64a4f2ed
17 files changed
+125 -71

No files matched your search

+1 -1
View File
@@ -24,7 +24,7 @@ const seatAt = (g: AbutParapetGeom, u: number) => {
const nearest = (paths: ParapetBarPath[], mark: ParapetMark, u: number, at = 1) =>
paths.filter((p) => p.mark === mark).reduce<ParapetBarPath | undefined>((a, b) => (!a || Math.abs(b.pts[at][0] - u) < Math.abs(a.pts[at][0] - u) ? b : a), undefined);
/** 교대축 직각 단면 at the 대표 단면 (흉벽 높이 = H_p): t 가로 (왼쪽 브라켓 배면 → 오른쪽 흉벽 전면), z 세로. */
/** 교대축 직각 단면 at the 대표 단면 (받침 ① 위치): t 가로 (왼쪽 브라켓 배면 → 오른쪽 흉벽 전면), z 세로. */
export function abutParapetSectionSvg(g: AbutParapetGeom, r: AbutParapetRebar, pick: AbutRebarPick = NO_PICK): string {
const u = r.uRef;
const seat = seatAt(g, u);
+12 -1
View File
@@ -90,7 +90,7 @@ export function abutWallSectionSvg(d: AbutWallDrawing): string {
}
/**
* 정면·배면도: 교좌면 계단(구간별 교좌면 EL), 흉벽 상단 경사, 받침 블럭, 수축줄눈, 저판. 왼쪽 IP3(① 쪽), 오른쪽 IP1.
* 정면·배면도: 교좌면 계단(구간별 교좌면 EL), 흉벽 상단 경사, 받침 블럭, 수축줄눈, 저판, 받침 ① 기준선과 벽체 높이 H_w. 왼쪽 IP3(① 쪽), 오른쪽 IP1.
*/
export function abutWallElevationSvg(d: AbutWallDrawing): string {
const k = 46;
@@ -118,6 +118,16 @@ export function abutWallElevationSvg(d: AbutWallDrawing): string {
parts.push(`<line x1="${X(s).toFixed(1)}" y1="${Y(parapetTopAt(d, s)).toFixed(1)}" x2="${X(s).toFixed(1)}" y2="${Y(d.footingTopEl).toFixed(1)}" stroke="${INK}" stroke-dasharray="5 3"/>`);
parts.push(label(X(s) + 4, Y(d.footingTopEl) - 8, `수축줄눈 ${j}`, 'start'));
}
// 벽체 높이 H_w (사용자 도면 2026-11-04): 받침 ① 연직 기준선, 받침 ① 교좌면 ~ 흉벽 상단.
const b1 = d.bearings.find((b) => b.no === 1) ?? d.bearings[0];
if (b1) {
const topB1 = parapetTopAt(d, b1.s);
const xb = X(b1.s);
parts.push(`<line x1="${xb.toFixed(1)}" y1="${(Y(topB1) - 14).toFixed(1)}" x2="${xb.toFixed(1)}" y2="${(Y(d.footingTopEl) + 6).toFixed(1)}" stroke="#c0392b" stroke-dasharray="10 3 2 3"/>`);
const xd = xb - 22;
parts.push(`<line x1="${xd.toFixed(1)}" y1="${Y(topB1).toFixed(1)}" x2="${xd.toFixed(1)}" y2="${Y(b1.seatEl).toFixed(1)}" stroke="#c0392b" marker-start="url(#hw-a)" marker-end="url(#hw-a)"/>`);
parts.push(`<text x="${(xd - 4).toFixed(1)}" y="${((Y(topB1) + Y(b1.seatEl)) / 2).toFixed(1)}" font-size="11" fill="#c0392b" text-anchor="end">H_w ${Math.round((topB1 - b1.seatEl) * 1000)}</text>`);
}
const first = d.segments.find((g) => d.lengthM >= g.sFrom && d.lengthM <= g.sTo) ?? d.segments[d.segments.length - 1];
const last = d.segments.find((g) => g.sFrom <= 0 && g.sTo >= 0) ?? d.segments[0];
parts.push(label(X(d.lengthM), Y(d.parapetTopEndEl) - 6, el(d.parapetTopEndEl), 'start'));
@@ -130,6 +140,7 @@ export function abutWallElevationSvg(d: AbutWallDrawing): string {
parts.push(label((X(0) + X(d.lengthM)) / 2, H - 8, `${d.lengthM.toFixed(3)} m`));
return [
`<svg viewBox="0 0 ${W.toFixed(0)} ${H.toFixed(0)}" width="100%" role="img" aria-label="정면·배면도: 흉벽 상단 ${t3(d.parapetTopEndEl)} (IP3) → ${t3(d.parapetTopStartEl)} (IP1), 교좌면 계단 ${d.segments.length}구간, 수축줄눈 ${d.shrinkJointCount}">`,
'<defs><marker id="hw-a" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#c0392b"/></marker></defs>',
...parts,
'</svg>',
].join('');