- 선택 줄 (타일): 좌우 같게(기본) / 따로 정하기, 편집할 측 (좌 · ① 쪽 / 우 · ⑤ 쪽), 형식 일반형(기본) / 단축형 / 없음 - 측별 치수 입력 (WingDims, 교대 기록 wingDims): 길이 6500, 두께 450, 끝 높이 1500(일반형), 헌치 300. 단축형 길이 기본 = 뒷굽 끝(1.807 m). 좌우 같게이면 양쪽에 함께 적용, 켤 때 좌측 값을 우측에 복사 - 연동: 두께 → 날개벽 3D · 접속슬래브 폭(geometry slabWidthMm) · 슬래브 평면, 헌치 → 연직 헌치 · 저판 상면 헌치 (0이면 없음) - 자동 값: 경사 (A1 = −i), 등단면 · 변화단면 경계(뒷굽 끝), 끝 EL 상단 / 하단 (가정), 단축형 하단 = 확대기초 상면 (뒷굽 밖은 AI02 안내) - 삽도 3개 (src/view/abut-wing-svg.ts): 평면(좌우 날개벽 · 접속슬래브, abutPlanOutline 공유), 측면(수평 단 · 뒷굽 끝), 날개벽–흉벽 모서리 모식도(헌치 · 브라켓 · 슬래브 모따기 · 간격) - "생성되는 날개벽" 줄과 시안 메모. 프로젝트 파일 검사에 wingDims · wingsLinked 추가 - 테스트 7건 추가 · 갱신. 전체 669건 통과, 타입 검사 · 빌드 통과. 브라우저 시각 확인은 미실시 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
5.0 KiB
TypeScript
96 lines
5.0 KiB
TypeScript
/**
|
||
* 교대 상세 (2026-11-03, 교각 상세와 같은 개념): the values the 삽도 shows, computed from the model. Read only.
|
||
* Each row is [label, value]. Lengths in mm, levels in m, as in the 삽도.
|
||
*/
|
||
import type { AbutRecord } from './parse';
|
||
import { buildAbut, type AbutModel } from './geometry';
|
||
import { toAbutInput } from './parse';
|
||
import { buildMembers, type AbutMembers } from './members';
|
||
import { buildWings } from './wings';
|
||
|
||
const m3 = (v: number) => `${v.toFixed(3)} m`;
|
||
const mm = (v: number) => `${Math.round(v)} mm`;
|
||
|
||
export function abutSummaryRows(rec: AbutRecord): [string, string][] {
|
||
const model: AbutModel = buildAbut(toAbutInput(rec));
|
||
const members = buildMembers(model, { bottomEl: rec.foundationBottomEl, pileLengthMm: rec.pileLengthMm }, rec.shape);
|
||
const wings = buildWings(model, members, rec.wings, rec.wingDims);
|
||
const shape = members.shape;
|
||
const first = model.bearings[0];
|
||
const L = model.length;
|
||
const rows: [string, string][] = [
|
||
['형식 / 기초 형식', `${rec.type} · ${rec.foundation}`],
|
||
['교좌면 EL (구간 ①)', m3(rec.seatEl)],
|
||
['기초 하단 EL', m3(rec.foundationBottomEl)],
|
||
['지반고 EL', m3(rec.groundEl)],
|
||
['저판 상면 EL', m3(members.footingTopEl)],
|
||
['교대축 길이 (IP1 → IP3)', m3(L)],
|
||
['skew', `${rec.skewDeg.toFixed(2)}°`],
|
||
['종단경사 i', `${(rec.slopeI * 100).toFixed(2)} %`],
|
||
['받침블럭 높이 (패드 포함)', mm(first.blockHeightMm)],
|
||
['벽체 높이 (구간 ①)', m3(members.wallSegments.find((w) => Math.abs(w.seatEl - first.seatEl) < 1e-9)?.heightM ?? NaN)],
|
||
['벽체 두께 · 전면', `${Math.round(shape.wallFrontM * 1000 - shape.wallBackM * 1000)} mm · ${Math.round(shape.wallFrontM * 1000)} mm`],
|
||
['흉벽 높이 · 두께', `${Math.round(shape.parapetHeightM * 1000)} mm · ${Math.round(-shape.parapetBackM * 1000)} mm`],
|
||
['흉벽 상단 EL (IP1 끝 / IP3 끝)', `${members.parapetTopStartEl.toFixed(3)} / ${members.parapetTopEndEl.toFixed(3)} m`],
|
||
['브라켓 상면 EL (구간 ①)', m3(first.seatEl + shape.parapetHeightM - shape.bracketHeightM)],
|
||
['흉벽 경사', `${(members.parapetSlope * 100).toFixed(3)} %`],
|
||
['계단 경계 (IP3 쪽에서)', model.stepBounds.map((s) => (L - s).toFixed(3)).reverse().join(' / ') + ' m'],
|
||
['저판 폭 (앞굽 끝 / 뒷굽 끝)', `${Math.round(shape.footingFrontM * 1000)} / ${Math.round(shape.footingBackM * 1000)} mm`],
|
||
['말뚝 (열 × 본수)', `${members.piles.length} × ${members.piles[0]?.s.length ?? 0} = ${members.piles.length * (members.piles[0]?.s.length ?? 0)} 본`],
|
||
['말뚝 선단 EL', members.pileBottomEl === undefined ? '—' : m3(members.pileBottomEl)],
|
||
['수축줄눈 (ceil(L/10) − 1)', `${Math.max(0, Math.ceil(L / 10) - 1)} 개`],
|
||
];
|
||
if (wings.length > 0) {
|
||
const w = wings[0];
|
||
rows.push(['날개벽 경사', `${model.wing ? (model.wing.slopePct).toFixed(2) : '—'} %`]);
|
||
rows.push(['날개벽 뒷굽 거리 (교대축 기준)', mm(w.heelDistanceM * 1000)]);
|
||
}
|
||
if (model.wing) rows.push(['접속슬래브 폭', mm(model.wing.slabWidthMm)]);
|
||
return rows;
|
||
}
|
||
|
||
/** 입력값 rows of the 교대 panel (시안 "A1 입력값" card): what the user typed, read only. */
|
||
export function abutInputRows(rec: AbutRecord): [string, string][] {
|
||
const rows: [string, string][] = [
|
||
['형식 / 기초 형식', `${rec.type} (${rec.type === 'A1' ? '시점' : '종점'}) · ${rec.foundation}`],
|
||
['교좌면 EL (구간 ①)', `${rec.seatEl.toFixed(3)} m`],
|
||
['기초 하단 EL', `${rec.foundationBottomEl.toFixed(3)} m`],
|
||
['지반고 EL', `${rec.groundEl.toFixed(3)} m`],
|
||
['skew', `${rec.skewDeg.toFixed(2)}°`],
|
||
['종단경사 i', `${(rec.slopeI * 100).toFixed(2)} %`],
|
||
['말뚝 길이', `${Math.round(rec.pileLengthMm)} mm`],
|
||
['IP', 'IP1 → IP3 직선'],
|
||
['받침', `${rec.bearings.length}개 · z = 패드 상면`],
|
||
];
|
||
return rows;
|
||
}
|
||
|
||
/** The 8 detail rows shown in the collapsed 상세 (the rest stay in the 삽도 view). */
|
||
export const ABUT_DETAIL_LABELS = [
|
||
'벽체 높이 (구간 ①)',
|
||
'흉벽 상단 EL (IP1 끝 / IP3 끝)',
|
||
'브라켓 상면 EL (구간 ①)',
|
||
'저판 상면 EL',
|
||
'계단 경계 (IP3 쪽에서)',
|
||
'말뚝 (열 × 본수)',
|
||
'말뚝 선단 EL',
|
||
'접속슬래브 폭',
|
||
];
|
||
|
||
/** 구간 표 of the 벽체·흉벽 card (시안 하단): one row per 교좌면 step (2026-11-03). Values come from the model and members. */
|
||
export const ABUT_SEGMENT_HEADER = ['구간', '시작 (m)', '끝 (m)', '교좌면 EL (m)', '받침블럭 높이 (mm)', '벽체 높이 (m)'];
|
||
|
||
export function abutSegmentRows(model: AbutModel, members: AbutMembers): string[][] {
|
||
const circled = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧'];
|
||
const bearings = [...model.bearings].sort((a, b) => a.s - b.s);
|
||
return members.wallSegments.map((seg, k) => [
|
||
circled[k] ?? String(k + 1),
|
||
seg.sFrom.toFixed(3),
|
||
seg.sTo.toFixed(3),
|
||
seg.seatEl.toFixed(3),
|
||
String(bearings[k]?.blockHeightMm ?? '—'),
|
||
(seg.seatEl - members.footingTopEl).toFixed(3),
|
||
]);
|
||
}
|
||
|