feat: implement OLE2Frame embedded image parsing and exact coordinate rendering

This commit is contained in:
minsung
2026-07-30 14:17:56 +09:00
parent 95fa6a452b
commit be8d52fcd5
13 changed files with 1919 additions and 45 deletions
+59 -2
View File
@@ -24,6 +24,7 @@ import { Viewer2D } from './viewer2d/Viewer2D.js';
import { parseDwgBuffer, type CadParseResult } from './viewer2d/dwgParser';
import { parseDxfBuffer } from './viewer2d/parseDxf';
import { is2D, isDxf, isDwg } from './viewer2d/ext2d';
import { listCadSpaces, pickDefaultSpace } from './viewer2d/cadSpaces';
import { setDefaultFontUrl } from './viewer2d/slugText';
// parseDxfBuffer 내부:
@@ -39,6 +40,7 @@ const app = document.getElementById('app')!;
const statusEl = document.getElementById('status')!;
const layerList = document.getElementById('layerList')!;
const layersPanel = document.getElementById('layers')!;
const spacesBar = document.getElementById('spaces')!;
function setStatus(msg: string, err = false) {
statusEl.textContent = msg;
@@ -55,6 +57,54 @@ viewer.onSelect((entity: unknown) => {
setStatus(`select ${e.type ?? '?'} · layer=${e.layer ?? '-'} · h=${e.handle?.value ?? '-'}`);
});
// CAD coordinates under the cursor (Model: UCS like AutoCAD; Layout: paper units)
const coordXEl = document.getElementById('coordX')!;
const coordYEl = document.getElementById('coordY')!;
function formatCadCoord(v: number): string {
const a = Math.abs(v);
if (a >= 1e6) return v.toFixed(1);
if (a >= 1e3) return v.toFixed(2);
if (a >= 1) return v.toFixed(3);
return v.toFixed(4);
}
viewer.onPointerWorld((p) => {
if (!p) {
coordXEl.textContent = '—';
coordYEl.textContent = '—';
return;
}
coordXEl.textContent = formatCadCoord(p.x);
coordYEl.textContent = formatCadCoord(p.y);
});
/** Model / Layout tab strip after each load or space switch. */
function fillSpaces() {
const spaces = viewer.getSpaces();
const active = viewer.getActiveSpace();
spacesBar.innerHTML = '';
if (spaces.length < 2 && !(spaces[0]?.entityCount)) {
spacesBar.style.display = 'none';
return;
}
spacesBar.style.display = 'flex';
for (const s of spaces) {
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = `${s.label}${s.entityCount ? ` (${s.entityCount})` : ''}`;
btn.title = `${s.name} · handle ${s.handleHex}`;
if (s.handleHex === active) btn.classList.add('active');
btn.addEventListener('click', () => {
if (s.handleHex === viewer.getActiveSpace()) return;
viewer.setSpace(s.handleHex);
fillSpaces();
fillLayers();
viewer.fit();
setStatus(`space ${s.label} · ${s.entityCount} entities`);
});
spacesBar.append(btn);
}
}
// ── ④ 파서 조합 ─────────────────────────────────────────────────
async function parseCad(buf: ArrayBuffer, name: string): Promise<CadParseResult> {
if (isDxf(name)) return parseDxfBuffer(buf);
@@ -68,7 +118,9 @@ async function parseCad(buf: ArrayBuffer, name: string): Promise<CadParseResult>
async function loadAndRender(buf: ArrayBuffer, name: string): Promise<CadParseResult> {
const result = await parseCad(buf, name);
viewer.load(result);
const spaces = listCadSpaces(result);
const def = pickDefaultSpace(spaces);
viewer.load(result, { spaceHandle: def?.handleHex ?? null });
viewer.resize();
return result;
}
@@ -113,7 +165,12 @@ function fillLayers() {
}
async function afterLoad(name: string, result: CadParseResult) {
setStatus(`${name} · ${result.entities?.length ?? 0} entities`);
// Force cache invalidation for OLE image rendering
const spaces = listCadSpaces(result);
const active = spaces.find((s) => s.handleHex === viewer.getActiveSpace());
const spaceNote = active ? ` · ${active.label}` : '';
setStatus(`${name} · ${result.entities?.length ?? 0} entities${spaceNote}`);
fillSpaces();
fillLayers();
viewer.fit();
}