@cadit-app/image-extrude 0.1.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Parameter schema for the Image Extrude generator.
3
3
  */
4
- // Default SVG - a simple star shape
5
- const defaultSvgDataUrl = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cG9seWdvbiBwb2ludHM9IjUwLDUgNjEsMzkgOTcsMzkgNjgsMjIgNzksOTUgNTAsNzAgMjEsOTUgMzIsNjIgMywzOSAzOSwzOSIgZmlsbD0iYmxhY2siLz48L3N2Zz4=';
4
+ // Default image - CookieCAD logo from GitHub via jsdelivr CDN
5
+ const defaultImageUrl = 'https://cdn.jsdelivr.net/gh/CADit-app/image-extrude@master/images/cookiecad-logo-dark.svg';
6
6
  export const imageExtrudeParamsSchema = {
7
7
  mode: {
8
8
  type: 'choice',
@@ -17,10 +17,10 @@ export const imageExtrudeParamsSchema = {
17
17
  type: 'image',
18
18
  label: 'Image File',
19
19
  default: {
20
- imageUrl: '',
21
- dataUrl: defaultSvgDataUrl,
20
+ imageUrl: defaultImageUrl,
21
+ dataUrl: '',
22
22
  fileType: 'image/svg+xml',
23
- fileName: 'star.svg'
23
+ fileName: 'cookiecad-logo-dark.svg'
24
24
  },
25
25
  },
26
26
  height: {
@@ -1,5 +1,8 @@
1
1
  /**
2
2
  * SVG to bitmap rendering using resvg-wasm
3
+ *
4
+ * Note: When running as a dynamically loaded script in CADit, the WASM module
5
+ * is loaded from esm.sh CDN since Vite's ?url import syntax isn't available.
3
6
  */
4
7
  /**
5
8
  * Renders an SVG Data URL to a PNG Data URL using resvg-wasm.
@@ -1 +1 @@
1
- {"version":3,"file":"resvg.d.ts","sourceRoot":"","sources":["../../src/resvg.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+BH;;GAEG;AACH,eAAO,MAAM,wBAAwB,GACnC,YAAY,MAAM,EAClB,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,KAC9B,OAAO,CAAC,MAAM,CAuBhB,CAAC"}
1
+ {"version":3,"file":"resvg.d.ts","sourceRoot":"","sources":["../../src/resvg.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsCH;;GAEG;AACH,eAAO,MAAM,wBAAwB,GACnC,YAAY,MAAM,EAClB,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,KAC9B,OAAO,CAAC,MAAM,CA6BhB,CAAC"}
package/dist/src/resvg.js CHANGED
@@ -1,26 +1,41 @@
1
1
  /**
2
2
  * SVG to bitmap rendering using resvg-wasm
3
+ *
4
+ * Note: When running as a dynamically loaded script in CADit, the WASM module
5
+ * is loaded from esm.sh CDN since Vite's ?url import syntax isn't available.
3
6
  */
4
7
  import * as resvg from '@resvg/resvg-wasm';
5
8
  import { svgDataUrlToString } from './utils';
9
+ // Hardcoded version for WASM URL - update when upgrading @resvg/resvg-wasm dependency
10
+ const RESVG_VERSION = '2.6.2';
6
11
  let wasmInitialized = false;
12
+ let wasmInitPromise = null;
7
13
  /**
8
- * Initialize resvg WASM module
9
- * In CLI context, we need to load from file system
10
- * In browser context (via bundler), the WASM is loaded differently
14
+ * Initialize resvg WASM module.
15
+ * Fetches the WASM binary from esm.sh CDN to work in dynamically loaded scripts.
16
+ *
17
+ * Safe to call multiple times - subsequent calls return immediately.
18
+ * If initialization fails, subsequent calls will retry.
11
19
  */
12
20
  async function initializeResvgWasm() {
13
21
  if (wasmInitialized)
14
22
  return;
15
- // For CLI usage, load WASM from node_modules
16
- const { readFile } = await import('fs/promises');
17
- const { resolve, dirname } = await import('path');
18
- const { fileURLToPath } = await import('url');
19
- // Find the wasm file in node_modules
20
- const wasmPath = resolve(dirname(fileURLToPath(import.meta.url)), '../node_modules/@resvg/resvg-wasm/index_bg.wasm');
21
- const wasmBuffer = await readFile(wasmPath);
22
- await resvg.initWasm(wasmBuffer);
23
- wasmInitialized = true;
23
+ if (wasmInitPromise)
24
+ return wasmInitPromise;
25
+ wasmInitPromise = (async () => {
26
+ try {
27
+ // Fetch WASM from esm.sh CDN
28
+ const wasmUrl = `https://esm.sh/@resvg/resvg-wasm@${RESVG_VERSION}/index_bg.wasm`;
29
+ await resvg.initWasm(fetch(wasmUrl));
30
+ wasmInitialized = true;
31
+ }
32
+ catch (error) {
33
+ // Reset promise so retry is possible
34
+ wasmInitPromise = null;
35
+ throw error;
36
+ }
37
+ })();
38
+ return wasmInitPromise;
24
39
  }
25
40
  /**
26
41
  * Renders an SVG Data URL to a PNG Data URL using resvg-wasm.
@@ -40,8 +55,10 @@ export const renderSvgToBitmapDataUrl = async (svgDataUrl, options) => {
40
55
  }
41
56
  const resvgInstance = new resvg.Resvg(svgString, opts);
42
57
  const pngData = resvgInstance.render();
43
- const pngBuffer = pngData.asPng();
44
- // Convert to data URL
45
- const base64 = Buffer.from(pngBuffer).toString('base64');
46
- return `data:image/png;base64,${base64}`;
58
+ const pngBuffer = pngData.asPng(); // Uint8Array
59
+ // Convert Uint8Array to proper ArrayBuffer for Blob
60
+ const arrayBuffer = pngBuffer.buffer.slice(pngBuffer.byteOffset, pngBuffer.byteOffset + pngBuffer.byteLength);
61
+ // Return blob URL (browser-compatible and more efficient)
62
+ const blob = new Blob([arrayBuffer], { type: 'image/png' });
63
+ return URL.createObjectURL(blob);
47
64
  };
@@ -1,5 +1,8 @@
1
1
  /**
2
2
  * Image tracing and SVG sampling utilities
3
+ *
4
+ * Uses potrace for bitmap tracing. When running in CADit, potrace is exposed
5
+ * as an importable library by the code.worker, which handles the jimp dependency.
3
6
  */
4
7
  import { CrossSection } from '@cadit-app/manifold-3d/manifoldCAD';
5
8
  /**
@@ -16,6 +19,7 @@ export declare const svgStringToCrossSection: (svgContent: string, maxWidth?: nu
16
19
  export declare const sampleSvg: (svgDataUrl: string, maxWidth?: number) => Promise<CrossSection>;
17
20
  /**
18
21
  * Traces a bitmap image and returns a centered CrossSection
22
+ * Uses potrace with jimp for image loading (works in web workers).
19
23
  */
20
24
  export declare const traceImage: (imageDataUrl: string, options: {
21
25
  maxWidth?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../src/tracing.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAKlE;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,EAClB,UAAU,MAAM,kCAWjB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAClC,YAAY,MAAM,EAClB,WAAW,MAAM,EACjB,WAAU,MAAa,KACtB,OAAO,CAAC,YAAY,CAgBtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,GAAU,YAAY,MAAM,EAAE,WAAW,MAAM,KAAG,OAAO,CAAC,YAAY,CAO3F,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,cAAc,MAAM,EACpB,SAAS;IACP,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,KACA,OAAO,CAAC,YAAY,CAmBtB,CAAC"}
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAKlE;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,EAClB,UAAU,MAAM,kCAWjB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAClC,YAAY,MAAM,EAClB,WAAW,MAAM,EACjB,WAAU,MAAa,KACtB,OAAO,CAAC,YAAY,CAgBtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,GAAU,YAAY,MAAM,EAAE,WAAW,MAAM,KAAG,OAAO,CAAC,YAAY,CAO3F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,GACrB,cAAc,MAAM,EACpB,SAAS;IACP,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,KACA,OAAO,CAAC,YAAY,CAmBtB,CAAC"}
@@ -1,5 +1,8 @@
1
1
  /**
2
2
  * Image tracing and SVG sampling utilities
3
+ *
4
+ * Uses potrace for bitmap tracing. When running in CADit, potrace is exposed
5
+ * as an importable library by the code.worker, which handles the jimp dependency.
3
6
  */
4
7
  import { svgToPolygons } from '@cadit-app/svg-sampler';
5
8
  import { CrossSection } from '@cadit-app/manifold-3d/manifoldCAD';
@@ -49,6 +52,7 @@ export const sampleSvg = async (svgDataUrl, maxWidth) => {
49
52
  };
50
53
  /**
51
54
  * Traces a bitmap image and returns a centered CrossSection
55
+ * Uses potrace with jimp for image loading (works in web workers).
52
56
  */
53
57
  export const traceImage = async (imageDataUrl, options) => {
54
58
  const tracer = new Potrace({
@@ -56,7 +60,7 @@ export const traceImage = async (imageDataUrl, options) => {
56
60
  });
57
61
  // Promisify tracer.loadImage
58
62
  await new Promise((resolve, reject) => {
59
- tracer.loadImage(imageDataUrl, (err) => {
63
+ tracer.loadImage(imageDataUrl, (_potrace, err) => {
60
64
  if (err) {
61
65
  reject(err);
62
66
  }
package/package.json CHANGED
@@ -1,23 +1,27 @@
1
1
  {
2
2
  "name": "@cadit-app/image-extrude",
3
- "version": "0.1.1",
3
+ "version": "0.3.2",
4
4
  "description": "Image Extrude for CADit - Extrude shapes from SVG or bitmap images",
5
5
  "type": "module",
6
- "main": "dist/src/main.js",
6
+ "main": "dist/bundle.js",
7
7
  "types": "dist/src/main.d.ts",
8
- "scripts": {
9
- "typecheck": "tsc --noEmit",
10
- "build": "tsc",
11
- "prepublishOnly": "npm run build",
12
- "generate": "npx tsx cli.ts",
13
- "build:glb": "npx tsx cli.ts output.glb",
14
- "build:3mf": "npx tsx cli.ts output.3mf"
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/bundle.js",
11
+ "types": "./dist/src/main.d.ts"
12
+ },
13
+ "./unbundled": {
14
+ "import": "./dist/src/main.js",
15
+ "types": "./dist/src/main.d.ts"
16
+ }
17
+ },
18
+ "cadit": {
19
+ "browserBundle": "./dist/bundle.js"
15
20
  },
16
21
  "dependencies": {
17
22
  "@cadit-app/script-params": "0.4.1",
18
23
  "@cadit-app/svg-sampler": "^0.1.0",
19
24
  "@resvg/resvg-wasm": "^2.6.2",
20
- "@types/potrace": "^2.1.5",
21
25
  "potrace": "^2.1.8"
22
26
  },
23
27
  "peerDependencies": {
@@ -28,9 +32,12 @@
28
32
  "@gltf-transform/core": "^4.1.1",
29
33
  "@jscadui/3mf-export": "^0.5.0",
30
34
  "@types/node": "^25.0.3",
35
+ "@types/potrace": "^2.1.5",
36
+ "esbuild": "^0.24.0",
31
37
  "fflate": "^0.8.2",
32
38
  "tsx": "^4.0.0",
33
- "typescript": "^5.0.0"
39
+ "typescript": "^5.0.0",
40
+ "vite": "^7.3.1"
34
41
  },
35
42
  "keywords": [
36
43
  "cadit",
@@ -45,12 +52,20 @@
45
52
  "license": "MIT",
46
53
  "files": [
47
54
  "dist/**/*",
48
- "src/**/*",
49
55
  "images/**/*",
50
56
  "cadit.json",
51
57
  "README.md"
52
58
  ],
53
59
  "publishConfig": {
54
60
  "access": "public"
61
+ },
62
+ "scripts": {
63
+ "typecheck": "tsc --noEmit",
64
+ "build:types": "tsc",
65
+ "build:bundle": "vite build",
66
+ "build": "npm run build:types && npm run build:bundle",
67
+ "generate": "npx tsx cli.ts",
68
+ "build:glb": "npx tsx cli.ts output.glb",
69
+ "build:3mf": "npx tsx cli.ts output.3mf"
55
70
  }
56
- }
71
+ }
@@ -1,30 +0,0 @@
1
- /**
2
- * CrossSection utility functions
3
- */
4
-
5
- import { CrossSection } from '@cadit-app/manifold-3d/manifoldCAD';
6
-
7
- /**
8
- * Centers a CrossSection at the origin based on its bounding box
9
- */
10
- export function centerCrossSection(crossSection: CrossSection): CrossSection {
11
- const bounds = crossSection.bounds();
12
- const centerX = (bounds.min[0] + bounds.max[0]) / 2;
13
- const centerY = (bounds.min[1] + bounds.max[1]) / 2;
14
- return crossSection.translate([-centerX, -centerY]);
15
- }
16
-
17
- /**
18
- * Scales a CrossSection to fit within a maximum size while maintaining aspect ratio
19
- */
20
- export function scaleToMaxSize(crossSection: CrossSection, maxSize: number): CrossSection {
21
- const bounds = crossSection.bounds();
22
- const width = bounds.max[0] - bounds.min[0];
23
- const height = bounds.max[1] - bounds.min[1];
24
- const maxDim = Math.max(width, height);
25
-
26
- if (maxDim <= 0) return crossSection;
27
-
28
- const scale = maxSize / maxDim;
29
- return crossSection.scale([scale, scale]);
30
- }
package/src/main.ts DELETED
@@ -1,77 +0,0 @@
1
- /**
2
- * @cadit-app/image-extrude
3
- *
4
- * Extrude 3D shapes from SVG or bitmap images.
5
- * Uses the defineParams API from @cadit-app/script-params.
6
- */
7
-
8
- import { defineParams } from '@cadit-app/script-params';
9
- import type { Manifold, CrossSection } from '@cadit-app/manifold-3d/manifoldCAD';
10
- import { imageExtrudeParamsSchema, ImageExtrudeParams, ImageFileValue } from './params';
11
- import { sampleSvg, traceImage } from './tracing';
12
- import { renderSvgToBitmapDataUrl } from './resvg';
13
- import { fetchImageAsDataUrl } from './utils';
14
- import { createEmptyManifold } from './manifoldUtils';
15
-
16
- // Re-export for external use
17
- export { sampleSvg, traceImage } from './tracing';
18
- export { renderSvgToBitmapDataUrl } from './resvg';
19
- export { makeCrossSection } from './makeCrossSection';
20
-
21
- /**
22
- * Main entry point using defineParams
23
- */
24
- export default defineParams({
25
- params: imageExtrudeParamsSchema as any,
26
- main: async (params): Promise<Manifold> => {
27
- const typedParams = params as unknown as ImageExtrudeParams;
28
- let { mode, height } = typedParams;
29
- let imageFile: ImageFileValue | undefined = typedParams.imageFile;
30
-
31
- // If imageFile has imageUrl but not dataUrl, fetch and convert to dataUrl
32
- if (imageFile && !imageFile.dataUrl && imageFile.imageUrl) {
33
- try {
34
- imageFile = {
35
- ...imageFile,
36
- dataUrl: await fetchImageAsDataUrl(imageFile.imageUrl)
37
- };
38
- } catch (err) {
39
- console.warn('Failed to fetch imageUrl:', err);
40
- return createEmptyManifold();
41
- }
42
- }
43
-
44
- if (!imageFile || !imageFile.dataUrl) {
45
- console.warn('No valid image file provided.');
46
- return createEmptyManifold();
47
- }
48
-
49
- // Adjust mode if sample is selected for non-SVG
50
- if (mode === 'sample' && !imageFile.fileType?.includes('svg')) {
51
- console.warn('Sample mode selected for non-SVG file. Defaulting to Trace mode.');
52
- mode = 'trace';
53
- }
54
-
55
- let crossSection: CrossSection;
56
- try {
57
- if (mode === 'trace') {
58
- // if svg, render svg to bitmap and then trace
59
- const isSvg = imageFile.fileType?.includes('svg');
60
- const dataUrl = isSvg ? await renderSvgToBitmapDataUrl(imageFile.dataUrl) : imageFile.dataUrl;
61
-
62
- crossSection = await traceImage(dataUrl, {
63
- maxWidth: typedParams.maxWidth,
64
- despeckleSize: typedParams.despeckleSize
65
- });
66
- } else {
67
- // mode is 'sample', and fileType is guaranteed to be svg+xml
68
- crossSection = await sampleSvg(imageFile.dataUrl, typedParams.maxWidth);
69
- }
70
- } catch (error) {
71
- console.error(`Error during image processing (mode: ${mode}):`, error);
72
- return createEmptyManifold();
73
- }
74
-
75
- return crossSection.extrude(height);
76
- },
77
- });
@@ -1,57 +0,0 @@
1
- /**
2
- * Create a CrossSection from image parameters
3
- * Exported for external use (e.g., embedding in other makers)
4
- */
5
-
6
- import type { CrossSection } from '@cadit-app/manifold-3d/manifoldCAD';
7
- import { ImageExtrudeParams } from './params';
8
- import { sampleSvg, traceImage } from './tracing';
9
- import { renderSvgToBitmapDataUrl } from './resvg';
10
- import { fetchImageAsDataUrl } from './utils';
11
-
12
- export type MakeCrossSectionOptions = {
13
- imageFile: ImageExtrudeParams['imageFile'];
14
- mode: 'trace' | 'sample';
15
- maxWidth?: number;
16
- despeckleSize?: number;
17
- };
18
-
19
- /**
20
- * Creates a CrossSection from image data
21
- * This is the main function for embedding in other makers
22
- */
23
- export async function makeCrossSection(options: MakeCrossSectionOptions): Promise<CrossSection> {
24
- let { imageFile, mode, maxWidth, despeckleSize } = options;
25
-
26
- // If imageFile has imageUrl but not dataUrl, fetch and convert
27
- if (imageFile && !imageFile.dataUrl && imageFile.imageUrl) {
28
- imageFile = {
29
- ...imageFile,
30
- dataUrl: await fetchImageAsDataUrl(imageFile.imageUrl)
31
- };
32
- }
33
-
34
- if (!imageFile?.dataUrl) {
35
- throw new Error('No valid image file provided');
36
- }
37
-
38
- // Adjust mode if sample is selected for non-SVG
39
- if (mode === 'sample' && !imageFile.fileType?.includes('svg')) {
40
- console.warn('Sample mode selected for non-SVG file. Defaulting to Trace mode.');
41
- mode = 'trace';
42
- }
43
-
44
- if (mode === 'trace') {
45
- // if svg, render svg to bitmap and then trace
46
- const isSvg = imageFile.fileType?.includes('svg');
47
- const dataUrl = isSvg ? await renderSvgToBitmapDataUrl(imageFile.dataUrl) : imageFile.dataUrl;
48
-
49
- return traceImage(dataUrl, {
50
- maxWidth,
51
- despeckleSize
52
- });
53
- } else {
54
- // mode is 'sample', and fileType is guaranteed to be svg+xml
55
- return sampleSvg(imageFile.dataUrl, maxWidth);
56
- }
57
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Manifold utility functions
3
- */
4
-
5
- import { Manifold, CrossSection } from '@cadit-app/manifold-3d/manifoldCAD';
6
-
7
- /**
8
- * Creates an empty manifold (a very small cube that will be invisible)
9
- */
10
- export function createEmptyManifold(): Manifold {
11
- // Create a tiny box that's essentially invisible
12
- return CrossSection.square([0.001, 0.001], true).extrude(0.001);
13
- }
package/src/params.ts DELETED
@@ -1,70 +0,0 @@
1
- /**
2
- * Parameter schema for the Image Extrude generator.
3
- */
4
-
5
- /**
6
- * Value type for image parameters.
7
- * Matches the ImageFileValue type from @cadit-app/script-params.
8
- * TODO: Import from script-params once version with ImageFileValue is published.
9
- */
10
- export interface ImageFileValue {
11
- /** Remote URL to fetch the image from (e.g., HTTP URL or relative path). */
12
- imageUrl?: string;
13
- /** Base64-encoded data URL of the image content. */
14
- dataUrl?: string;
15
- /** MIME type of the image (e.g., 'image/svg+xml', 'image/png'). */
16
- fileType?: string;
17
- /** Original filename of the image. */
18
- fileName?: string;
19
- }
20
-
21
- // Default SVG - a simple star shape
22
- const defaultSvgDataUrl = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cG9seWdvbiBwb2ludHM9IjUwLDUgNjEsMzkgOTcsMzkgNjgsMjIgNzksOTUgNTAsNzAgMjEsOTUgMzIsNjIgMywzOSAzOSwzOSIgZmlsbD0iYmxhY2siLz48L3N2Zz4=';
23
-
24
- export const imageExtrudeParamsSchema = {
25
- mode: {
26
- type: 'choice' as const,
27
- label: 'Mode',
28
- options: [
29
- { value: 'trace', label: 'Trace' },
30
- { value: 'sample', label: 'Sample (SVG only)' },
31
- ],
32
- default: 'trace',
33
- },
34
- imageFile: {
35
- type: 'image' as const,
36
- label: 'Image File',
37
- default: {
38
- imageUrl: '',
39
- dataUrl: defaultSvgDataUrl,
40
- fileType: 'image/svg+xml',
41
- fileName: 'star.svg'
42
- } as ImageFileValue,
43
- },
44
- height: {
45
- type: 'number' as const,
46
- label: 'Extrusion Height (mm)',
47
- default: 1,
48
- min: 0.1,
49
- },
50
- maxWidth: {
51
- type: 'number' as const,
52
- label: 'Maximum Width (mm)',
53
- default: 50,
54
- min: 0.1,
55
- },
56
- despeckleSize: {
57
- type: 'number' as const,
58
- label: 'Despeckle Size (Tracing only)',
59
- default: 2,
60
- min: 0.1,
61
- },
62
- };
63
-
64
- export type ImageExtrudeParams = {
65
- mode: 'trace' | 'sample';
66
- imageFile: ImageFileValue;
67
- height: number;
68
- maxWidth: number;
69
- despeckleSize: number;
70
- };
package/src/resvg.ts DELETED
@@ -1,63 +0,0 @@
1
- /**
2
- * SVG to bitmap rendering using resvg-wasm
3
- */
4
-
5
- import * as resvg from '@resvg/resvg-wasm';
6
- import { svgDataUrlToString } from './utils';
7
-
8
- let wasmInitialized = false;
9
-
10
- /**
11
- * Initialize resvg WASM module
12
- * In CLI context, we need to load from file system
13
- * In browser context (via bundler), the WASM is loaded differently
14
- */
15
- async function initializeResvgWasm() {
16
- if (wasmInitialized) return;
17
-
18
- // For CLI usage, load WASM from node_modules
19
- const { readFile } = await import('fs/promises');
20
- const { resolve, dirname } = await import('path');
21
- const { fileURLToPath } = await import('url');
22
-
23
- // Find the wasm file in node_modules
24
- const wasmPath = resolve(
25
- dirname(fileURLToPath(import.meta.url)),
26
- '../node_modules/@resvg/resvg-wasm/index_bg.wasm'
27
- );
28
-
29
- const wasmBuffer = await readFile(wasmPath);
30
- await resvg.initWasm(wasmBuffer);
31
- wasmInitialized = true;
32
- }
33
-
34
- /**
35
- * Renders an SVG Data URL to a PNG Data URL using resvg-wasm.
36
- */
37
- export const renderSvgToBitmapDataUrl = async (
38
- svgDataUrl: string,
39
- options?: { maxWidth?: number }
40
- ): Promise<string> => {
41
- await initializeResvgWasm();
42
-
43
- const svgString = svgDataUrlToString(svgDataUrl);
44
- if (!svgString) {
45
- throw new Error("Could not decode SVG Data URL");
46
- }
47
-
48
- const opts: Record<string, unknown> = {};
49
- if (options?.maxWidth && options.maxWidth > 0 && isFinite(options.maxWidth)) {
50
- opts.fitTo = {
51
- mode: 'width',
52
- value: options.maxWidth
53
- };
54
- }
55
-
56
- const resvgInstance = new resvg.Resvg(svgString, opts);
57
- const pngData = resvgInstance.render();
58
- const pngBuffer = pngData.asPng();
59
-
60
- // Convert to data URL
61
- const base64 = Buffer.from(pngBuffer).toString('base64');
62
- return `data:image/png;base64,${base64}`;
63
- };
@@ -1,67 +0,0 @@
1
- /**
2
- * 3MF Export for image-extrude
3
- */
4
-
5
- import type { Manifold } from '@cadit-app/manifold-3d/manifoldCAD';
6
- // @ts-ignore - No type declarations available
7
- import { to3dmodel, fileForContentTypes, FileForRelThumbnail } from '@jscadui/3mf-export';
8
- import { strToU8, zipSync, Zippable } from 'fflate';
9
-
10
- /**
11
- * Creates a 3MF ArrayBuffer from manifolds
12
- */
13
- export async function create3mfArrayBuffer(manifolds: Manifold[]): Promise<ArrayBuffer> {
14
- const meshes = manifolds.map((m, index) => {
15
- const mesh = m.getMesh();
16
- return {
17
- id: String(index + 1),
18
- vertices: mesh.vertProperties,
19
- indices: mesh.triVerts,
20
- name: `Part-${index + 1}`,
21
- };
22
- });
23
-
24
- // Generate a single component with all meshes as children
25
- const components = [
26
- {
27
- id: meshes.length + 1,
28
- children: meshes.map((mesh) => ({ objectID: mesh.id })),
29
- name: 'ImageExtrude-Assembly',
30
- },
31
- ];
32
-
33
- // The main item should reference the component
34
- const items = components.map((component) => ({
35
- objectID: component.id,
36
- }));
37
-
38
- const header = {
39
- unit: 'millimeter',
40
- title: 'CADit Image Extrude',
41
- description: 'Image Extrude 3MF export',
42
- application: 'CADit',
43
- };
44
-
45
- const to3mf = {
46
- meshes,
47
- components,
48
- items,
49
- precision: 7,
50
- header,
51
- };
52
-
53
- // Generate the 3D model XML
54
- const model = to3dmodel(to3mf as any);
55
-
56
- // Package the 3MF file using fflate
57
- const fileForRelThumbnail = new FileForRelThumbnail();
58
- fileForRelThumbnail.add3dModel('3D/3dmodel.model');
59
-
60
- const files: Zippable = {};
61
- files['3D/3dmodel.model'] = strToU8(model);
62
- files[fileForContentTypes.name] = strToU8(fileForContentTypes.content);
63
- files[fileForRelThumbnail.name] = strToU8(fileForRelThumbnail.content);
64
-
65
- const zipData = zipSync(files);
66
- return zipData.buffer as ArrayBuffer;
67
- }