Files
Pier_v1/src/core/abut/summary.ts
T
ypshimandClaude Opus 5.5 80e5302192 feat(abut): ② 날개벽 카드를 시안대로 작성 (좌우 같게 · 측별 치수 · 삽도 3개)
- 선택 줄 (타일): 좌우 같게(기본) / 따로 정하기, 편집할 측 (좌 · ① 쪽 / 우 · ⑤ 쪽), 형식 일반형(기본) / 단축형 / 없음
- 측별 치수 입력 (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>
2026-11-04 09:20:29 +09:00

96 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 교대 상세 (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),
]);
}