fix(abut-rebar): 날개벽 WR9 = 저판 상면 헌치 보강 꺾임철근
- 사용자 도면(2026-11-04, 연직헌치 보강철근): 날개벽 방향 벽체 배면 ~ 뒷굽 끝 @2·s_h. 날개벽 수직 단면에서 배면 − c 연직 다리 (벽체 W9 · W10 정착장, 기본 330) → 헌치 빗변과 나란한 대각 (c 안쪽) → 저판 하면망 높이 → 날개벽 반대쪽 90° 갈고리 - 갈고리 끝이 저판 배면 + c 안에 드는 범위까지만 (skew로 뒤쪽으로 밀림): A1 우 4개 · 좌 3개 - 이전 WR9 (연직 헌치 대각 위 연직) 삭제. 카드 자동 값 · 철근표 문구 반영 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
81fe1f1549
commit
89ebf852a9
3 files changed
+45
-21
No files matched your search
+29
-16
@@ -5,7 +5,7 @@
|
||||
* ceil((L_heel − δ)/s) + 1개, 저판 하면망 위 갈고리. 변화단면(WR1 · WR11)은 끝 쪽으로 floor((끝 − c − 기준선 − e_min)/s)개, 아래 끝 경사 하단 + c.
|
||||
* - 수평: WR3–5 배면 (@s_v), WR13–15 전면 (@2·s_v), 띠 = 등단면 / 변화단면1 / 변화단면2. 맨 위 2단은 상단 경사와 나란히
|
||||
* (사용자 결정 2026-11-04, Q-WR1), 일반 단은 그 아래 s 이상. 교대 쪽 끝은 교대 단면을 지나 반대쪽 면 − c, 90° 갈고리.
|
||||
* - WR6 경사 하단 2개, WR7-1 · 7-2 · 7-3 헌치 꺾임 (전면 수평과 같은 단), WR9 헌치 대각 위 연직 (@2·s_h 자동 개수, Q-WR3),
|
||||
* - WR6 경사 하단 2개, WR7-1 · 7-2 · 7-3 헌치 꺾임 (전면 수평과 같은 단), WR9 저판 상면 헌치 보강 꺾임 (@2·s_h 자동 개수, 사용자 도면),
|
||||
* WR10 모서리 꺾임 (@s_v), WR20 간격재. 교대 철근과의 간섭은 보지 않는다 (Q-WR2 · Q-WR4 결정).
|
||||
* - 단축형 (QA-14 · §9): WR1 · WR11 · WR6 없음, 연직은 모두 등단면.
|
||||
* - 좌표: 교대 (s, t, z) — s 교대축 (IP1에서), t 교대축 직각 (전면 +), z 저판 하면에서. 날개벽 거리 d는 교대축 선에서 날개벽 방향.
|
||||
@@ -196,7 +196,8 @@ export function abutWingGeom(
|
||||
};
|
||||
}
|
||||
|
||||
export function abutWingRebar(g: AbutWingGeom, changes?: Partial<AbutWingRebarSettings>, material: Material = DEFAULT_MATERIAL, who = ''): AbutWingRebar {
|
||||
/** @param la9 WR9 연직 다리 = 벽체 W9 · W10 정착장 (벽체 카드 입력, 기본 330). */
|
||||
export function abutWingRebar(g: AbutWingGeom, changes?: Partial<AbutWingRebarSettings>, material: Material = DEFAULT_MATERIAL, who = '', la9 = 330): AbutWingRebar {
|
||||
const s = resolveWingRebar(changes);
|
||||
const issues: Issue[] = [];
|
||||
const side = g.side === 'left' ? '좌' : '우';
|
||||
@@ -396,21 +397,33 @@ export function abutWingRebar(g: AbutWingGeom, changes?: Partial<AbutWingRebarSe
|
||||
const len = leg + Math.hypot(B[0] - A[0], B[1] - A[1]) + Math.abs(tEnd - B[1]) + h25;
|
||||
put(mark, [P3(P0, z), P3(A, z), P3(B, z), P3(C, z), P3(C, z - h25)], len);
|
||||
}
|
||||
// WR9 (§6, 결정 2026-11-04): 아래 헌치 대각 위 연직 @2·s_h, 개수 = floor(대각 / 간격) + 1, 가운데 맞춤. 위 끝 브라켓 상면 − c (그 위는 헌치 없음).
|
||||
const corner = lineX(at(0, T), wDir, [0, g.wallBackT], [1, 0]);
|
||||
const hw = add2(corner, wDir, g.haunch);
|
||||
const ha = add2(corner, uOut, g.haunch);
|
||||
const mid: V2 = [(hw[0] + ha[0]) / 2, (hw[1] + ha[1]) / 2];
|
||||
const toC: V2 = [corner[0] - mid[0], corner[1] - mid[1]];
|
||||
const tc = Math.hypot(toC[0], toC[1]);
|
||||
const diagLen = Math.hypot(ha[0] - hw[0], ha[1] - hw[1]);
|
||||
}
|
||||
// WR9 (사용자 도면 2026-11-04, 연직헌치 보강철근): 저판 상면 헌치(날개벽 배면 ~ 저판 상면, 다리 g.haunch) 보강.
|
||||
// 날개벽 방향으로 벽체 배면 ~ 뒷굽 끝 @2·s_h (가운데 맞춤). 날개벽에 수직인 단면에서: 배면 − c 연직 다리 l_a (벽체 W9 · W10 정착장)
|
||||
// → 헌치 빗변과 나란히 c 안쪽 대각 → 저판 하면망 높이까지 → 날개벽 반대쪽으로 90° 갈고리.
|
||||
if (g.haunch > 0) {
|
||||
const sp9 = 2 * sBack;
|
||||
const n9 = Math.floor(diagLen / sp9 + 1e-9) + 1;
|
||||
const du: V2 = [(ha[0] - hw[0]) / diagLen, (ha[1] - hw[1]) / diagLen];
|
||||
const k9 = g.haunch - c * Math.SQRT2; // (깊이 − T) + (z − 저판 상면) = k9 on the bar line
|
||||
const zTopLeg = g.footTop + k9 + c; // at depth T − c
|
||||
const zLow = zHook16;
|
||||
const depthLow = T + k9 - (zLow - g.footTop);
|
||||
// 대각 · 갈고리가 날개벽 옆으로 ~1.4 m 가며 skew로 뒤쪽(t −)으로도 밀리므로, 갈고리 끝이 저판 배면 + c 안에 드는 범위까지만.
|
||||
const footBackT = -g.heelD * Math.abs(wt);
|
||||
const hookT0 = at(0, depthLow + h16)[1];
|
||||
const dKeep = wt < 0 ? (footBackT + c - hookT0) / wt : Infinity;
|
||||
const from = g.wallBackD + c;
|
||||
const to = Math.min(Math.min(g.heelD, g.L) - c, dKeep);
|
||||
const n9 = to > from ? Math.floor((to - from) / sp9 + 1e-9) + 1 : 0;
|
||||
const off = (to - from - (n9 - 1) * sp9) / 2;
|
||||
for (let i = 0; i < n9; i++) {
|
||||
const p = add2(add2(mid, du, (i - (n9 - 1) / 2) * sp9), [toC[0] / tc, toC[1] / tc], c);
|
||||
const ph = add2(p, tUnit, h16);
|
||||
put('WR9', [P3(ph, zHook16), P3(p, zHook16), P3(p, g.bracketTop - c)], g.bracketTop - c - zHook16 + h16);
|
||||
const dd = from + off + i * sp9;
|
||||
const pts: [number, number, number][] = [
|
||||
P3(at(dd, T - c), zTopLeg + la9),
|
||||
P3(at(dd, T - c), zTopLeg),
|
||||
P3(at(dd, depthLow), zLow),
|
||||
P3(at(dd, depthLow + h16), zLow),
|
||||
];
|
||||
put('WR9', pts, la9 + Math.hypot(depthLow - (T - c), zTopLeg - zLow) + h16);
|
||||
}
|
||||
}
|
||||
// WR10 (§7): 모서리 L 꺾임 @s_v, 저판 상면 + c ~ 흉벽 상단 − c. 교대 배면 · 날개벽 배면에서 각 120.65 안, 모서리 대각 300, 양끝 l_d.
|
||||
@@ -475,7 +488,7 @@ export function abutWingRebar(g: AbutWingGeom, changes?: Partial<AbutWingRebarSe
|
||||
push({ mark: 'WR7-1', size: SZ['WR7-1'], role: '헌치', spacing: hz2, actual: hz2, shape: `꺾임 · 날개벽 l_d ${ld.H25} · 헌치 대각 · 흉벽 지나 갈고리`, position: '위 헌치 · 브라켓 상면 ~ 상단', note: '날개벽 다리 WR3–5 안쪽 25.4 (Q-WR7)', basis: '규칙', modified: hMod });
|
||||
push({ mark: 'WR7-2', size: SZ['WR7-2'], role: '헌치', spacing: hz2, actual: hz2, shape: '꺾임 · 흉벽 + 브라켓 지나 갈고리', position: '아래 헌치 윗부분 · 교좌면 ~ 브라켓 상면', note: '띠 경계 브라켓 상면 / 교좌면 (Q-WR6)', basis: '규칙', modified: hMod });
|
||||
push({ mark: 'WR7-3', size: SZ['WR7-3'], role: '헌치', spacing: hz2, actual: hz2, shape: '꺾임 · 벽체 지나 갈고리', position: '아래 헌치 아랫부분 · 저판 상면 ~ 교좌면', note: 'WR8 결번', basis: '규칙', modified: hMod });
|
||||
push({ mark: 'WR9', size: SZ.WR9, role: '헌치', spacing: lenText(2 * sBack), actual: '—', shape: `아래 90° 갈고리 ${h16} (저판 안)`, position: '아래 헌치 대각 위 연직 · 위 끝 브라켓 상면 − c', note: `@${lenText(2 * sBack)} 자동 개수 (사용자 결정 2026-11-04, Q-WR3)`, basis: '규칙', modified: vMod });
|
||||
push({ mark: 'WR9', size: SZ.WR9, role: '헌치', spacing: lenText(2 * sBack), actual: lenText(2 * sBack), shape: `꺾임 · 연직 다리 ${lenText(la9)} · 헌치 빗변과 나란한 대각 · 저판 하면망에서 90° 갈고리 ${h16}`, position: '저판 상면 헌치 (날개벽 배면) · 벽체 배면 ~ 뒷굽 끝', note: `연직헌치 보강 (사용자 도면 2026-11-04) · @${lenText(2 * sBack)} 자동 개수 · 연직 다리 = 벽체 W9 · W10 정착장`, basis: '규칙', modified: vMod });
|
||||
push({ mark: 'WR10', size: SZ.WR10, role: '모서리', spacing: hz1, actual: hz1, shape: `꺾임 · 모서리 대각 300 · 양끝 l_d ${ld.H25}`, position: '벽체 · 흉벽 배면 ↔ 날개벽 배면 모서리 · 저판 상면 ~ 흉벽 상단', note: '헌치 철근과의 간섭은 보지 않음 (Q-WR4)', basis: '규칙', modified: hMod });
|
||||
push({ mark: 'WR20', size: SZ.WR20, role: '간격재', spacing: `${lenText(s.k20 * sBack)} × ${lenText(s.k20 * sBackV)}`, actual: '—', shape: `양끝 90° 갈고리 ${hook(SZ.WR20)}`, position: `내측 간격재 · 두께 ${lenText(T - 2 * (c + (d('H16') + d('H25')) / 2))} 걸침`, note: '철근표 H20은 오기 → H13', basis: '규칙', modified: s.k20 !== 8 });
|
||||
|
||||
|
||||
+4
-3
@@ -37,7 +37,7 @@ import { ABUT_WING_REBAR_DEFAULT, ABUT_WING_SIZES, abutWingGeom, abutWingRebar,
|
||||
import { ABUT_WING_COLORS, abutWingCornerSvg as abutWingRebarCornerSvg, abutWingFaceSvg } from './view/abut-wing-rebar-svg';
|
||||
import { ABUT_PARAPET_REBAR_DEFAULT, ABUT_PARAPET_SIZES, abutParapetGeom, abutParapetRebar, slabSpanAt, type AbutParapetGeom, type AbutParapetRebar, type AbutParapetRebarSettings } from './core/abut/rebar/parapet';
|
||||
import { ABUT_PARAPET_COLORS, abutParapetDowelSvg, abutParapetElevationSvg, abutParapetSectionSvg } from './view/abut-parapet-rebar-svg';
|
||||
import { ABUT_WALL_REBAR_DEFAULT, ABUT_WALL_SIZES, abutWallGeom, abutWallRebar, type AbutWallGeom, type AbutWallRebar, type AbutWallRebarSettings } from './core/abut/rebar/wall';
|
||||
import { ABUT_WALL_REBAR_DEFAULT, ABUT_WALL_SIZES, abutWallGeom, abutWallRebar, resolveWallRebar, type AbutWallGeom, type AbutWallRebar, type AbutWallRebarSettings } from './core/abut/rebar/wall';
|
||||
import { ABUT_WALL_COLORS, abutWallElevationSvg as abutWallRebarElevationSvg, abutWallSectionSvg as abutWallRebarSectionSvg } from './view/abut-wall-rebar-svg';
|
||||
import { lapLength } from './core/abut/rebar/splice';
|
||||
import { ABUT_FOOTING_COLORS, abutFootingRebarPlanSvg, abutFootingSectionLSvg, abutFootingSectionWSvg, type AbutRebarPick } from './view/abut-footing-rebar-svg';
|
||||
@@ -3165,7 +3165,8 @@ function abutWingsBuilt(rec: AbutRecord, footing: AbutFootingRebar): { geom: Abu
|
||||
const material = materialFor(state.material, state.fckByMember, 'abutBody');
|
||||
return buildWings(model, members, rec.wings, rec.wingDims).map((w) => {
|
||||
const geom = abutWingGeom(model, members, w, footing.z.B68);
|
||||
return { geom, result: abutWingRebar(geom, rec.rebar?.wing, material, rec.name) };
|
||||
// WR9 연직 다리 = 벽체 W9 · W10 정착장 (사용자 결정 2026-11-04).
|
||||
return { geom, result: abutWingRebar(geom, rec.rebar?.wing, material, rec.name, resolveWallRebar(rec.rebar?.wall).la) };
|
||||
});
|
||||
} catch {
|
||||
return [];
|
||||
@@ -3604,7 +3605,7 @@ function fillAbutWingRebarCard(box: HTMLDivElement, rec: AbutRecord, wings: { ge
|
||||
['배면 s · 전면 · 헌치 2s', `${lenText(s.sVert ?? s.sH)} · ${lenText(2 * (s.sVert ?? s.sH))}`],
|
||||
['연직 기준선 (뒷굽 끝 − c)', `전면 ${lenText(r.vertical.front.base)} · 배면 ${lenText(r.vertical.back.base)}`],
|
||||
['수평 단 (교대 쪽 · 끝)', `전면 ${r.levels.front.start} · ${r.levels.front.end} / 배면 ${r.levels.back.start} · ${r.levels.back.end}`],
|
||||
['WR9 개수 (헌치 대각, 자동)', String(r.bars.find((b) => b.mark === 'WR9')?.count ?? 0)],
|
||||
['WR9 (저판 상면 헌치 보강, 자동)', `${r.bars.find((b) => b.mark === 'WR9')?.count ?? 0}개 @${lenText(2 * (s.sVert ?? s.sH))} · 연직 다리 = 벽체 정착장 ${lenText(resolveWallRebar(rec.rebar?.wall).la)}`],
|
||||
[`l_d (fck ${fck})`, `H13 ${r.ld.H13} · H16 ${r.ld.H16} · H25 ${r.ld.H25}`],
|
||||
],
|
||||
bars: r.bars,
|
||||
|
||||
@@ -56,8 +56,18 @@ describe('교대 날개벽 배근 A1 좌 (AD7, 교대날개벽철근.md §11)',
|
||||
expect(bar(L1, 'WR10')?.count).toBe(38);
|
||||
});
|
||||
|
||||
it('WR9: 아래 헌치 대각 (300 · √2 = 424) 위 @250 → 2개 (자동, Q-WR3)', () => {
|
||||
expect(bar(L1, 'WR9')?.count).toBe(Math.floor((300 * Math.SQRT2) / 250) + 1);
|
||||
it('WR9 (사용자 도면 2026-11-04): 저판 상면 헌치 보강, 벽체 배면 ~ 뒷굽 끝 @250 → 우 4개 · 좌 3개 (skew로 갈고리가 저판 배면 쪽으로 밀려 범위가 짧음); 연직 다리 330 → 헌치 빗변과 나란한 대각 → 하면망에서 갈고리 246', () => {
|
||||
expect(bar(abutWingRebar(geomOf('right'), undefined, fck27), 'WR9')?.count).toBe(4);
|
||||
expect(bar(L1, 'WR9')?.count).toBe(3);
|
||||
const p = L1.paths.find((q) => q.mark === 'WR9')!;
|
||||
expect(p.pts.length).toBe(4);
|
||||
expect(p.pts[0][2] - p.pts[1][2]).toBeCloseTo(330, 6); // 연직 다리
|
||||
expect(p.pts[2][2]).toBeCloseTo(p.pts[3][2], 6); // 갈고리는 수평
|
||||
expect(Math.hypot(p.pts[3][0] - p.pts[2][0], p.pts[3][1] - p.pts[2][1])).toBeCloseTo(246, 6);
|
||||
// 대각은 45° (헌치 빗변과 나란히): 수평 이동 = 높이 차.
|
||||
const run = Math.hypot(p.pts[2][0] - p.pts[1][0], p.pts[2][1] - p.pts[1][1]);
|
||||
expect(run).toBeCloseTo(p.pts[1][2] - p.pts[2][2], 3);
|
||||
expect(abutWingRebar(left, undefined, fck27, '', 400).paths.find((q) => q.mark === 'WR9')!.pts[0][2] - p.pts[1][2]).toBeCloseTo(400, 6);
|
||||
});
|
||||
|
||||
it('우측: 상단은 우측 흉벽 상단 47.180 기준 (저판 상면 위 5185)', () => {
|
||||
|
||||
Reference in new issue
Block a user