fix(bearing): C4 and C5 are the 교각축 distances from the bearing to the block edges (승인 2026-10-26)
- bearingEdgeDistances measures along the 교각축: 분리 600 / 600; 일체 from the pair block centred on the mean 교각축 position (P1 outer margins 557). - Table headings updated; C1 · C2 · C3 on the plan are measured on the 교축 from the drawn positions. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
29acfba1e1
commit
2f67efd449
4 files changed
+31
-36
No files matched your search
+13
-22
@@ -2,7 +2,7 @@ import type { BlockModel, PierModel } from './geometry';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 받침 배치 규칙 (승인 2026-10-26).
|
* 받침 배치 규칙 (승인 2026-10-26).
|
||||||
* - C4·C5: 교축 방향, 받침 중심과 받침블럭 연단 사이 거리 (승인 2026-10-26). 블록 형상에서 계산하고 도면값과 비교한다.
|
* - C4·C5: 교각축 방향, 받침 중심과 받침블럭 연단 사이 거리 (승인 2026-10-26, 교각축 방향으로 확정). 블록 형상에서 계산하고 도면값과 비교한다.
|
||||||
* - 일체: 같은 순서끼리 (전열 1 · 후열 1, 전열 2 · 후열 2 …, 각 열의 번호 순) 두 받침의 편심거리가 100 mm를 넘으면 분리를 우선한다.
|
* - 일체: 같은 순서끼리 (전열 1 · 후열 1, 전열 2 · 후열 2 …, 각 열의 번호 순) 두 받침의 편심거리가 100 mm를 넘으면 분리를 우선한다.
|
||||||
* (P1 은 전열 1–5, 후열 6–10 으로 번호가 매겨져 있어서 번호 자체가 아니라 열 안의 순서로 짝짓는다.)
|
* (P1 은 전열 1–5, 후열 6–10 으로 번호가 매겨져 있어서 번호 자체가 아니라 열 안의 순서로 짝짓는다.)
|
||||||
* 편심거리 = 두 받침 중심의 교각축 방향 차이 |s(전열) − s(후열)|.
|
* 편심거리 = 두 받침 중심의 교각축 방향 차이 |s(전열) − s(후열)|.
|
||||||
@@ -12,10 +12,10 @@ export const PAIR_ECCENTRICITY_LIMIT_MM = 100;
|
|||||||
export interface EdgeDistance {
|
export interface EdgeDistance {
|
||||||
no: number;
|
no: number;
|
||||||
row: BlockModel['row'];
|
row: BlockModel['row'];
|
||||||
/** 교축 방향: 받침 중심에서 블록의 낮은 쪽(−n) 연단까지 (mm) = C4. null = 쌍 안쪽 (C4·C5 아님). */
|
/** 교각축 방향: 받침 중심에서 블록의 낮은 쪽(−s) 연단까지 (mm) = C4. */
|
||||||
leftMm: number | null;
|
leftMm: number;
|
||||||
/** 교축 방향: 받침 중심에서 블록의 높은 쪽(+n) 연단까지 (mm) = C5. null = 쌍 안쪽 (C4·C5 아님). */
|
/** 교각축 방향: 받침 중심에서 블록의 높은 쪽(+s) 연단까지 (mm) = C5. */
|
||||||
rightMm: number | null;
|
rightMm: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PairEccentricity {
|
export interface PairEccentricity {
|
||||||
@@ -28,27 +28,18 @@ export interface PairEccentricity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* C4·C5 from the block geometry: distances along the 교축 from each bearing centre to the block edges (mm).
|
* C4·C5 from the block geometry: distances along the 교각축 from each bearing centre to the block edges (mm).
|
||||||
* 분리: the 1200 block is centred on its bearing, so both are 600. 일체: the pair's 2500 block, so the outer
|
* 분리: the 1200 block is centred on its bearing, so both are 600. 일체: the pair's block is centred on the pair's mean
|
||||||
* margins are 600 (C1) and the inner ones run to the other bearing.
|
* 교각축 position, so each bearing's margins follow its own offset (P1: the outer margins 557).
|
||||||
*/
|
*/
|
||||||
export function bearingEdgeDistances(m: PierModel): EdgeDistance[] {
|
export function bearingEdgeDistances(m: PierModel): EdgeDistance[] {
|
||||||
const HALF_SEPARATE = 0.6; // 1200 / 2, m
|
const HALF_DEPTH = 0.6; // 1200 / 2 along the 교각축, m
|
||||||
const HALF_INTEGRATED = 1.25; // 2500 / 2, m
|
|
||||||
return m.blocks.map((b) => {
|
return m.blocks.map((b) => {
|
||||||
const host = m.integratedBlocks.find((x) => (b.row === '전열' ? x.frontNo : x.backNo) === b.no);
|
const host = m.integratedBlocks.find((x) => (b.row === '전열' ? x.frontNo : x.backNo) === b.no);
|
||||||
if (!host) {
|
const centre = host ? host.s : b.s;
|
||||||
return { no: b.no, row: b.row, leftMm: (HALF_SEPARATE) * 1000, rightMm: HALF_SEPARATE * 1000 };
|
const low = centre - HALF_DEPTH;
|
||||||
}
|
const high = centre + HALF_DEPTH;
|
||||||
// 일체: only the outer edges are C4 (front, low side) and C5 (back, high side); the inner side faces the other bearing.
|
return { no: b.no, row: b.row, leftMm: (b.s - low) * 1000, rightMm: (high - b.s) * 1000 };
|
||||||
const low = host.n - HALF_INTEGRATED;
|
|
||||||
const high = host.n + HALF_INTEGRATED;
|
|
||||||
return {
|
|
||||||
no: b.no,
|
|
||||||
row: b.row,
|
|
||||||
leftMm: b.row === '전열' ? (b.n - low) * 1000 : null,
|
|
||||||
rightMm: b.row === '후열' ? (high - b.n) * 1000 : null,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -1608,14 +1608,13 @@ function renderBearingLayout(wrap: HTMLDivElement, pier: Pier): void {
|
|||||||
const table = el('table', undefined, 'grid-table rebar-table');
|
const table = el('table', undefined, 'grid-table rebar-table');
|
||||||
const head = el('thead');
|
const head = el('thead');
|
||||||
const hr = el('tr');
|
const hr = el('tr');
|
||||||
for (const h of ['받침', '열', 'C4 (교축 · 받침 ↔ 블록 바깥 연단, mm)', 'C5 (교축 · 받침 ↔ 블록 바깥 연단, mm)']) hr.appendChild(el('th', h));
|
for (const h of ['받침', '열', 'C4 (교각축 · 받침 ↔ 블록 연단, mm)', 'C5 (교각축 · 받침 ↔ 블록 연단, mm)']) hr.appendChild(el('th', h));
|
||||||
head.appendChild(hr);
|
head.appendChild(hr);
|
||||||
table.appendChild(head);
|
table.appendChild(head);
|
||||||
const body = el('tbody');
|
const body = el('tbody');
|
||||||
for (const e of edges) {
|
for (const e of edges) {
|
||||||
const tr = el('tr');
|
const tr = el('tr');
|
||||||
const fmt = (v: number | null) => (v === null ? '—' : v.toFixed(0));
|
for (const v of [String(e.no), e.row, e.leftMm.toFixed(0), e.rightMm.toFixed(0)]) tr.appendChild(el('td', v));
|
||||||
for (const v of [String(e.no), e.row, fmt(e.leftMm), fmt(e.rightMm)]) tr.appendChild(el('td', v));
|
|
||||||
body.appendChild(tr);
|
body.appendChild(tr);
|
||||||
}
|
}
|
||||||
table.appendChild(body);
|
table.appendChild(body);
|
||||||
|
|||||||
@@ -79,9 +79,10 @@ export function bearingPlanSvg(m: PierModel, pairIndex: number, overlay?: Bearin
|
|||||||
`<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="#55606E" stroke-width="6"></line><line x1="${x1}" y1="${y - 50}" x2="${x1}" y2="${y + 50}" stroke="#55606E" stroke-width="6"></line><line x1="${x2}" y1="${y - 50}" x2="${x2}" y2="${y + 50}" stroke="#55606E" stroke-width="6"></line><text x="${(x1 + x2) / 2}" y="${y + 110}" text-anchor="middle" font-size="90" fill="#18212B">${label}</text>`;
|
`<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="#55606E" stroke-width="6"></line><line x1="${x1}" y1="${y - 50}" x2="${x1}" y2="${y + 50}" stroke="#55606E" stroke-width="6"></line><line x1="${x2}" y1="${y - 50}" x2="${x2}" y2="${y + 50}" stroke="#55606E" stroke-width="6"></line><text x="${(x1 + x2) / 2}" y="${y + 110}" text-anchor="middle" font-size="90" fill="#18212B">${label}</text>`;
|
||||||
const y1 = 760;
|
const y1 = 760;
|
||||||
const y2 = 1000;
|
const y2 = 1000;
|
||||||
parts.push(dimLine(xLeft, X(f.n), y1, `C1 ${Math.round(d(f.no).leftMm ?? 600)}`));
|
// C1 and C2 are along the 교축 (horizontal), measured on the drawn positions.
|
||||||
|
parts.push(dimLine(xLeft, X(f.n), y1, `C1 ${Math.round(X(f.n) - xLeft)}`));
|
||||||
parts.push(dimLine(X(f.n), X(k.n), y1, `C2 ${Math.round(Math.abs(k.n - f.n) * 1000)}`));
|
parts.push(dimLine(X(f.n), X(k.n), y1, `C2 ${Math.round(Math.abs(k.n - f.n) * 1000)}`));
|
||||||
parts.push(dimLine(X(k.n), xRight, y1, `C1 ${Math.round(d(k.no).rightMm ?? 600)}`));
|
parts.push(dimLine(X(k.n), xRight, y1, `C1 ${Math.round(xRight - X(k.n))}`));
|
||||||
parts.push(dimLine(xLeft, xRight, y2, `C3 ${Math.round((xRight - xLeft))}`));
|
parts.push(dimLine(xLeft, xRight, y2, `C3 ${Math.round((xRight - xLeft))}`));
|
||||||
|
|
||||||
// C4 and C5 as in the 시안: vertical dimensions on the left of the blocks, from the top edge to the bearing row (C4) and
|
// C4 and C5 as in the 시안: vertical dimensions on the left of the blocks, from the top edge to the bearing row (C4) and
|
||||||
@@ -90,8 +91,8 @@ export function bearingPlanSvg(m: PierModel, pairIndex: number, overlay?: Bearin
|
|||||||
const halfDepth = 600;
|
const halfDepth = 600;
|
||||||
const vdim = (y1: number, y2: number, label: string) =>
|
const vdim = (y1: number, y2: number, label: string) =>
|
||||||
`<line x1="${xv}" y1="${y1}" x2="${xv}" y2="${y2}" stroke="#55606E" stroke-width="6"></line><line x1="${xv - 50}" y1="${y1}" x2="${xv + 50}" y2="${y1}" stroke="#55606E" stroke-width="6"></line><line x1="${xv - 50}" y1="${y2}" x2="${xv + 50}" y2="${y2}" stroke="#55606E" stroke-width="6"></line><text x="${xv - 90}" y="${(y1 + y2) / 2 + 30}" text-anchor="end" font-size="90" fill="#18212B">${label}</text>`;
|
`<line x1="${xv}" y1="${y1}" x2="${xv}" y2="${y2}" stroke="#55606E" stroke-width="6"></line><line x1="${xv - 50}" y1="${y1}" x2="${xv + 50}" y2="${y1}" stroke="#55606E" stroke-width="6"></line><line x1="${xv - 50}" y1="${y2}" x2="${xv + 50}" y2="${y2}" stroke="#55606E" stroke-width="6"></line><text x="${xv - 90}" y="${(y1 + y2) / 2 + 30}" text-anchor="end" font-size="90" fill="#18212B">${label}</text>`;
|
||||||
parts.push(vdim(-halfDepth, 0, `C4 ${Math.round(d(f.no).leftMm ?? 0)}`));
|
parts.push(vdim(-halfDepth, 0, `C4 ${Math.round(d(f.no).leftMm)}`));
|
||||||
parts.push(vdim(0, halfDepth, `C5 ${Math.round(d(k.no).rightMm ?? 0)}`));
|
parts.push(vdim(0, halfDepth, `C5 ${Math.round(d(k.no).rightMm)}`));
|
||||||
|
|
||||||
return `<svg viewBox="-1900 -1300 3800 2600" width="100%" style="max-width: 560px; height: auto" role="img" aria-label="교량받침 상세 평면: 쌍 ${pair.frontNo}·${pair.backNo}, 블록 한 줄, 교축·교각축 방향, C1·C2·C3, C4·C5, 도면값 겹쳐 보기">${parts.join('')}</svg>`;
|
return `<svg viewBox="-1900 -1300 3800 2600" width="100%" style="max-width: 560px; height: auto" role="img" aria-label="교량받침 상세 평면: 쌍 ${pair.frontNo}·${pair.backNo}, 블록 한 줄, 교축·교각축 방향, C1·C2·C3, C4·C5, 도면값 겹쳐 보기">${parts.join('')}</svg>`;
|
||||||
}
|
}
|
||||||
@@ -23,14 +23,18 @@ describe('받침 배치 규칙 (승인 2026-10-26)', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('일체: the outer margins stay 600 (C4 of the front bearing, C5 of the back bearing)', () => {
|
it('일체: C4 and C5 along the 교각축 from each bearing to the block edges, from the pair block (centred on the mean)', () => {
|
||||||
|
const ib = integrated.integratedBlocks[0];
|
||||||
const d = bearingEdgeDistances(integrated);
|
const d = bearingEdgeDistances(integrated);
|
||||||
// The 2500 block is centred on the pair midpoint, so the outer margins are 601 (within the 5 mm drawing tolerance of 600).
|
const f = d.find((x) => x.no === 1 && x.row === '전열')!;
|
||||||
expect(d.find((x) => x.no === 1 && x.row === '전열')!.leftMm).toBeCloseTo(600, -1);
|
const k = d.find((x) => x.no === 6 && x.row === '후열')!;
|
||||||
expect(d.find((x) => x.no === 6 && x.row === '후열')!.rightMm).toBeCloseTo(600, -1);
|
const fb = integrated.blocks.find((b) => b.no === 1)!;
|
||||||
// the inner sides face the other bearing of the pair: no C4 or C5 there
|
const kb = integrated.blocks.find((b) => b.no === 6)!;
|
||||||
expect(d.find((x) => x.no === 1 && x.row === '전열')!.rightMm).toBeNull();
|
expect(f.leftMm).toBeCloseTo((fb.s - (ib.s - 0.6)) * 1000, 6);
|
||||||
expect(d.find((x) => x.no === 6 && x.row === '후열')!.leftMm).toBeNull();
|
expect(f.rightMm).toBeCloseTo(((ib.s + 0.6) - fb.s) * 1000, 6);
|
||||||
|
expect(k.rightMm).toBeCloseTo(((ib.s + 0.6) - kb.s) * 1000, 6);
|
||||||
|
// the outer margins (front C4, back C5) are equal on P1
|
||||||
|
expect(f.leftMm).toBeCloseTo(k.rightMm, 6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('the front and back bearings of one number are about 85 mm apart on P1, so 일체 is allowed', () => {
|
it('the front and back bearings of one number are about 85 mm apart on P1, so 일체 is allowed', () => {
|
||||||
|
|||||||
Reference in new issue
Block a user