fix(M02): 단가산출 다이어그램 — 알약 값 쉼표 · 표 「· ○줄」 · 저절로 맞춤 90% 아래로 안 내림(PLAN 35-7 브레인 검증 ② ③ ⑥)
- 수는 천 단위 쉼표 · 소수 셋째 자리까지 · 끝 0 지움 · 글은 그대로 - 저절로 맞춤이 넘치면 왼쪽 위부터 보임 · 축소 단추로는 더 줄임 - 알약 대신 길(Model 준비 전) 걷음 · 682줄 · CostFlow 전부 검사 초록 - ORCA 견본 메쌓기: 97,882.512 · 268,908 · 0.364 · 표 · 40줄 · 처음 확대 0.9 · 한눈에 0.9 · 축소 0.75 · 알약 글 10.8px Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ly2oHAaTDYRjcniAmHR81z
This commit is contained in:
@@ -93,29 +93,9 @@ export interface DiagramOpts {
|
||||
|
||||
/* ── 알약 · 자리(DOM 없음) ─────────────────────────────────────────── */
|
||||
|
||||
/** 알약 — Model 이 아직이면 상자 칸에서 바로 뽑음 */
|
||||
/** 알약 — Model 의 받는 · 내보내는 알약 */
|
||||
export function pillsOf(box: CfBox): { ins: CfPill[]; outs: CfPill[] } {
|
||||
try {
|
||||
return { ins: portsIn(box), outs: portsOut(box) };
|
||||
} catch {
|
||||
if (box.kind === "input") {
|
||||
const inp = box.input;
|
||||
const outs = [
|
||||
...(inp?.elements ?? []).map((e) => ({ name: e.name, unit: e.unit })),
|
||||
...(inp?.table ? [{ name: "표", unit: "-" }] : []),
|
||||
...(inp?.variables ?? []).map((v) => ({ name: v.name, unit: v.unit })),
|
||||
];
|
||||
return { ins: [], outs };
|
||||
}
|
||||
if (box.kind === "function") {
|
||||
const f = box.func;
|
||||
return {
|
||||
ins: (f?.inputs ?? []).map((p) => ({ name: p.port, unit: "-" })),
|
||||
outs: f ? [{ name: f.output.name, unit: f.output.unit }] : [],
|
||||
};
|
||||
}
|
||||
return { ins: [{ name: "값", unit: box.unit }], outs: [] };
|
||||
}
|
||||
return { ins: portsIn(box), outs: portsOut(box) };
|
||||
}
|
||||
|
||||
/** 떨군 자리 → 받을 끝 · 상자에 떨구면 같은 이름 알약(받는 알약이 하나면 그것) · 못 정하면 까닭 */
|
||||
@@ -170,7 +150,10 @@ export function arrange(
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 모두 보이게 맞춤 — 상자 둘레 → 확대 · 판 자리 */
|
||||
/** 저절로 맞출 때 가장 작은 확대 — 알약 글이 너무 작아지지 않게(더 줄이기는 축소 단추로) */
|
||||
export const FIT_MIN = 0.9;
|
||||
|
||||
/** 모두 보이게 맞춤 — 상자 둘레 → 확대 · 판 자리 · FIT_MIN 아래로 안 내림(넘치면 왼쪽 위부터 보임) */
|
||||
export function fitView(
|
||||
rects: { x: number; y: number; w: number; h: number }[],
|
||||
view: { w: number; h: number },
|
||||
@@ -181,12 +164,11 @@ export function fitView(
|
||||
const y0 = Math.min(...rects.map((r) => r.y)) - pad;
|
||||
const x1 = Math.max(...rects.map((r) => r.x + r.w)) + pad;
|
||||
const y1 = Math.max(...rects.map((r) => r.y + r.h)) + pad;
|
||||
const zoom = clampZoom(Math.min(view.w / (x1 - x0), view.h / (y1 - y0), 1.5));
|
||||
return {
|
||||
zoom,
|
||||
panX: (view.w - (x1 - x0) * zoom) / 2 - x0 * zoom,
|
||||
panY: (view.h - (y1 - y0) * zoom) / 2 - y0 * zoom,
|
||||
};
|
||||
const zoom = clampZoom(Math.max(FIT_MIN, Math.min(view.w / (x1 - x0), view.h / (y1 - y0), 1.5)));
|
||||
/** 남으면 가운데 · 넘치면 왼쪽(위) 끝부터 */
|
||||
const place = (size: number, from: number, to: number): number =>
|
||||
Math.max(0, (size - (to - from) * zoom) / 2) - from * zoom;
|
||||
return { zoom, panX: place(view.w, x0, x1), panY: place(view.h, y0, y1) };
|
||||
}
|
||||
|
||||
const clampZoom = (z: number): number => Math.min(2, Math.max(0.25, +z.toFixed(3)));
|
||||
@@ -208,10 +190,15 @@ interface HostState {
|
||||
const states = new WeakMap<HTMLElement, HostState>();
|
||||
const SVG = "http://www.w3.org/2000/svg";
|
||||
|
||||
const showValue = (v: unknown): string => {
|
||||
/** 알약 값 글 — 수는 천 단위 쉼표 · 소수 셋째 자리까지(끝 0 지움) · 표는 「· ○줄」 · 글은 그대로 */
|
||||
export const showValue = (v: unknown): string => {
|
||||
if (v === null || v === undefined || v === "") return "";
|
||||
if (typeof v === "number")
|
||||
return Number.isInteger(v) ? v.toLocaleString() : String(+v.toFixed(4));
|
||||
return Number.isFinite(v) ? v.toLocaleString("ko-KR", { maximumFractionDigits: 3 }) : "";
|
||||
if (Array.isArray(v)) {
|
||||
const head = Array.isArray(v[0]) && v[0].every((c) => typeof c === "string") ? 1 : 0;
|
||||
return `· ${Math.max(0, v.length - head).toLocaleString("ko-KR")}줄`;
|
||||
}
|
||||
return String(v).slice(0, 12);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user