feat(rebar): per-member fck on ② for column, coping and footing
Each member can take its own fck on its ② card. A member without one uses the ③ 공통 fck, and fy stays common. The rebar table and the 3D model both read the resolved value. The I02 note stays until every fck member has its own value. The project file stores the values as optional fckByMember and refuses a bad value on open. Changing the fck is not a shape change, so it is not counted in the pending changes. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d8fd6adcf0
commit
7cf5835689
6 files changed
+175
-18
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { buildPier } from '../src/core/geometry';
|
||||
import { DEFAULT_MATERIAL, materialFor } from '../src/core/rebar/common';
|
||||
import { rebarSummary, type RebarRow } from '../src/core/rebar/summary';
|
||||
import { parseProject, SCHEMA_VERSION } from '../src/core/project';
|
||||
import { loadP1 } from './helpers';
|
||||
|
||||
const model = buildPier(loadP1());
|
||||
const byName = (rows: RebarRow[]) => new Map(rows.map((r) => [r.name, r]));
|
||||
const base = rebarSummary(model).rows;
|
||||
const members = (rows: RebarRow[], member: RebarRow['member']) => rows.filter((r) => r.member === member);
|
||||
/** Rows of one member whose length or count changed against the base. */
|
||||
const changedIn = (rows: RebarRow[], member: RebarRow['member']) =>
|
||||
members(rows, member)
|
||||
.filter((r) => {
|
||||
const b = byName(base).get(r.name)!;
|
||||
return b.length !== r.length || b.count !== r.count;
|
||||
})
|
||||
.map((r) => r.name);
|
||||
|
||||
describe('② 부재별 fck (spec §6.2 · ②)', () => {
|
||||
it('a member with its own fck uses it; the others keep the 공통 value', () => {
|
||||
const common = { ...DEFAULT_MATERIAL, fck: 30 };
|
||||
expect(materialFor(common, { coping: 35 }, 'coping').fck).toBe(35);
|
||||
expect(materialFor(common, { coping: 35 }, 'column').fck).toBe(30);
|
||||
expect(materialFor(common, { coping: 35 }, 'coping').fyS).toBe(common.fyS);
|
||||
});
|
||||
|
||||
it('a coping fck changes only the coping rows (C15 stair legs use the H19 development length)', () => {
|
||||
const rows = rebarSummary(model, DEFAULT_MATERIAL, { coping: 20 }).rows;
|
||||
expect(changedIn(rows, 'column')).toEqual([]);
|
||||
expect(changedIn(rows, 'footing')).toEqual([]);
|
||||
expect(changedIn(rows, 'coping')).toContain('C15');
|
||||
expect(byName(rows).get('C15')!.length!).toBeGreaterThan(byName(base).get('C15')!.length!);
|
||||
});
|
||||
|
||||
it('a column fck changes only the column rows', () => {
|
||||
const rows = rebarSummary(model, DEFAULT_MATERIAL, { column: 35 }).rows;
|
||||
expect(changedIn(rows, 'coping')).toEqual([]);
|
||||
expect(changedIn(rows, 'footing')).toEqual([]);
|
||||
expect(changedIn(rows, 'column').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('a footing fck changes only the footing rows (B2 splice)', () => {
|
||||
const rows = rebarSummary(model, DEFAULT_MATERIAL, { footing: 35 }).rows;
|
||||
expect(changedIn(rows, 'coping')).toEqual([]);
|
||||
expect(changedIn(rows, 'column')).toEqual([]);
|
||||
expect(changedIn(rows, 'footing')).toContain('B2');
|
||||
});
|
||||
|
||||
it('a per-member fck equal to the 공통 value changes nothing', () => {
|
||||
expect(rebarSummary(model, DEFAULT_MATERIAL, { coping: 27, column: 27, footing: 27 }).rows).toEqual(base);
|
||||
});
|
||||
|
||||
it('I02 stays while a member uses the assumed fck; it goes when all three members have their own fck', () => {
|
||||
const codes = (byMember: Parameters<typeof rebarSummary>[2]) => rebarSummary(model, DEFAULT_MATERIAL, byMember).warnings.map((w) => w.code);
|
||||
expect(codes({})).toContain('I02');
|
||||
expect(codes({ coping: 35, column: 35 })).toContain('I02');
|
||||
expect(codes({ coping: 35, column: 35, footing: 35 })).not.toContain('I02');
|
||||
});
|
||||
});
|
||||
|
||||
describe('② 부재별 fck: 프로젝트 파일', () => {
|
||||
const file = (fckByMember: unknown) => JSON.stringify({ schemaVersion: SCHEMA_VERSION, project: {}, source: {}, piers: [], history: [], fckByMember });
|
||||
|
||||
it('accepts known members with positive numbers, and an absent field', () => {
|
||||
expect(parseProject(file({ coping: 35, column: 30 })).fckByMember).toEqual({ coping: 35, column: 30 });
|
||||
expect(parseProject(file(undefined)).fckByMember).toBeUndefined();
|
||||
});
|
||||
|
||||
it('refuses an unknown member or a value that is not a positive number', () => {
|
||||
expect(() => parseProject(file({ bearing: 35 }))).toThrow('부재별 fck');
|
||||
expect(() => parseProject(file({ coping: 0 }))).toThrow('부재별 fck');
|
||||
expect(() => parseProject(file({ coping: '35' }))).toThrow('부재별 fck');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user