feat(view): bearing 공통 규칙 inputs (mortar h2, bearing h3, solplate T, margin) and recalculated E, L, W, T1, T2
On the bearing card, four inputs drive E = h1 + h2 + h3 + h4, L = A + margin, W = B + margin and T1/T2 = T ± L·|S|/2. The defaults are the drawing values, so every row gives back the drawing values; changed rows are marked. The values are screen state for now and do not change the model or the project file (Q16 stays open). Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
64795e0a19
commit
553c696638
4 files changed
+97
-1
No files matched your search
@@ -79,3 +79,25 @@ export function solplateTs(T: number, L: number, s: number): { T1: number; T2: n
|
||||
const half = (L * Math.abs(s)) / 200;
|
||||
return { T1: Math.round(T + half), T2: Math.round(T - half) };
|
||||
}
|
||||
|
||||
/** 공통 규칙 (2단계, 승인 2026-10-23): inputs that drive E, L, W, T1, T2. Defaults are the drawing's values. */
|
||||
export interface BearingRules {
|
||||
/** 모르타르 h2 (mm). */
|
||||
h2: number;
|
||||
/** 받침 높이 h3 (mm). */
|
||||
h3: number;
|
||||
/** 솔플레이트 중심 두께 T (mm). */
|
||||
T: number;
|
||||
/** 솔플 여유 (mm): L = A + margin, W = B + margin. */
|
||||
margin: number;
|
||||
}
|
||||
|
||||
export const BEARING_RULES_DEFAULT: BearingRules = { h2: 50, h3: 181, T: SOLPLATE_T_RULE, margin: 100 };
|
||||
|
||||
/** Values of one bearing with the 공통 규칙 applied: E = h1 + h2 + h3 + h4, L = A + margin, W = B + margin, T1/T2 from T. */
|
||||
export function applyBearingRules(row: BearingRefRow, rules: BearingRules): { E: number; L: number; W: number; T1: number; T2: number } {
|
||||
const sp = SOLPLATE_REF[row.no - 1];
|
||||
const L = row.A + rules.margin;
|
||||
const { T1, T2 } = solplateTs(rules.T, L, sp.s);
|
||||
return { E: row.h1 + rules.h2 + rules.h3 + row.h4, L, W: row.B + rules.margin, T1, T2 };
|
||||
}
|
||||
+51
-1
@@ -16,7 +16,10 @@ import { buildRebar3D, type RebarColorMode, type RebarScene } from './view/rebar
|
||||
import { DEFAULT_MATERIAL, FCK_MEMBERS, fyOf, GRADE_CHOICES, isAssumedMaterial, materialFrom, type BendFactors, type FckByMember, type FckMember, type GradeKey, type Material } from './core/rebar/common';
|
||||
import { buildQuantity, quantityRows, type Quantity, type QuantityLine } from './core/rebar/quantity';
|
||||
import { P1_COPING_GEOM } from './core/coping-geom';
|
||||
import { BEARING_ELEVATION_REF, SOLPLATE_REF, SOLPLATE_T_RULE, solplateTs } from './core/bearing-ref';
|
||||
import { applyBearingRules, BEARING_ELEVATION_REF, BEARING_RULES_DEFAULT, type BearingRules, SOLPLATE_REF, SOLPLATE_T_RULE, solplateTs } from './core/bearing-ref';
|
||||
|
||||
/** 공통 규칙 of the bearing card (2단계). Screen state for now; not yet saved to the project file. */
|
||||
let bearingRules: BearingRules = { ...BEARING_RULES_DEFAULT };
|
||||
import { allVisibleChecked, setVisibleChecked, type TypeFilter } from './core/selection';
|
||||
import { choose, defaultSummary, isSelectable, SHAPE_GROUPS, UNDEFINED_REASON, type MemberId } from './core/shape-options';
|
||||
import { parseProject, SCHEMA_VERSION, serializeProject, type ProjectFile, type ProjectInfo } from './core/project';
|
||||
@@ -1251,6 +1254,53 @@ function setMaterial(key: 'fck' | GradeKey, raw: string): void {
|
||||
function renderBearingRef(box: HTMLDivElement, pier: Pier): void {
|
||||
const m = pier.model;
|
||||
const wrap = el('div', undefined, 'bearing-ref');
|
||||
wrap.appendChild(el('p', '공통 규칙 (입력 · 기본값 = 도면값)', 'lbl'));
|
||||
const rules: [keyof BearingRules, string][] = [['h2', '모르타르 h2 (mm)'], ['h3', '받침 높이 h3 (mm)'], ['T', '솔플레이트 중심 T (mm)'], ['margin', '솔플 여유 (mm)']];
|
||||
const grid = el('div', undefined, 'edit-row');
|
||||
for (const [key, label] of rules) {
|
||||
const id = `bear-${key}`;
|
||||
const lab = el('label', label);
|
||||
lab.htmlFor = id;
|
||||
const input = el('input');
|
||||
input.id = id;
|
||||
input.type = 'number';
|
||||
input.step = 'any';
|
||||
input.value = String(bearingRules[key]);
|
||||
const changed = bearingRules[key] !== BEARING_RULES_DEFAULT[key];
|
||||
if (changed) input.classList.add('changed-input');
|
||||
input.addEventListener('change', () => {
|
||||
const v = Number(input.value);
|
||||
const min = key === 'margin' ? 0 : 0.0001;
|
||||
if (!Number.isFinite(v) || v < min) {
|
||||
setError(`${label}은(는) ${key === 'margin' ? '0 이상' : '0보다 큰'} 숫자여야 합니다.`);
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
bearingRules = { ...bearingRules, [key]: v };
|
||||
renderAll();
|
||||
});
|
||||
grid.append(lab, input, el('span', `기본 ${BEARING_RULES_DEFAULT[key]}`, 'muted small'));
|
||||
}
|
||||
wrap.appendChild(grid);
|
||||
wrap.appendChild(el('p', '규칙 적용 값 (E = h1 + h2 + h3 + h4, L = A + 여유, W = B + 여유, T1·T2 = T ± L·|S|/2)', 'lbl'));
|
||||
const ct = el('table', undefined, 'grid-table rebar-table');
|
||||
const ch = el('thead');
|
||||
const chr = el('tr');
|
||||
for (const h of ['No', '열', 'E', 'L', 'W', 'T1 (−측)', 'T2 (+측)', '도면 대비']) chr.appendChild(el('th', h));
|
||||
ch.appendChild(chr);
|
||||
ct.appendChild(ch);
|
||||
const cb = el('tbody');
|
||||
for (const r of BEARING_ELEVATION_REF) {
|
||||
const v = applyBearingRules(r, bearingRules);
|
||||
const d = applyBearingRules(r, BEARING_RULES_DEFAULT);
|
||||
const same = v.E === d.E && v.L === d.L && v.W === d.W && v.T1 === d.T1 && v.T2 === d.T2;
|
||||
cb.appendChild(el('tr')).append(
|
||||
el('td', String(r.no)), el('td', r.row), el('td', String(v.E)), el('td', String(v.L)), el('td', String(v.W)),
|
||||
el('td', String(v.T1)), el('td', String(v.T2)), el('td', same ? '같음' : '규칙 변경'),
|
||||
);
|
||||
}
|
||||
ct.appendChild(cb);
|
||||
wrap.appendChild(el('div', undefined, 'table-scroll')).appendChild(ct);
|
||||
wrap.appendChild(el('p', '교량받침 ELEVATION TABLE (도면값 · 읽기 전용, mm · EL m)', 'lbl'));
|
||||
wrap.appendChild(el('p', '도면값은 모델에 쓰지 않습니다(Q16). 아래 "차이"는 도면과 모델의 코핑 상면 차이입니다.', 'muted small'));
|
||||
const front = m?.segments ?? [];
|
||||
|
||||
@@ -793,3 +793,4 @@ body { background: #eef1f4; font-family: 'Noto Sans KR', system-ui, sans-serif;
|
||||
/* ② 교량받침 reference tables (읽기 전용) */
|
||||
.bearing-ref .table-scroll { overflow-x: auto; margin-bottom: 8px; }
|
||||
.bearing-ref table { font-size: 12px; }
|
||||
.bearing-ref input.changed-input { border: 2px solid #e8833a; background: #fff6ee; }
|
||||
@@ -41,3 +41,26 @@ describe('교량받침 reference (도면값, 읽기 전용, 1단계)', () => {
|
||||
for (const d of diffs) expect(d).toBeCloseTo(0.211, 2);
|
||||
});
|
||||
});
|
||||
|
||||
import { applyBearingRules, BEARING_RULES_DEFAULT } from '../src/core/bearing-ref';
|
||||
|
||||
describe('교량받침 공통 규칙 (2단계)', () => {
|
||||
it('with the drawing values, the rules give back the drawing E, L, W, T1, T2 on every bearing', () => {
|
||||
BEARING_ELEVATION_REF.forEach((r, i) => {
|
||||
const v = applyBearingRules(r, BEARING_RULES_DEFAULT);
|
||||
expect(v.E, `No ${r.no}`).toBe(r.E);
|
||||
expect(v.L, `No ${r.no}`).toBe(SOLPLATE_REF[i].L);
|
||||
expect(v.W, `No ${r.no}`).toBe(SOLPLATE_REF[i].W);
|
||||
expect(v.T1, `No ${r.no}`).toBe(SOLPLATE_REF[i].T1);
|
||||
expect(v.T2, `No ${r.no}`).toBe(SOLPLATE_REF[i].T2);
|
||||
});
|
||||
});
|
||||
|
||||
it('a 10 mm larger mortar h2 raises E by 10 and leaves L, W and T1, T2 unchanged', () => {
|
||||
const r = BEARING_ELEVATION_REF[0];
|
||||
const v = applyBearingRules(r, { ...BEARING_RULES_DEFAULT, h2: BEARING_RULES_DEFAULT.h2 + 10 });
|
||||
expect(v.E).toBe(r.E + 10);
|
||||
expect(v.L).toBe(r.A + 100);
|
||||
expect(v.T1).toBe(SOLPLATE_REF[0].T1);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user