From 03bb1f9929ba27fa6eee4357e05771b5ee313494 Mon Sep 17 00:00:00 2001 From: ypshim Date: Fri, 23 Oct 2026 10:04:36 +0900 Subject: [PATCH] =?UTF-8?q?fix(rebar):=20stack=20the=20three=20C9=C2=B7C10?= =?UTF-8?q?=C2=B7C11=20=ED=8F=90=ED=95=A9=20to=20the=20right=20so=20they?= =?UTF-8?q?=20do=20not=20overlap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three 폐합 of one position were drawn at the same x, so their U bars overlapped. Loop i now sits at x + i·2d and its 삿갓 one bar (d) beside its U, so no U meets another loop's U or 삿갓. The 철근표 counts and lengths are unchanged. The rule is geometry only; the spec (§4.3) needs the stacking note. Co-Authored-By: Claude Haiku 5.5 --- src/core/rebar/coping.ts | 6 ++++++ src/view/rebar3d.ts | 12 +++++++----- tests/rebar-shear-loops.test.ts | 22 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 tests/rebar-shear-loops.test.ts diff --git a/src/core/rebar/coping.ts b/src/core/rebar/coping.ts index e6ce2c1..7a8eaf3 100644 --- a/src/core/rebar/coping.ts +++ b/src/core/rebar/coping.ts @@ -252,6 +252,12 @@ export function capLength(x: number, coping: number): number { return CAP.flat + 2 * lCap; } +/** + * Step (mm) between the three 폐합 at one position, to the right (수정 2026-10-23): a 삿갓 sits one bar (d) beside its U, + * so the next loop's U starts two bars on. Loop i is then at x + i·step and its 삿갓 at x + i·step + d, with no overlap. + */ +export const SHEAR_LOOP_STEP = 2 * NOMINAL_DIAMETER.H25; + /** Pieces per position: 3 closed stirrups × (U + cap) = 6. Total 270 for C9 + C10 + C11 (document). */ export const PIECES_PER_POSITION = 6; diff --git a/src/view/rebar3d.ts b/src/view/rebar3d.ts index 2b9b0d8..ea19ffc 100644 --- a/src/view/rebar3d.ts +++ b/src/view/rebar3d.ts @@ -2,7 +2,7 @@ import * as THREE from 'three'; import type { PierModel } from '../core/geometry'; import { assemblyEndOffset, assemblyJs, b10Levels, b9Bars, FOOTING_DEFAULTS, gridPositions, grid, layers, legLength, legOffsets, sideDepth } from '../core/rebar/footing'; import { bottomPiece, MAIN, widthLayout } from '../core/rebar/coping-main'; -import { C14_INSET, c14Bars, CAP, COPING_REBAR, shearBars, shearBarsMulti, shearUHeight, copingSideBars, SIDE_HOOK_LEG, stairBars, undersideAt } from '../core/rebar/coping'; +import { C14_INSET, c14Bars, CAP, COPING_REBAR, SHEAR_LOOP_STEP, shearBars, shearBarsMulti, shearUHeight, copingSideBars, SIDE_HOOK_LEG, stairBars, undersideAt } from '../core/rebar/coping'; import { columnRebar, COLUMN_P1 } from '../core/rebar/column'; import { barsAcross, BLOCK_REBAR } from '../core/rebar/bearing-block'; import { DEFAULT_MATERIAL, FckByMember, hookLength, Material, materialFor, NOMINAL_DIAMETER } from '../core/rebar/common'; @@ -139,13 +139,15 @@ export function buildRebar3D(m: PierModel, common: Material = DEFAULT_MATERIAL, const h = shearUHeight(x, L * 1000) / 1000; const bot = top - h; const capBot = top - (h - CAP.cover / 1000); - const xc = xm + mm(NOMINAL_DIAMETER.S25); for (let i = 0; i < 3; i++) { const a = legN(i); const b = legN(i + 3); - addBar(name, 0, [xm, bot, a], [xm, bot, b], dShear, COLOR.shear); - addBar(name, 0, [xm, bot, a], [xm, top, a], dShear, COLOR.shear); - addBar(name, 0, [xm, bot, b], [xm, top, b], dShear, COLOR.shear); + // The three 폐합 stack to the right, so no U meets another loop's U or 삿갓 (SHEAR_LOOP_STEP). + const xu = xm + mm(i * SHEAR_LOOP_STEP); + const xc = xu + mm(dShear); + addBar(name, 0, [xu, bot, a], [xu, bot, b], dShear, COLOR.shear); + addBar(name, 0, [xu, bot, a], [xu, top, a], dShear, COLOR.shear); + addBar(name, 0, [xu, bot, b], [xu, top, b], dShear, COLOR.shear); addBar(name, 0, [xc, top, a], [xc, top, b], dShear, COLOR.shear); addBar(name, 0, [xc, top, a], [xc, capBot, a], dShear, COLOR.shear); addBar(name, 0, [xc, top, b], [xc, capBot, b], dShear, COLOR.shear); diff --git a/tests/rebar-shear-loops.test.ts b/tests/rebar-shear-loops.test.ts new file mode 100644 index 0000000..12f6eab --- /dev/null +++ b/tests/rebar-shear-loops.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; +import { buildPier } from '../src/core/geometry'; +import { NOMINAL_DIAMETER } from '../src/core/rebar/common'; +import { PIECES_PER_POSITION, SHEAR_LOOP_STEP } from '../src/core/rebar/coping'; +import { buildRebar3D } from '../src/view/rebar3d'; +import { loadP1 } from './helpers'; + +const d = NOMINAL_DIAMETER.H25; + +describe('C9·C10·C11 폐합 (코핑철근.md §4 · 수정 2026-10-23)', () => { + it('the loop step leaves room for a 삿갓 (one bar beside its U) before the next loop\'s U', () => { + // Loop i: U at x_i, 삿갓 at x_i + d. Loop i+1: U at x_i + step. Clear if step − d ≥ d. + expect(SHEAR_LOOP_STEP - d).toBeGreaterThanOrEqual(d - 1e-9); + }); + + it('three 폐합 per position and 6 pieces each: the count and the 철근표 are unchanged', () => { + expect(PIECES_PER_POSITION).toBe(6); + const rb = buildRebar3D(buildPier(loadP1())); + // C10 has 9 positions, each with 3 loops of 6 bars (2 U legs, U base, 삿갓 top + 2 legs) = 6 bars per loop. + expect(rb.byName.get('C10')!.length).toBe(9 * 3 * 6); + }); +});