feat(abut): 접속슬래브 경사는 형식이 정함, 단면 · 접합 상세를 파단선 단면 하나로 합침, 흉벽 높이 입력
- 경사 선택 삭제: 매립형 = 하향 4 %, 노출형 = 종단경사 따라 (노트 표기). 예전 파일의 slope 값은 읽고 무시 - 단면 + 접합 상세: 흉벽 쪽과 먼 끝만 그리고 중간 생략, 부속재 · 치수(7000 · 450 · 850 · 295 · 헌치) · EL을 한 그림에 - 흉벽 높이 (브라켓 상면 ~ 흉벽 상단) 입력 = 벽체·흉벽 카드 브라켓 높이와 같은 값 - 자동 값: 브라켓 얹힘 길이 = 브라켓 폭 − 흉벽과 간격 (295) Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4b8b80ae97
commit
9835fb42b3
7 files changed
+157
-199
No files matched your search
+5
-12
@@ -6,7 +6,7 @@ import type { AbutRecord } from './parse';
|
||||
import type { WingForm } from './geometry';
|
||||
import { WING_DIMS_DEFAULT, wingDimsIssues, type WingDims } from './wings';
|
||||
import { ABUT_SHAPE_DEFAULT, normalizeShape, shapeIssues, type AbutShape } from './shape';
|
||||
import { resolveSlab, slabDefaultSlope, slabIssues, type AbutSlab, type SlabForm, type SlabSlope } from './slab';
|
||||
import { resolveSlab, slabIssues, type AbutSlab, type SlabForm } from './slab';
|
||||
import { abutBearingRuleIssues, resolveBearingRule, type AbutBearingRule } from './bearing-table';
|
||||
|
||||
export type AbutEditKey = 'seatEl' | 'foundationBottomEl' | 'groundEl' | 'pileLengthMm' | 'skewDeg' | 'slopeI';
|
||||
@@ -131,22 +131,15 @@ function slabFitIssues(slab: Partial<AbutSlab> | undefined, shape: Partial<AbutS
|
||||
return s.thicknessMm > h ? [`브라켓 높이(${h})는 접속슬래브 두께(${s.thicknessMm}) 이상이어야 합니다.`] : [];
|
||||
}
|
||||
|
||||
/** 접속슬래브 형식 (2026-11-04): picking a form also sets its slope (매립형 하향 4 %, 노출형 종단경사). */
|
||||
/** 접속슬래브 형식 (2026-11-04); the form also sets the slope (매립형 하향 4 %, 노출형 종단경사). */
|
||||
export function setSlabForm(rec: AbutRecord, form: SlabForm): AbutRecord {
|
||||
const slab: Partial<AbutSlab> = { ...(rec.slab ?? {}), form };
|
||||
delete slab.slope;
|
||||
const { slope: _old, ...kept } = (rec.slab ?? {}) as Partial<AbutSlab> & { slope?: unknown };
|
||||
const slab: Partial<AbutSlab> = { ...kept, form };
|
||||
const issues = slabIssues(resolveSlab(slab), bracketHeightMm(rec.shape));
|
||||
if (issues.length > 0) throw new Error(issues[0]);
|
||||
return { ...rec, slab };
|
||||
}
|
||||
|
||||
/** 접속슬래브 경사; the form's own slope is stored as "not set". */
|
||||
export function setSlabSlope(rec: AbutRecord, slope: SlabSlope): AbutRecord {
|
||||
const slab: Partial<AbutSlab> = { ...(rec.slab ?? {}), slope };
|
||||
if (slope === slabDefaultSlope(resolveSlab(rec.slab).form)) delete slab.slope;
|
||||
return { ...rec, slab };
|
||||
}
|
||||
|
||||
/** 접속슬래브 입력칸 (② 접속슬래브 카드, 시안 순서). 헌치 길이는 노출형만. */
|
||||
export const SLAB_EDIT_FIELDS: { key: 'lengthMm' | 'thicknessMm' | 'parapetGapMm' | 'wingGapMm' | 'haunchLengthMm'; label: string; exposedOnly?: boolean; note?: string }[] = [
|
||||
{ key: 'lengthMm', label: '길이 (mm)' },
|
||||
@@ -172,7 +165,7 @@ export const SHAPE_EDIT_FIELDS: { key: keyof AbutShape; label: string; group: 'w
|
||||
{ key: 'wallFrontMm', label: '교좌면 폭 (mm)', group: 'wall' },
|
||||
{ key: 'parapetHeightMm', label: '흉벽 높이 (mm)', group: 'wall' },
|
||||
{ key: 'parapetThicknessMm', label: '흉벽 두께 (mm)', group: 'wall' },
|
||||
{ key: 'bracketHeightMm', label: '브라켓 높이 (mm)', group: 'wall' },
|
||||
{ key: 'bracketHeightMm', label: '브라켓 높이 (mm)', group: 'wall', note: '= 접속슬래브 카드 흉벽 높이' },
|
||||
{ key: 'bracketWidthMm', label: '브라켓 폭 (mm)', group: 'wall' },
|
||||
{ key: 'shrinkJointCount', label: '수축줄눈 개수', group: 'wall' },
|
||||
{ key: 'parapetLevelLeftMm', label: '흉벽 상단 Level 좌 (mm)', group: 'wall', note: '기준 확인 QA-4' },
|
||||
|
||||
+8
-16
@@ -4,17 +4,15 @@
|
||||
* - 매립형 (type-1, 기본): 하면 = 브라켓 상면, 두께 일정. 브라켓 위(벽체 배면까지)는 수평이고 그 뒤로 경사(기본 하향 4 %).
|
||||
* - 노출형 (type-2, 2026-11-04 삽도 기준, QA-8): 상면 = 흉벽 상단에서 경사(기본 종단경사)를 따른다. 끝은 브라켓 상면에
|
||||
* 얹히고(끝 두께 = 브라켓 높이), 벽체 배면부터 헌치 길이 동안 두께가 줄어 그 뒤로는 두께가 일정하다.
|
||||
* - 경사: 형식을 고르면 그 형식의 기본값(매립형 하향 4 %, 노출형 종단경사)으로 바뀌고, 따로 바꿀 수 있다.
|
||||
* - 경사는 형식이 정한다 (2026-11-04): 매립형 = 하향 4 %, 노출형 = 종단경사 따라 (엑셀 A555 "type-1 = 하향경사 4 %, type-2 = 종단경사").
|
||||
* - t: 교대축 직각 (전면 +, 배면 −, m). 슬래브는 흉벽 배면에서 "흉벽과 간격"만큼 띄워 시작하고 길이만큼 배면 쪽으로 간다.
|
||||
* - 단면의 EL은 그 s의 흉벽 상단 EL에서 잰 값이다 (흉벽 상단 경사는 호출하는 쪽에서 더한다).
|
||||
*/
|
||||
|
||||
export type SlabForm = 'buried' | 'exposed';
|
||||
export type SlabSlope = 'down4' | 'grade';
|
||||
|
||||
export interface AbutSlab {
|
||||
form: SlabForm;
|
||||
slope: SlabSlope;
|
||||
lengthMm: number;
|
||||
thicknessMm: number;
|
||||
parapetGapMm: number;
|
||||
@@ -25,7 +23,6 @@ export interface AbutSlab {
|
||||
|
||||
export const ABUT_SLAB_DEFAULT: AbutSlab = {
|
||||
form: 'buried',
|
||||
slope: 'down4',
|
||||
lengthMm: 7000,
|
||||
thicknessMm: 450,
|
||||
parapetGapMm: 5,
|
||||
@@ -38,31 +35,26 @@ export const SLAB_FALL_PCT = 4;
|
||||
|
||||
export const SLAB_FORM_LABEL: Record<SlabForm, string> = { buried: '매립형', exposed: '노출형' };
|
||||
|
||||
/** The slope a form starts with: 매립형 하향 4 %, 노출형 종단경사 (엑셀 A555 "type-1 = 하향경사 4 %, type-2 = 종단경사"). */
|
||||
export function slabDefaultSlope(form: SlabForm): SlabSlope {
|
||||
return form === 'exposed' ? 'grade' : 'down4';
|
||||
}
|
||||
|
||||
/** The user's values over the defaults; the slope follows the form unless it was set. */
|
||||
/** The user's values over the defaults. A `slope` stored by an earlier version (2026-11-04 오전) is dropped: the form sets it. */
|
||||
export function resolveSlab(changes?: Partial<AbutSlab>): AbutSlab {
|
||||
const form = changes?.form ?? ABUT_SLAB_DEFAULT.form;
|
||||
return { ...ABUT_SLAB_DEFAULT, slope: slabDefaultSlope(form), ...(changes ?? {}) };
|
||||
const { slope: _old, ...rest } = (changes ?? {}) as Partial<AbutSlab> & { slope?: unknown };
|
||||
return { ...ABUT_SLAB_DEFAULT, ...rest };
|
||||
}
|
||||
|
||||
/** Keys whose value differs from the default (형식, 경사 and the dims), for the change marks. */
|
||||
/** Keys whose value differs from the default (형식 and the dims), for the change marks. */
|
||||
export function slabChangedKeys(changes?: Partial<AbutSlab>): (keyof AbutSlab)[] {
|
||||
const s = resolveSlab(changes);
|
||||
return (Object.keys(ABUT_SLAB_DEFAULT) as (keyof AbutSlab)[]).filter((k) => s[k] !== ABUT_SLAB_DEFAULT[k]);
|
||||
}
|
||||
|
||||
/** Slope (%), + = rises away from the wall: 하향 4 % → −4, 종단경사 → the wing slope (A1 = −i, A2 = +i). */
|
||||
/** Slope (%), + = rises away from the wall: 매립형 하향 4 % → −4, 노출형 종단경사 → the wing slope (A1 = −i, A2 = +i). */
|
||||
export function slabGradePct(slab: AbutSlab, wingSlopePct: number): number {
|
||||
return slab.slope === 'down4' ? -SLAB_FALL_PCT : wingSlopePct;
|
||||
return slab.form === 'buried' ? -SLAB_FALL_PCT : wingSlopePct;
|
||||
}
|
||||
|
||||
/** Slope text as on the card: "하향 4 %" or "종단경사 +1.91 %". */
|
||||
export function slabSlopeLabel(slab: AbutSlab, wingSlopePct?: number): string {
|
||||
if (slab.slope === 'down4') return `하향 ${SLAB_FALL_PCT} %`;
|
||||
if (slab.form === 'buried') return `하향 ${SLAB_FALL_PCT} %`;
|
||||
if (wingSlopePct === undefined) return '종단경사';
|
||||
return `종단경사 ${wingSlopePct >= 0 ? '+' : ''}${wingSlopePct.toFixed(2)} %`;
|
||||
}
|
||||
|
||||
+3
-1
@@ -144,7 +144,9 @@ export function checkStoredAbutments(list: unknown): void {
|
||||
}
|
||||
if (a.slab !== undefined) {
|
||||
for (const [key, v] of Object.entries(a.slab)) {
|
||||
const ok = key === 'form' ? v === 'buried' || v === 'exposed' : key === 'slope' ? v === 'down4' || v === 'grade' : finite(v);
|
||||
// 'slope' was stored by an earlier version (2026-11-04 오전) and is ignored now: the form sets the slope.
|
||||
const x: unknown = v;
|
||||
const ok = key === 'form' ? x === 'buried' || x === 'exposed' : key === 'slope' ? x === 'down4' || x === 'grade' : finite(x);
|
||||
if (!ok) throw new Error(`${who}: 접속슬래브 값 ${key}이(가) 맞지 않습니다.`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user