feat(shape): 꺾임점에서 겹치는 기초를 하나의 꺾인 기초로 합친다
꺾임을 넘는 기초를 합치지 않아, 기둥 간격이 기초 길이보다 짧은 꺾인 교각(산외교 P11: 2.75 m)에서 두 기초가 겹치고(콘크리트 쐐기 틈, 철근 겹침, 수량 이중 계산) 말뚝도 겹쳤다. 꺾임 전후 기초가 겹치거나 미터 퍼짐 안에서 맞닿으면 하나의 꺾인 기초로 본다: - 형상: 구간별 조각 둘(bent), 각 조각을 미터 퍼짐 + 10 mm 너머로 늘려 미터면에서 틈 없이 잇는다 - 말뚝: 편 길이에 한 번 배치하고 꺾임점 기준으로 구간을 나눈다 - 철근: 편 좌표의 한 기초(footing-units.ts)로 철근표와 3D가 같이 계산, 3D는 미터점에서 접는다 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ed84761449
commit
ac646d554c
6 files changed
+142
-46
No files matched your search
@@ -134,12 +134,17 @@ describe('bent coping model (stage 2)', () => {
|
||||
expect(buildPier(bentInput('직접기초')).piles).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('2주형 bent with columns on both sides of the kink: separate footings, one per stretch', () => {
|
||||
it('2주형 bent with columns on both sides of the kink: the overlapping footings become one bent footing (2026-11-10)', () => {
|
||||
const m = buildPier(bentInput('말뚝기초', '2주형', 10));
|
||||
expect(m.footings.map((f) => f.k)).toEqual([0, 1]);
|
||||
expect(m.footings.every((f) => Math.abs(f.length - 10.4) < 1e-9)).toBe(true);
|
||||
expect(m.piles).toHaveLength(40);
|
||||
expect(m.piles.filter((p) => p.k === 1)).toHaveLength(20);
|
||||
// The two 10.4 m footings overlap across the kink, so they are one bent footing, a part per stretch.
|
||||
expect(m.footings.map((f) => [f.k, f.bent?.part])).toEqual([[0, 0], [1, 1]]);
|
||||
// Piles are laid once over the unfolded footing: both stretches get some, and none overlap (they used to: 2 × 20).
|
||||
expect(m.piles.some((p) => p.k === 0) && m.piles.some((p) => p.k === 1)).toBe(true);
|
||||
const kink = m.stretches[1].base;
|
||||
const g = m.piles.map((p) => ({ g: p.k === 0 ? p.s : p.s + kink, n: p.n }));
|
||||
for (let i = 0; i < g.length; i++) for (let j = i + 1; j < g.length; j++) {
|
||||
expect(Math.hypot(g[i].g - g[j].g, g[i].n - g[j].n)).toBeGreaterThan(1.0);
|
||||
}
|
||||
const scene = buildPierScene(m);
|
||||
const footings = scene.parts.footings.children as THREE.Mesh[];
|
||||
expect(footings).toHaveLength(2);
|
||||
|
||||
+38
-25
@@ -10,6 +10,7 @@ import { multicolCopingInfo } from '../src/core/rebar/multicol-coping';
|
||||
import { copingFold, inWedge, isTransverse } from '../src/core/rebar/coping-fold';
|
||||
import { buildRebar3D } from '../src/view/rebar3d';
|
||||
import { shearB9 } from '../src/core/rebar/footing';
|
||||
import { footingUnits } from '../src/core/rebar/footing-units';
|
||||
import { loadP1 } from './helpers';
|
||||
|
||||
// 산외교 P11 (샘플01) with the coping bent 8.767° at IP4 (2026-11-10 검토): back ⑥ g 11.64, ⑦ g 18.49, kink g 15.14.
|
||||
@@ -210,35 +211,47 @@ describe('꺾인 코핑 3D 철근 (E, 2026-11-10)', () => {
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
describe('꺾인 교각의 확대기초 철근 (2026-11-10)', () => {
|
||||
// 3주형 as in the project (산외교 P11): columns ① ② share one 18.05 m footing in stretch 0, ③ has a 10.4 m footing in stretch 1.
|
||||
describe('꺾인 기초 (2026-11-10)', () => {
|
||||
// 3주형 as in the project (산외교 P11): footings ① ② (−0.10 … 17.95) and ③ (15.20 … 25.60) overlap 2.75 m across the kink.
|
||||
const m = buildPier({ ...p11(), type: '3주형', columnCount: 3 });
|
||||
const kink = m.stretches[1].base;
|
||||
const spread = (m.footings[0].width / 2) * Math.tan((8.767 * Math.PI) / 360);
|
||||
|
||||
it('each footing draws its rebar in its own stretch', () => {
|
||||
const rb = buildRebar3D(m);
|
||||
const footingNames = /^B\d/;
|
||||
const ks = new Set(rb.records.filter((r) => footingNames.test(r.name)).map((r) => r.k));
|
||||
expect(m.footings.map((f) => f.k)).toEqual([0, 1]);
|
||||
expect(ks).toEqual(new Set([0, 1]));
|
||||
}, 60000);
|
||||
|
||||
it('B9 leaves out only the column zones of the same stretch', () => {
|
||||
const rows = rebarSummary(m).rows;
|
||||
const expected = m.footings.reduce((sum, f) => {
|
||||
const cols = m.columns.filter((c) => c.k === f.k && Math.abs(c.s - f.s) <= f.length / 2).map((c) => (c.s - f.s) * 1000);
|
||||
return sum + shearB9(f.length * 1000, f.width * 1000, (f.top - f.bottom) * 1000, cols, 2000, m.footingRebar).count;
|
||||
}, 0);
|
||||
expect(rows.find((r) => r.name === 'B9')!.count).toBe(expected);
|
||||
it('the two footings become one bent footing: each part reaches past the mitre spread, one unfolded extent', () => {
|
||||
expect(m.footings.map((f) => [f.k, f.bent?.part])).toEqual([[0, 0], [1, 1]]);
|
||||
const [p0, p1] = m.footings;
|
||||
expect(p0.bent!.gFrom).toBeCloseTo(-0.1, 9);
|
||||
expect(p0.bent!.gTo).toBeCloseTo(kink + m.columns[2].s + 5.2, 9);
|
||||
expect(p0.s + p0.length / 2).toBeGreaterThanOrEqual(kink + spread);
|
||||
expect(p1.s - p1.length / 2).toBeLessThanOrEqual(-spread);
|
||||
});
|
||||
|
||||
it('footings of different lengths keep each length (no longest-for-all), spliced bars count both pieces', () => {
|
||||
const rows = rebarSummary(m).rows.filter((r) => r.member === 'footing');
|
||||
expect(m.footings.map((f) => f.length)).toEqual([18.05, 10.4]);
|
||||
const b5 = rows.find((r) => r.name === 'B5')!;
|
||||
expect(b5.pieces!.length).toBe(2);
|
||||
const total = b5.pieces!.reduce((s, p) => s + p.count, 0);
|
||||
// The 18.05 m footing's bars are two pieces each; the 10.4 m footing's one piece.
|
||||
expect(total).toBeGreaterThan(b5.count);
|
||||
it('rebar is one footing of the unfolded length with all three columns (no double count of the overlap)', () => {
|
||||
const units = footingUnits(m);
|
||||
expect(units).toHaveLength(1);
|
||||
expect(units[0].length).toBeCloseTo(m.footings[0].bent!.gTo - m.footings[0].bent!.gFrom, 9);
|
||||
expect(units[0].columnX).toHaveLength(3);
|
||||
const rows = rebarSummary(m).rows;
|
||||
const u = units[0];
|
||||
expect(rows.find((r) => r.name === 'B9')!.count).toBe(shearB9(u.length * 1000, u.width * 1000, (u.top - u.bottom) * 1000, u.columnX, 2000, m.footingRebar).count);
|
||||
});
|
||||
|
||||
it('3D draws the bent footing in both stretches, bars along the footing turning at the mitre', () => {
|
||||
const rb = buildRebar3D(m);
|
||||
const footing = rb.records.filter((r) => /^B\d/.test(r.name));
|
||||
expect(new Set(footing.map((r) => r.k))).toEqual(new Set([0, 1]));
|
||||
// Every footing bar point stays within the unfolded footing (no part drawn past its own stretch end).
|
||||
for (const r of footing) for (const p of r.pts) {
|
||||
const g = r.k === 0 ? p[0] : p[0] + kink;
|
||||
expect(g).toBeGreaterThan(-0.1 - spread - 1e-6);
|
||||
expect(g).toBeLessThan(m.footings[0].bent!.gTo + spread + 1e-6);
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
it('footings apart across the kink stay separate (2주형 sample: 10.4 m footings 4.9 m apart)', () => {
|
||||
const two = buildPier(p11());
|
||||
expect(two.footings.every((f) => f.bent === undefined)).toBe(true);
|
||||
expect(footingUnits(two)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user