From 2607989ea9f19bcb988a6240a2b564751ef1c09e Mon Sep 17 00:00:00 2001 From: Tim Arbaev Date: Mon, 27 Apr 2026 13:48:12 +0300 Subject: [PATCH 01/10] fix: browser fs false to stub out Node built-ins --- packages/dxf-render/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/dxf-render/package.json b/packages/dxf-render/package.json index 7a44249..4df2bfe 100644 --- a/packages/dxf-render/package.json +++ b/packages/dxf-render/package.json @@ -41,6 +41,9 @@ "main": "./dist/dxf-render-index.es.js", "module": "./dist/dxf-render-index.es.js", "types": "./dist/index.d.ts", + "browser": { + "fs": false + }, "exports": { ".": { "types": "./dist/index.d.ts", From e3825877448fc5059ca7786c1c8816ebadd7667e Mon Sep 17 00:00:00 2001 From: Tim Arbaev Date: Tue, 28 Apr 2026 14:54:34 +0300 Subject: [PATCH 02/10] feat: 6 antialiasing modes --- README.md | 2 +- demo/App.vue | 92 ++++++++++++++++++- demo/components/FeaturesSection.vue | 2 +- demo/components/WhatsNewSection.vue | 76 +++++++++++++-- packages/dxf-vuer/README.md | 20 +++- .../dxf-vuer/src/components/DXFViewer.vue | 6 +- .../dxf-vuer/src/composables/useThreeScene.ts | 89 ++++++++++++++---- packages/dxf-vuer/src/index.ts | 2 +- packages/dxf-vuer/src/types.ts | 2 + 9 files changed, 260 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 8bbb06c..906aa45 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ async function loadFile(file) { - **Error display** — parse/render/fetch errors shown in the viewer with retry support - **Debug overlay** — FPS, draw calls, lines, triangles - **Zoom level** — percentage display relative to fit-to-view -- **TAA anti-aliasing** — smooth edges after idle accumulation +- **Configurable antialiasing** — choose between MSAA (default, hardware), SMAA, FXAA, TAA, SSAA, or none - **TypeScript** — strict types, full `.d.ts` declarations ## Acknowledgements diff --git a/demo/App.vue b/demo/App.vue index 5fd4ae4..96fe135 100644 --- a/demo/App.vue +++ b/demo/App.vue @@ -89,6 +89,19 @@ +
+ Antialiasing: + +

{{ aaDescription }}

+
+

{{ isTouchDevice ? "Pinch to zoom · Drag to pan" : "Scroll to zoom · Drag to pan" }}

@@ -113,6 +126,7 @@
diff --git a/demo/components/StatsSection.vue b/demo/components/StatsSection.vue new file mode 100644 index 0000000..be03a94 --- /dev/null +++ b/demo/components/StatsSection.vue @@ -0,0 +1,86 @@ + + + From ce9cbd7f65b142215fd2788e9235d1dd12b9795e Mon Sep 17 00:00:00 2001 From: Tim Arbaev Date: Tue, 28 Apr 2026 19:47:01 +0300 Subject: [PATCH 10/10] refactor: move aa-modes to render --- packages/dxf-render/CHANGELOG.md | 15 +++ packages/dxf-render/README.md | 40 +++++++ packages/dxf-render/package.json | 2 +- packages/dxf-render/src/index.ts | 9 ++ packages/dxf-render/src/scene/antialiasing.ts | 103 ++++++++++++++++++ packages/dxf-vuer/CHANGELOG.md | 18 +++ packages/dxf-vuer/package.json | 4 +- .../dxf-vuer/src/components/DXFViewer.vue | 3 +- .../dxf-vuer/src/composables/useThreeScene.ts | 81 ++------------ packages/dxf-vuer/src/index.ts | 2 +- packages/dxf-vuer/src/types.ts | 2 - 11 files changed, 203 insertions(+), 76 deletions(-) create mode 100644 packages/dxf-render/src/scene/antialiasing.ts diff --git a/packages/dxf-render/CHANGELOG.md b/packages/dxf-render/CHANGELOG.md index 11aef8c..13cebdd 100644 --- a/packages/dxf-render/CHANGELOG.md +++ b/packages/dxf-render/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.3.0 + +### Features + +- **Configurable antialiasing pipeline**: new `scene/antialiasing.ts` module with framework-agnostic factories — `createRenderer({ aaMode })`, `createComposer({ aaMode, scene, camera, renderer })`, and `isReducedMotionPreferred()`. Six modes: `msaa` (default, hardware), `smaa`, `fxaa`, `taa`, `ssaa`, `none`. New exported type `AntialiasingMode`. Lets React / Svelte / vanilla users use the same AA palette that powers `dxf-vuer`'s ``. + +### Documentation + +- README: new "Antialiasing" section with comparison table and example. +- npm search SEO: deduplicated keywords, added precise search phrases (`dxf-viewer`, `dxf-three`, `dxf-js`, `render-dxf`, `parser`, `viewer`, `blueprint`); homepage now points to the live demo; added the `bugs` field. + +### Bug Fixes + +- Added `"browser": { "fs": false }` to package.json — eliminates `Could not resolve "fs"` when bundling for the browser (Vite / esbuild / Angular CLI). The Node branch of `opentype.js` (file-system font loading) is now automatically stripped by downstream bundlers. + ## 1.2.0 ### Features diff --git a/packages/dxf-render/README.md b/packages/dxf-render/README.md index 218d658..f97e345 100644 --- a/packages/dxf-render/README.md +++ b/packages/dxf-render/README.md @@ -261,6 +261,46 @@ export function DxfViewer({ dxfText }: { dxfText: string }) { - `useCamera(domElement)` — orthographic camera with `fitCameraToBox()` - `useControls(camera, domElement)` — pan/zoom controls (no rotation), mobile touch support +- `createRenderer({ aaMode })` — `WebGLRenderer` with the right `antialias` flag for the selected AA mode +- `createComposer({ aaMode, scene, camera, renderer })` — builds the post-processing pipeline (`EffectComposer` + AA pass + `OutputPass`); returns `{ composer: null }` for `msaa`/`none` (use `renderer.render()` directly) +- `isReducedMotionPreferred()` — `true` when the user has enabled "reduce motion" in their OS + +### Antialiasing + +Six modes available via `AntialiasingMode = "msaa" | "smaa" | "fxaa" | "taa" | "ssaa" | "none"`: + +| Mode | Use case | +|------|----------| +| `msaa` | Hardware multisampling (default). Crisp geometric edges, almost free runtime cost. Best for CAD with thin lines and text | +| `smaa` | Edge-detection post-processing. Cheap and works while panning. Note: may fade pixels at corners of 1px lines (line-art limitation) | +| `fxaa` | Cheapest fullscreen AA — single shader pass. Smooths edges but tends to blur thin lines and small text | +| `taa` | Temporal AA: 32 jittered frames accumulated when the camera stops. Smooth on static views; first frame after movement looks aliased. Skipped when `prefers-reduced-motion: reduce` | +| `ssaa` | Super-sampling: renders at higher resolution and downscales. Reference quality; expensive | +| `none` | No antialiasing. Maximum performance and pixel sharpness | + +```ts +import * as THREE from "three"; +import { createRenderer, createComposer, isReducedMotionPreferred } from "dxf-render"; + +const scene = new THREE.Scene(); +const camera = new THREE.OrthographicCamera(/* ... */); +const renderer = createRenderer({ aaMode: "msaa" }); +const { composer, taaPass } = createComposer({ aaMode: "msaa", scene, camera, renderer }); + +function render() { + if (taaPass && composer) { + taaPass.accumulateIndex = -1; + composer.render(); + if (!isReducedMotionPreferred()) { + // schedule next jittered frame via requestAnimationFrame... + } + } else if (composer) { + composer.render(); + } else { + renderer.render(scene, camera); + } +} +``` ### Fonts diff --git a/packages/dxf-render/package.json b/packages/dxf-render/package.json index 4df2bfe..8a8ab17 100644 --- a/packages/dxf-render/package.json +++ b/packages/dxf-render/package.json @@ -1,6 +1,6 @@ { "name": "dxf-render", - "version": "1.2.1", + "version": "1.3.0", "description": "DXF parser and Three.js renderer — parse and render AutoCAD DXF files in the browser. Framework-agnostic.", "type": "module", "license": "MIT", diff --git a/packages/dxf-render/src/index.ts b/packages/dxf-render/src/index.ts index 431db91..c8e2463 100644 --- a/packages/dxf-render/src/index.ts +++ b/packages/dxf-render/src/index.ts @@ -12,6 +12,15 @@ export { computePolylinePoints } from "./render/collectors"; // Scene helpers export { useCamera } from "./scene/useCamera"; export { useControls, useOrbitControls } from "./scene/useControls"; +export { + createRenderer, + createComposer, + isReducedMotionPreferred, + type AntialiasingMode, + type CreateRendererOptions, + type CreateComposerOptions, + type AntialiasingPipeline, +} from "./scene/antialiasing"; // Fonts export { loadDefaultFont, loadFont, getDefaultFont, loadSerifFont, getSerifFont } from "./render/text/fontManager"; diff --git a/packages/dxf-render/src/scene/antialiasing.ts b/packages/dxf-render/src/scene/antialiasing.ts new file mode 100644 index 0000000..fcea3cc --- /dev/null +++ b/packages/dxf-render/src/scene/antialiasing.ts @@ -0,0 +1,103 @@ +import * as THREE from "three"; +import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js"; +import { RenderPass } from "three/addons/postprocessing/RenderPass.js"; +import { TAARenderPass } from "three/addons/postprocessing/TAARenderPass.js"; +import { SSAARenderPass } from "three/addons/postprocessing/SSAARenderPass.js"; +import { SMAAPass } from "three/addons/postprocessing/SMAAPass.js"; +import { FXAAPass } from "three/addons/postprocessing/FXAAPass.js"; +import { OutputPass } from "three/addons/postprocessing/OutputPass.js"; + +export type AntialiasingMode = "none" | "msaa" | "smaa" | "fxaa" | "taa" | "ssaa"; + +export interface CreateRendererOptions { + aaMode?: AntialiasingMode; + alpha?: boolean; + preserveDrawingBuffer?: boolean; +} + +/** + * Create a Three.js WebGLRenderer with the antialias flag set per AA mode. + * MSAA uses native hardware multisampling (antialias: true); other modes + * apply post-processing via createComposer() and use antialias: false. + */ +export function createRenderer(opts: CreateRendererOptions = {}): THREE.WebGLRenderer { + const { aaMode = "msaa", alpha = true, preserveDrawingBuffer = true } = opts; + const renderer = new THREE.WebGLRenderer({ + antialias: aaMode === "msaa", + alpha, + preserveDrawingBuffer, + }); + renderer.sortObjects = false; + return renderer; +} + +export interface CreateComposerOptions { + aaMode: AntialiasingMode; + scene: THREE.Scene; + camera: THREE.Camera; + renderer: THREE.WebGLRenderer; +} + +export interface AntialiasingPipeline { + /** EffectComposer for post-processing AA modes; null for msaa/none (render direct). */ + composer: EffectComposer | null; + /** Reference to TAARenderPass when aaMode === "taa", for accumulation control. */ + taaPass: (TAARenderPass & { accumulateIndex: number }) | null; +} + +/** + * Build the post-processing pipeline for the given AA mode. + * Returns { composer: null, taaPass: null } for msaa/none — caller should + * call renderer.render(scene, camera) directly. For other modes, returns a + * configured EffectComposer to be invoked via composer.render(). + */ +export function createComposer(opts: CreateComposerOptions): AntialiasingPipeline { + const { aaMode, scene, camera, renderer } = opts; + + if (aaMode === "msaa" || aaMode === "none") { + return { composer: null, taaPass: null }; + } + + const composer = new EffectComposer(renderer); + let taaPass: AntialiasingPipeline["taaPass"] = null; + + if (aaMode === "taa") { + const pass = new TAARenderPass(scene, camera) as TAARenderPass & { + accumulateIndex: number; + }; + pass.accumulate = true; + composer.addPass(pass); + taaPass = pass; + } else if (aaMode === "ssaa") { + composer.addPass(new SSAARenderPass(scene, camera)); + } else if (aaMode === "smaa") { + composer.addPass(new RenderPass(scene, camera)); + const smaa = new SMAAPass(); + const size = new THREE.Vector2(); + renderer.getSize(size); + const pixelRatio = renderer.getPixelRatio(); + smaa.setSize(size.x * pixelRatio, size.y * pixelRatio); + composer.addPass(smaa); + } else if (aaMode === "fxaa") { + composer.addPass(new RenderPass(scene, camera)); + const fxaa = new FXAAPass(); + const size = new THREE.Vector2(); + renderer.getSize(size); + const pixelRatio = renderer.getPixelRatio(); + fxaa.setSize(size.x * pixelRatio, size.y * pixelRatio); + composer.addPass(fxaa); + } + + // OutputPass converts from linear to sRGB color space for correct color display + composer.addPass(new OutputPass()); + return { composer, taaPass }; +} + +/** + * Returns true if the user has enabled "reduce motion" in their OS. + * Use this to skip animations like TAA jitter accumulation. + */ +export function isReducedMotionPreferred(): boolean { + if (typeof window === "undefined" || !window.matchMedia) return false; + return window.matchMedia("(prefers-reduced-motion: reduce)").matches; +} diff --git a/packages/dxf-vuer/CHANGELOG.md b/packages/dxf-vuer/CHANGELOG.md index a8e9eac..7524c49 100644 --- a/packages/dxf-vuer/CHANGELOG.md +++ b/packages/dxf-vuer/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 2.4.0 + +### Features + +- **`antialiasing` prop**: choose the AA mode at init time — `msaa` (default, hardware), `smaa`, `fxaa`, `taa`, `ssaa`, or `none`. Default changed from TAA to MSAA for crisper thin lines and text in CAD drawings. Init-time only — recreate the component via `:key` to switch at runtime. Powered by the new `createRenderer` / `createComposer` factories in `dxf-render` 1.3.0; `AntialiasingMode` is now imported from `dxf-render` (still re-exported from `dxf-vuer` for backward compatibility). +- **`showLayerPanel` prop**: hide the layers panel programmatically (default `true`). Position is still controlled by the existing `layerPanelPosition`. +- **Layers panel filter**: text input for searching layers by name. Auto-shown when the drawing has more than 5 layers. +- **`loadDXFFromBuffer` and `loadDXFFromBlob` methods**: load drawings from `ArrayBuffer` or `Blob` for integrations with IndexedDB, S3, Supabase Storage and other binary sources. Auto-detects encoding (UTF-8, UTF-16 LE/BE) by BOM. +- **`prefers-reduced-motion` support**: when the user has enabled "reduce motion" in the OS, the TAA mode renders a single frame and skips the 32-frame jittered accumulation loop. Other AA modes are unaffected. + +### Documentation + +- npm search SEO: added keywords (`dxf-renderer`, `dxf-three`, `render-dxf`, `parser`), added the `bugs` field. + +### Dependencies + +- Requires `dxf-render` ≥ 1.3.0 (new AA pipeline factories). + ## 2.3.0 ### Features diff --git a/packages/dxf-vuer/package.json b/packages/dxf-vuer/package.json index 8f4875f..8f9702b 100644 --- a/packages/dxf-vuer/package.json +++ b/packages/dxf-vuer/package.json @@ -1,6 +1,6 @@ { "name": "dxf-vuer", - "version": "2.3.1", + "version": "2.4.0", "description": "Vue 3 DXF file viewer — render AutoCAD DXF drawings in the browser with Three.js. Built-in parser, vector text rendering, TypeScript support.", "type": "module", "license": "MIT", @@ -61,7 +61,7 @@ "test": "vitest run" }, "peerDependencies": { - "dxf-render": "^1.2.0", + "dxf-render": "^1.3.0", "three": ">=0.160.0", "vue": "^3.4.0" }, diff --git a/packages/dxf-vuer/src/components/DXFViewer.vue b/packages/dxf-vuer/src/components/DXFViewer.vue index abdd65d..8be05d1 100644 --- a/packages/dxf-vuer/src/components/DXFViewer.vue +++ b/packages/dxf-vuer/src/components/DXFViewer.vue @@ -190,7 +190,8 @@ import { useDXFRenderer } from "../composables/useDXFRenderer"; import { useLayers } from "../composables/useLayers"; import { useLoadError } from "../composables/useLoadError"; import type { DxfData, DxfLayer } from "dxf-render"; -import type { OverlayPosition, AntialiasingMode } from "../types"; +import type { OverlayPosition } from "../types"; +import type { AntialiasingMode } from "dxf-render"; import LayerPanel from "./LayerPanel.vue"; import ViewerToolbar from "./ViewerToolbar.vue"; diff --git a/packages/dxf-vuer/src/composables/useThreeScene.ts b/packages/dxf-vuer/src/composables/useThreeScene.ts index 62669b4..0a4d98a 100644 --- a/packages/dxf-vuer/src/composables/useThreeScene.ts +++ b/packages/dxf-vuer/src/composables/useThreeScene.ts @@ -1,20 +1,17 @@ import { ref } from "vue"; import * as THREE from "three"; -import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js"; -import { RenderPass } from "three/addons/postprocessing/RenderPass.js"; -import { TAARenderPass } from "three/addons/postprocessing/TAARenderPass.js"; -import { SSAARenderPass } from "three/addons/postprocessing/SSAARenderPass.js"; -import { SMAAPass } from "three/addons/postprocessing/SMAAPass.js"; -import { FXAAPass } from "three/addons/postprocessing/FXAAPass.js"; -import { OutputPass } from "three/addons/postprocessing/OutputPass.js"; import { useControls, CAMERA_NEAR_PLANE, CAMERA_FAR_PLANE, CAMERA_INITIAL_Z_POSITION, SCENE_BG_COLOR, + createRenderer, + createComposer, + isReducedMotionPreferred, + type AntialiasingMode, + type AntialiasingPipeline, } from "dxf-render"; -import type { AntialiasingMode } from "../types"; export interface ThreeJSOptions { enableControls?: boolean; @@ -44,9 +41,8 @@ export function useThreeScene() { let scene: THREE.Scene | null = null; let camera: THREE.OrthographicCamera | null = null; let renderer: THREE.WebGLRenderer | null = null; - let composer: EffectComposer | null = null; - // accumulateIndex exists at runtime but is missing from @types/three - let taaPass: (TAARenderPass & { accumulateIndex: number }) | null = null; + let composer: AntialiasingPipeline["composer"] = null; + let taaPass: AntialiasingPipeline["taaPass"] = null; let accumulationFrameId: number | null = null; const { @@ -122,50 +118,6 @@ export function useThreeScene() { } }; - const createComposer = ( - aaMode: AntialiasingMode, - sceneRef: THREE.Scene, - cameraRef: THREE.OrthographicCamera, - rendererRef: THREE.WebGLRenderer, - ): EffectComposer | null => { - // MSAA uses native WebGL antialiasing; "none" skips post-processing entirely - if (aaMode === "msaa" || aaMode === "none") return null; - - const newComposer = new EffectComposer(rendererRef); - - if (aaMode === "taa") { - // TAA accumulates jittered frames when idle for smooth AA - const pass = new TAARenderPass(sceneRef, cameraRef) as TAARenderPass & { - accumulateIndex: number; - }; - pass.accumulate = true; - newComposer.addPass(pass); - taaPass = pass; - } else if (aaMode === "ssaa") { - newComposer.addPass(new SSAARenderPass(sceneRef, cameraRef)); - } else if (aaMode === "smaa") { - newComposer.addPass(new RenderPass(sceneRef, cameraRef)); - const smaaPass = new SMAAPass(); - const size = new THREE.Vector2(); - rendererRef.getSize(size); - const pixelRatio = rendererRef.getPixelRatio(); - smaaPass.setSize(size.x * pixelRatio, size.y * pixelRatio); - newComposer.addPass(smaaPass); - } else if (aaMode === "fxaa") { - newComposer.addPass(new RenderPass(sceneRef, cameraRef)); - const fxaaPass = new FXAAPass(); - const size = new THREE.Vector2(); - rendererRef.getSize(size); - const pixelRatio = rendererRef.getPixelRatio(); - fxaaPass.setSize(size.x * pixelRatio, size.y * pixelRatio); - newComposer.addPass(fxaaPass); - } - - // OutputPass converts from linear to sRGB color space for correct color display - newComposer.addPass(new OutputPass()); - return newComposer; - }; - const initThreeJS = (container: HTMLDivElement, options: ThreeJSOptions = {}) => { const { enableControls = false, aaMode = "msaa" } = options; @@ -198,13 +150,7 @@ export function useThreeScene() { camera.zoom = 1; try { - renderer = new THREE.WebGLRenderer({ - // Native MSAA is configured at construction time and cannot be toggled later - antialias: aaMode === "msaa", - alpha: true, - preserveDrawingBuffer: true, - }); - renderer.sortObjects = false; + renderer = createRenderer({ aaMode }); renderer.setSize(containerWidth, containerHeight); } catch (err) { const errorMessage = @@ -214,7 +160,9 @@ export function useThreeScene() { return; } - composer = createComposer(aaMode, scene, camera, renderer); + const pipeline = createComposer({ aaMode, scene, camera, renderer }); + composer = pipeline.composer; + taaPass = pipeline.taaPass; container.appendChild(renderer.domElement); @@ -282,11 +230,6 @@ export function useThreeScene() { accumulationFrameId = requestAnimationFrame(accumulateFrame); }; - const prefersReducedMotion = (): boolean => { - if (typeof window === "undefined" || !window.matchMedia) return false; - return window.matchMedia("(prefers-reduced-motion: reduce)").matches; - }; - const renderScene = () => { if (!renderer || !scene || !camera) return; @@ -296,7 +239,7 @@ export function useThreeScene() { composer.render(); // Skip jittered accumulation loop when user prefers reduced motion — // the slowly-resolving frames read as animation - if (!prefersReducedMotion()) { + if (!isReducedMotionPreferred()) { accumulationFrameId = requestAnimationFrame(accumulateFrame); } return; diff --git a/packages/dxf-vuer/src/index.ts b/packages/dxf-vuer/src/index.ts index d2d22c6..d36552e 100644 --- a/packages/dxf-vuer/src/index.ts +++ b/packages/dxf-vuer/src/index.ts @@ -14,7 +14,7 @@ export { useThreeScene } from "./composables/useThreeScene"; export { useLayers } from "./composables/useLayers"; // Types -export type { OverlayPosition, AntialiasingMode } from "./types"; +export type { OverlayPosition } from "./types"; // Re-export everything from dxf-render (backward compatibility) export * from "dxf-render"; diff --git a/packages/dxf-vuer/src/types.ts b/packages/dxf-vuer/src/types.ts index 1423408..ded90a2 100644 --- a/packages/dxf-vuer/src/types.ts +++ b/packages/dxf-vuer/src/types.ts @@ -5,5 +5,3 @@ export type OverlayPosition = | "bottom-left" | "bottom-center" | "bottom-right"; - -export type AntialiasingMode = "none" | "msaa" | "smaa" | "fxaa" | "taa" | "ssaa";