forked from kimminsung/dwg-dxf-viewer-sample
refactor: extract viewer2d workspace package
This commit is contained in:
1 parent
95fa6a452b
commit
5e10bb1be4
27 files changed
+1588
-704
No files matched your search
Generated
+1465
-675
File diff suppressed because it is too large.
Load diff
+13
-7
@@ -4,20 +4,26 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Standalone sample: compose acadrust-dwg + dxf-parser + Viewer2D (sibling of hmwebviewer)",
|
"description": "Standalone sample: compose acadrust-dwg + dxf-parser + Viewer2D (sibling of hmwebviewer)",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"workspaces": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --port 5173 --strictPort",
|
"dev": "vite --port 5173 --strictPort",
|
||||||
"build": "tsc --noEmit && vite build",
|
"build": "tsc --noEmit && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:unit": "vitest run tests/viewer2d.contract.test.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dxf-parser": "^1.1.2",
|
"@hmwebviewer/viewer2d": "workspace:*",
|
||||||
"opentype.js": "^2.0.0",
|
"three": "0.185.1"
|
||||||
"three": "^0.185.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/three": "^0.185.1",
|
"@playwright/test": "1.62.0",
|
||||||
"typescript": "^5.6.0",
|
"@types/three": "0.185.1",
|
||||||
"vite": "^5.4.0"
|
"typescript": "7.0.2",
|
||||||
|
"vite": "8.1.5",
|
||||||
|
"vitest": "4.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "@hmwebviewer/viewer2d",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"dependencies": {
|
||||||
|
"dxf-parser": "1.1.2",
|
||||||
|
"opentype.js": "2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"three": "0.185.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
Vendored
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,42 @@
|
|||||||
|
import { Viewer2D } from './Viewer2D.js';
|
||||||
|
import { parseDwgBuffer, type CadParseResult } from './dwgParser';
|
||||||
|
import { is2D, isDwg } from './ext2d';
|
||||||
|
import { parseDxfBuffer } from './parseDxf';
|
||||||
|
import { setDefaultFontUrl } from './slugText';
|
||||||
|
|
||||||
|
export type { CadParseResult } from './dwgParser';
|
||||||
|
|
||||||
|
export type CadInput = {
|
||||||
|
name: string;
|
||||||
|
data: ArrayBuffer | Uint8Array;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Viewer2DOptions = {
|
||||||
|
fontUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function toArrayBuffer(data: ArrayBuffer | Uint8Array): ArrayBuffer {
|
||||||
|
if (data instanceof ArrayBuffer) return data;
|
||||||
|
return data.slice().buffer as ArrayBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCad2DFile(name: string): boolean {
|
||||||
|
return is2D(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parseCad(input: CadInput): Promise<CadParseResult> {
|
||||||
|
const buffer = toArrayBuffer(input.data);
|
||||||
|
if (isDwg(input.name)) return parseDwgBuffer(new Uint8Array(buffer));
|
||||||
|
if (is2D(input.name)) return parseDxfBuffer(buffer);
|
||||||
|
|
||||||
|
const head = new TextDecoder('latin1').decode(
|
||||||
|
new Uint8Array(buffer, 0, Math.min(6, buffer.byteLength)),
|
||||||
|
);
|
||||||
|
if (head.startsWith('AC')) return parseDwgBuffer(new Uint8Array(buffer));
|
||||||
|
return parseDxfBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createViewer2D(container: HTMLElement, options: Viewer2DOptions = {}): Viewer2D {
|
||||||
|
if (options.fontUrl) setDefaultFontUrl(options.fontUrl);
|
||||||
|
return new Viewer2D(container);
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
+17
-21
@@ -20,11 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// ── ① 모듈 지도의 각 카드 (src/viewer2d/*) ────────────────────────
|
// ── ① 모듈 지도의 각 카드 (src/viewer2d/*) ────────────────────────
|
||||||
import { Viewer2D } from './viewer2d/Viewer2D.js';
|
import {
|
||||||
import { parseDwgBuffer, type CadParseResult } from './viewer2d/dwgParser';
|
createViewer2D,
|
||||||
import { parseDxfBuffer } from './viewer2d/parseDxf';
|
isCad2DFile,
|
||||||
import { is2D, isDxf, isDwg } from './viewer2d/ext2d';
|
parseCad,
|
||||||
import { setDefaultFontUrl } from './viewer2d/slugText';
|
type CadParseResult,
|
||||||
|
} from '@hmwebviewer/viewer2d';
|
||||||
|
|
||||||
// parseDxfBuffer 내부:
|
// parseDxfBuffer 내부:
|
||||||
// dxf-parser → HatchHandler → dxfAdapter
|
// dxf-parser → HatchHandler → dxfAdapter
|
||||||
@@ -32,7 +33,8 @@ import { setDefaultFontUrl } from './viewer2d/slugText';
|
|||||||
// acadrustParser → acadrust-dwg WASM
|
// acadrustParser → acadrust-dwg WASM
|
||||||
|
|
||||||
// ── ② 호스트 정적 자산 ──────────────────────────────────────────
|
// ── ② 호스트 정적 자산 ──────────────────────────────────────────
|
||||||
setDefaultFontUrl('/fonts/NanumGothic-Regular.ttf');
|
const appBase = new URL(import.meta.env.BASE_URL, window.location.origin);
|
||||||
|
const publicUrl = (path: string) => new URL(path, appBase).href;
|
||||||
|
|
||||||
// ── ③ Viewer2D 마운트 ───────────────────────────────────────────
|
// ── ③ Viewer2D 마운트 ───────────────────────────────────────────
|
||||||
const app = document.getElementById('app')!;
|
const app = document.getElementById('app')!;
|
||||||
@@ -45,7 +47,9 @@ function setStatus(msg: string, err = false) {
|
|||||||
statusEl.classList.toggle('err', err);
|
statusEl.classList.toggle('err', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewer = new Viewer2D(app);
|
const viewer = createViewer2D(app, {
|
||||||
|
fontUrl: publicUrl('fonts/NanumGothic-Regular.ttf'),
|
||||||
|
});
|
||||||
viewer.onSelect((entity: unknown) => {
|
viewer.onSelect((entity: unknown) => {
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
setStatus('deselected');
|
setStatus('deselected');
|
||||||
@@ -56,18 +60,8 @@ viewer.onSelect((entity: unknown) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ── ④ 파서 조합 ─────────────────────────────────────────────────
|
// ── ④ 파서 조합 ─────────────────────────────────────────────────
|
||||||
async function parseCad(buf: ArrayBuffer, name: string): Promise<CadParseResult> {
|
|
||||||
if (isDxf(name)) return parseDxfBuffer(buf);
|
|
||||||
if (isDwg(name)) return parseDwgBuffer(new Uint8Array(buf));
|
|
||||||
const head = new TextDecoder('latin1').decode(
|
|
||||||
new Uint8Array(buf, 0, Math.min(6, buf.byteLength)),
|
|
||||||
);
|
|
||||||
if (head.startsWith('AC')) return parseDwgBuffer(new Uint8Array(buf));
|
|
||||||
return parseDxfBuffer(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadAndRender(buf: ArrayBuffer, name: string): Promise<CadParseResult> {
|
async function loadAndRender(buf: ArrayBuffer, name: string): Promise<CadParseResult> {
|
||||||
const result = await parseCad(buf, name);
|
const result = await parseCad({ data: buf, name });
|
||||||
viewer.load(result);
|
viewer.load(result);
|
||||||
viewer.resize();
|
viewer.resize();
|
||||||
return result;
|
return result;
|
||||||
@@ -81,7 +75,7 @@ async function loadUrl(url: string, nameHint?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadFile(file: File) {
|
async function loadFile(file: File) {
|
||||||
if (!is2D(file.name)) throw new Error(`not .dwg/.dxf: ${file.name}`);
|
if (!isCad2DFile(file.name)) throw new Error(`not .dwg/.dxf: ${file.name}`);
|
||||||
return loadAndRender(await file.arrayBuffer(), file.name);
|
return loadAndRender(await file.arrayBuffer(), file.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,10 +123,12 @@ async function run(label: string, job: () => Promise<CadParseResult>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('sample')!.addEventListener('click', () => {
|
document.getElementById('sample')!.addEventListener('click', () => {
|
||||||
void run('BasicSample.dwg', () => loadUrl('/samples/BasicSample.dwg', 'BasicSample.dwg'));
|
void run('BasicSample.dwg', () =>
|
||||||
|
loadUrl(publicUrl('samples/BasicSample.dwg'), 'BasicSample.dwg'),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
document.getElementById('sampleDxf')!.addEventListener('click', () => {
|
document.getElementById('sampleDxf')!.addEventListener('click', () => {
|
||||||
void run('simple.dxf', () => loadUrl('/samples/simple.dxf', 'simple.dxf'));
|
void run('simple.dxf', () => loadUrl(publicUrl('samples/simple.dxf'), 'simple.dxf'));
|
||||||
});
|
});
|
||||||
document.getElementById('file')!.addEventListener('change', (ev) => {
|
document.getElementById('file')!.addEventListener('change', (ev) => {
|
||||||
const file = (ev.target as HTMLInputElement).files?.[0];
|
const file = (ev.target as HTMLInputElement).files?.[0];
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { readFile } from 'node:fs/promises';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { isCad2DFile, parseCad } from '@hmwebviewer/viewer2d';
|
||||||
|
|
||||||
|
describe('viewer2d package interface', () => {
|
||||||
|
it('parses the sample DXF through the public package interface', async () => {
|
||||||
|
const file = await readFile(new URL('../public/samples/simple.dxf', import.meta.url));
|
||||||
|
const result = await parseCad({
|
||||||
|
name: 'simple.dxf',
|
||||||
|
data: file.buffer.slice(file.byteOffset, file.byteOffset + file.byteLength),
|
||||||
|
});
|
||||||
|
const entities = result.entities as Array<{ layer?: string; type?: string }>;
|
||||||
|
|
||||||
|
expect(isCad2DFile('drawing.DWG')).toBe(true);
|
||||||
|
expect(isCad2DFile('drawing.dxf')).toBe(true);
|
||||||
|
expect(isCad2DFile('drawing.pdf')).toBe(false);
|
||||||
|
expect(entities.map(({ type, layer }) => [type, layer])).toEqual([
|
||||||
|
['LINE', 'OUTLINE'],
|
||||||
|
['LINE', 'OUTLINE'],
|
||||||
|
['LINE', 'OUTLINE'],
|
||||||
|
['LINE', 'OUTLINE'],
|
||||||
|
['CIRCLE', '0'],
|
||||||
|
['TEXT', '0'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
+1
-1
@@ -11,5 +11,5 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"allowJs": true
|
"allowJs": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "vite.config.ts"]
|
"include": ["src/**/*", "packages/*/src/**/*", "vite.config.ts"]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user