- 구조물 옵션 안내 「칸 더하기」 · 도면 → 면적 알림 「시트 더하기」 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
112 lines
4.7 KiB
TypeScript
112 lines
4.7 KiB
TypeScript
/* =============================================================================
|
||
* M02_MasterTemplete_AreaFromDrawing.ts
|
||
* 도면 → 면적산출 한 번 옮기기(PLAN 83-3 · 연동 아님) — 오른쪽 도면 패널(`AreaDrawing`)이 받은 표 자료를
|
||
* 면적 통합문서의 새 시트(「도면에서 n」)로 만듦. 기존 시트는 안 덮음 · 병합 · 열 폭 · 행 높이 반영.
|
||
* 표 자료 꼴 = CAD `helpers/table-from-drawing.ts` 의 `DrawingTable`(rows · merges · colWidths · rowHeights · 도면 mm).
|
||
* 스프레드시트 부품은 안 고침 — 시트 자료만 만들고 싣기는 공개 손잡이 `addSheet`(되돌리기 한 번 · onChange → 화면 고침 표시).
|
||
* ========================================================================== */
|
||
|
||
import { showToast } from "@ui/ui_template_elements";
|
||
import type { Cell, Sheet, Workbook } from "../A00_Common/spreadsheet/spreadsheet_types";
|
||
|
||
const TEXT = {
|
||
done: (name: string) =>
|
||
`도면 표를 「${name}」 시트로 옮김 — [저장]을 눌러야 저장 · 되돌리기 Ctrl+Z`,
|
||
loose: "표 선을 못 찾아 글 자리로 줄 · 칸을 나눔 — 칸이 맞는지 확인",
|
||
noHandle: "스프레드시트가 시트 추가를 아직 못 함 — 옮기지 않음",
|
||
noSheet: "면적산출 시트가 열려 있지 않음",
|
||
};
|
||
|
||
/** CAD 가 보낸 표 자료(`DrawingTable`) */
|
||
export interface DrawingTable {
|
||
kind: "table" | "grid" | "text";
|
||
rows: (string | number)[][];
|
||
merges: { row: number; col: number; rowSpan: number; colSpan: number }[];
|
||
colWidths: number[];
|
||
rowHeights: number[];
|
||
}
|
||
|
||
export const SHEET_PREFIX = "도면에서";
|
||
|
||
/** 도면 mm → 엑셀 열 폭(글자 단위 · 1글자 ≈ 7 px ≈ 1.85 mm) · 행 높이(pt = mm × 72 / 25.4) */
|
||
const CHAR_PER_MM = 0.54;
|
||
const PT_PER_MM = 72 / 25.4;
|
||
const clamp = (v: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, v));
|
||
const round1 = (v: number): number => Math.round(v * 10) / 10;
|
||
|
||
/** 0 → `A` · 27 → `AB`(`spreadsheet_address.colName` 과 같은 규칙) */
|
||
function colName(c: number): string {
|
||
let s = "";
|
||
for (let n = c + 1; n > 0; n = Math.floor((n - 1) / 26))
|
||
s = String.fromCharCode(65 + ((n - 1) % 26)) + s;
|
||
return s;
|
||
}
|
||
const a1 = (r: number, c: number): string => `${colName(c)}${r + 1}`;
|
||
|
||
/** 「도면에서 n」 — 안 쓴 가장 작은 n */
|
||
export function nextSheetName(book: Pick<Workbook, "시트">): string {
|
||
const used = new Set(book.시트.map((s) => s.이름));
|
||
let n = 1;
|
||
while (used.has(`${SHEET_PREFIX} ${n}`)) n += 1;
|
||
return `${SHEET_PREFIX} ${n}`;
|
||
}
|
||
|
||
/** 안 쓴 시트 id(`s1` …) */
|
||
function nextSheetId(book: Pick<Workbook, "시트">): string {
|
||
const used = new Set(book.시트.map((s) => s.id));
|
||
let n = book.시트.length + 1;
|
||
while (used.has(`s${n}`)) n += 1;
|
||
return `s${n}`;
|
||
}
|
||
|
||
/** 표 자료 → 새 시트(이름 · id 는 문서에서 안 쓴 것) */
|
||
export function tableToSheet(table: DrawingTable, book: Pick<Workbook, "시트">): Sheet {
|
||
const 칸: Record<string, Cell> = {};
|
||
table.rows.forEach((line, r) =>
|
||
line.forEach((value, c) => {
|
||
if (value === "" || value === null || value === undefined) return;
|
||
칸[a1(r, c)] = { 값: value };
|
||
}),
|
||
);
|
||
const 병합 = table.merges
|
||
.filter((m) => m.rowSpan > 1 || m.colSpan > 1)
|
||
.map((m) => `${a1(m.row, m.col)}:${a1(m.row + m.rowSpan - 1, m.col + m.colSpan - 1)}`);
|
||
const sheet: Sheet = { id: nextSheetId(book), 이름: nextSheetName(book), 칸 };
|
||
if (병합.length) sheet.병합 = 병합;
|
||
if (table.colWidths.length) {
|
||
sheet.열 = {};
|
||
table.colWidths.forEach((w, c) => {
|
||
if (w > 0) sheet.열![colName(c)] = { 폭: round1(clamp(w * CHAR_PER_MM, 2, 60)) };
|
||
});
|
||
}
|
||
if (table.rowHeights.length) {
|
||
sheet.행 = {};
|
||
table.rowHeights.forEach((h, r) => {
|
||
if (h > 0) sheet.행![String(r + 1)] = { 높이: round1(clamp(h * PT_PER_MM, 12, 100)) };
|
||
});
|
||
}
|
||
return sheet;
|
||
}
|
||
|
||
/** 스프레드시트 공개 손잡이 중 쓰는 것 — 시트 더하기(되돌리기 한 번 · onChange · 탭 줄은 부품 몫) */
|
||
export interface AddSheetHandle {
|
||
getDoc(): Workbook;
|
||
addSheet?: (sheet: Sheet, opts?: { activate?: boolean }) => unknown;
|
||
}
|
||
|
||
/** 받은 표 → 면적 새 시트(활성) · 알림 — 옮긴 시트 이름 · 못 하면 null */
|
||
export function importDrawingTable(
|
||
handle: AddSheetHandle | null,
|
||
table: DrawingTable,
|
||
): string | null {
|
||
if (!handle?.addSheet) {
|
||
showToast(handle ? TEXT.noHandle : TEXT.noSheet, "error");
|
||
return null;
|
||
}
|
||
const sheet = tableToSheet(table, handle.getDoc());
|
||
handle.addSheet(sheet, { activate: true });
|
||
showToast(TEXT.done(sheet.이름), "success");
|
||
if (table.kind === "text") showToast(TEXT.loose, "info");
|
||
return sheet.이름;
|
||
}
|