@cadit-app/maker-chips 0.2.1

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.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # @cadit-app/maker-chips
2
+
3
+ Generate customizable maker chips with various patterns and optional embedded QR codes. Perfect for multi-color 3D printing.
4
+
5
+ ![Makerchip Maker Preview](images/preview.png)
6
+
7
+ ## Features
8
+
9
+ - **20 Pattern Styles**: Choose from a variety of decorative chip patterns
10
+ - **Customizable Dimensions**: Adjust radius, height, and edge rounding
11
+ - **Center Circle**: Add a center circle for additional customization
12
+ - **Multi-Color 3D Printing**: 3MF export with separate parts for multi-extruder printers
13
+ - **Assembly Modes**: Flat preview or assembled for printing
14
+ - **CLI Support**: Generate models directly from the command line
15
+
16
+ ## Usage
17
+
18
+ ### Open in CADit
19
+
20
+ Open this design in [CADit](https://app.cadit.app) to customize and generate your maker chip with a visual interface.
21
+
22
+ ### Command Line
23
+
24
+ Generate 3D models directly from the command line:
25
+
26
+ ```bash
27
+ # Install dependencies
28
+ pnpm install
29
+
30
+ # Generate a .glb file (3D model)
31
+ pnpm build:glb
32
+
33
+ # Generate a .3mf file (for multi-color 3D printing)
34
+ pnpm build:3mf
35
+
36
+ # Generate with custom parameters
37
+ npx tsx cli.ts my-chip.glb --radius 25 --markings makerChipV5
38
+ npx tsx cli.ts my-chip.3mf --markings makerChipV10 --assembly printable
39
+ ```
40
+
41
+ ### CLI Options
42
+
43
+ ```
44
+ Usage:
45
+ npx tsx cli.ts <output.[glb|3mf]> [options]
46
+
47
+ Output Formats:
48
+ .glb 3D model (GLTF binary)
49
+ .3mf 3D model for multi-color printing
50
+
51
+ Options:
52
+ -r, --radius <number> Chip radius in mm (default: 20)
53
+ -h, --height <number> Extrusion height in mm (default: 3)
54
+ --rounding <number> Edge rounding radius in mm (default: 1)
55
+ --center-radius <number> Center circle radius in mm (default: 14)
56
+ -a, --assembly <type> Assembly type: flat or printable (default: flat)
57
+ -m, --markings <pattern> Pattern style (default: makerChipV1)
58
+ --help Show this help
59
+
60
+ Examples:
61
+ npx tsx cli.ts chip.glb
62
+ npx tsx cli.ts chip.3mf --markings makerChipV5 --radius 25
63
+ npx tsx cli.ts chip.glb -m makerChipV10 -a printable
64
+ ```
65
+
66
+ ### Available Patterns
67
+
68
+ The following pattern styles are available:
69
+
70
+ `makerChipV1`, `makerChipV2`, `makerChipV3`, `makerChipV4`, `makerChipV5`,
71
+ `makerChipV6`, `makerChipV7`, `makerChipV8`, `makerChipV9`, `makerChipV10`,
72
+ `makerChipV11`, `makerChipV12`, `makerChipV13`, `makerChipV14`, `makerChipV15`,
73
+ `makerChipV16`, `makerChipV17`, `makerChipV18`, `makerChipV19`, `makerChipV20`
74
+
75
+ ## Parameters
76
+
77
+ | Parameter | Type | Default | Description |
78
+ |-----------|------|---------|-------------|
79
+ | radius | number | 20 | Chip radius in millimeters |
80
+ | height | number | 3 | Extrusion height in millimeters |
81
+ | roundingRadius | number | 1 | Edge rounding radius in millimeters |
82
+ | centerCircleRadius | number | 14 | Center circle radius in millimeters |
83
+ | assemblyType | choice | flat | Assembly mode: flat (preview) or printable |
84
+ | markings | buttonGrid | makerChipV1 | Pattern style selection |
85
+
86
+ ## Multi-Color Printing
87
+
88
+ The 3MF export includes separate parts assigned to different extruders:
89
+ - Part 1: Base disk (Extruder 1)
90
+ - Part 2: Center circle (Extruder 2)
91
+ - Part 3: Pattern/marking (Extruder 3)
92
+
93
+ This works with slicers like Bambu Studio, PrusaSlicer, and Cura.
94
+
95
+ ## Attribution
96
+
97
+ This project is based on [Makerchip Maker Chip](https://makerworld.com/en/models/415825-makerchip-maker-chip) by K2_Kevin.
98
+
99
+ The original maker chip designs were manually converted to SVG and used as a starting point for this parametric generator.
100
+
101
+ **License**: [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)
102
+
103
+ ---
104
+
105
+ <p align="center">
106
+ <sub>Created with <a href="https://cadit.app">CADit</a> - The open platform for code-based 3D models.</sub>
107
+ </p>
package/cadit.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "Makerchip Maker",
3
+ "description": "Generate customizable maker chips with various patterns and optional embedded QR codes. Features 20 decorative patterns and multi-color 3MF export for 3D printing.",
4
+ "entryPoint": "src/main.ts",
5
+ "tags": ["maker-chip", "patterns", "3d-printing", "multi-color", "customizable"],
6
+ "category": "Tools",
7
+ "license": {
8
+ "type": "CC BY-NC-SA 4.0",
9
+ "url": "https://creativecommons.org/licenses/by-nc-sa/4.0/"
10
+ },
11
+ "author": {
12
+ "name": "CADit"
13
+ },
14
+ "attribution": {
15
+ "isDerivative": true,
16
+ "originalAuthor": "K2_Kevin",
17
+ "originalAuthorUrl": "https://makerworld.com/en/@K2_Kevin",
18
+ "sourceUrl": "https://makerworld.com/en/models/415825-makerchip-maker-chip",
19
+ "description": "Original maker chip designs by K2_Kevin were manually converted to SVG and used as a starting point."
20
+ },
21
+ "images": [
22
+ {
23
+ "path": "images/preview.png",
24
+ "alt": "Makerchip Maker Preview",
25
+ "isPrimary": true
26
+ }
27
+ ]
28
+ }
package/cli.ts ADDED
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * CLI for generating Makerchip models
4
+ *
5
+ * Usage:
6
+ * npx tsx cli.ts output.glb
7
+ * npx tsx cli.ts output.3mf
8
+ * npx tsx cli.ts output.glb --radius 25 --markings makerChipV5
9
+ */
10
+
11
+ import { extname, basename } from 'path';
12
+ import { writeFile, readFile } from 'fs/promises';
13
+ import { existsSync } from 'fs';
14
+ import { parseArgs } from 'util';
15
+ import type { Manifold } from '@cadit-app/manifold-3d';
16
+
17
+ const SUPPORTED_FORMATS = ['.glb', '.3mf'] as const;
18
+ type OutputFormat = (typeof SUPPORTED_FORMATS)[number];
19
+
20
+ // Parse command line arguments
21
+ const { values, positionals } = parseArgs({
22
+ allowPositionals: true,
23
+ options: {
24
+ radius: { type: 'string', short: 'r', default: '20' },
25
+ height: { type: 'string', short: 'h', default: '3' },
26
+ rounding: { type: 'string', default: '1' },
27
+ 'center-radius': { type: 'string', default: '14' },
28
+ assembly: { type: 'string', short: 'a', default: 'flat' },
29
+ markings: { type: 'string', short: 'm', default: 'makerChipV1' },
30
+ // QR Code embedded params
31
+ 'qr-enabled': { type: 'boolean', default: false },
32
+ 'qr-content': { type: 'string', default: 'https://cadit.app' },
33
+ 'qr-size': { type: 'string', default: '18' },
34
+ 'qr-height': { type: 'string', default: '1' },
35
+ // Image Extrude embedded params
36
+ 'image-enabled': { type: 'boolean', default: false },
37
+ 'image-file': { type: 'string' },
38
+ 'image-mode': { type: 'string', default: 'sample' },
39
+ 'image-height': { type: 'string', default: '1' },
40
+ 'image-max-width': { type: 'string', default: '18' },
41
+ help: { type: 'boolean', default: false },
42
+ },
43
+ });
44
+
45
+ if (values.help || positionals.length === 0) {
46
+ console.log(`
47
+ Makerchip Generator CLI
48
+
49
+ Usage:
50
+ npx tsx cli.ts <output.[glb|3mf]> [options]
51
+
52
+ Output Formats:
53
+ .glb 3D model (GLTF binary)
54
+ .3mf 3D model for multi-color printing
55
+
56
+ Chip Options:
57
+ -r, --radius <number> Chip radius in mm (default: 20)
58
+ -h, --height <number> Extrusion height in mm (default: 3)
59
+ --rounding <number> Edge rounding radius in mm (default: 1)
60
+ --center-radius <number> Center circle radius in mm (default: 14)
61
+ -a, --assembly <type> Assembly type: flat or printable (default: flat)
62
+ -m, --markings <pattern> Pattern style (default: makerChipV1)
63
+
64
+ QR Code Options (embedded maker):
65
+ --qr-enabled Enable QR code generation
66
+ --qr-content <text> QR code content (default: https://cadit.app)
67
+ --qr-size <number> QR code size in mm (default: 18)
68
+ --qr-height <number> QR code extrusion height in mm (default: 1)
69
+
70
+ Image Extrude Options (embedded maker):
71
+ --image-enabled Enable image extrusion
72
+ --image-file <path> Path to image file (SVG, PNG, JPG)
73
+ --image-mode <trace|sample> Processing mode (default: sample)
74
+ --image-height <number> Extrusion height in mm (default: 1)
75
+ --image-max-width <number> Maximum width in mm (default: 18)
76
+
77
+ General:
78
+ --help Show this help
79
+
80
+ Available Patterns:
81
+ makerChipV1 through makerChipV20
82
+
83
+ Examples:
84
+ npx tsx cli.ts chip.glb
85
+ npx tsx cli.ts chip.3mf --markings makerChipV5 --radius 25
86
+ npx tsx cli.ts chip.glb -m makerChipV10 -a printable
87
+ npx tsx cli.ts chip.glb --qr-enabled --qr-content "Hello World"
88
+ npx tsx cli.ts chip.glb --image-enabled --image-file logo.svg
89
+ `);
90
+ process.exit(0);
91
+ }
92
+
93
+ const outputFile = positionals[0];
94
+ const ext = extname(outputFile).toLowerCase() as OutputFormat;
95
+
96
+ if (!SUPPORTED_FORMATS.includes(ext)) {
97
+ console.error(`Error: Output file must have one of these extensions: ${SUPPORTED_FORMATS.join(', ')}`);
98
+ console.error(`Got: ${ext}`);
99
+ process.exit(1);
100
+ }
101
+
102
+ async function main() {
103
+ console.log('Initializing manifold...');
104
+
105
+ // Initialize manifold-3d (must happen before importing maker modules)
106
+ const manifoldModule = await import('@cadit-app/manifold-3d');
107
+ await manifoldModule.default();
108
+
109
+ console.log('Loading Makerchip module...');
110
+
111
+ // Import the Makerchip generator
112
+ const makerchipModule = await import('./src/main');
113
+ const { getDefaults } = await import('@cadit-app/script-params');
114
+ const { qrCodeParamsSchema } = await import('@cadit-app/qr-code/src/params');
115
+ const { imageExtrudeParamsSchema } = await import('@cadit-app/image-extrude/src/params');
116
+
117
+ // Get defaults from the embedded makers' schemas
118
+ const qrCodeDefaults = getDefaults(qrCodeParamsSchema);
119
+ const imageExtrudeDefaults = getDefaults(imageExtrudeParamsSchema);
120
+
121
+ // Build QR code embedded params
122
+ const qrCodeSettings = {
123
+ enabled: values['qr-enabled'] ?? false,
124
+ showSettings: false,
125
+ params: {
126
+ ...qrCodeDefaults,
127
+ text: values['qr-content'] || qrCodeDefaults.text,
128
+ size: parseFloat(values['qr-size'] || String(qrCodeDefaults.size)),
129
+ extrudeDepth: parseFloat(values['qr-height'] || String(qrCodeDefaults.extrudeDepth)),
130
+ },
131
+ };
132
+
133
+ // Build image extrude embedded params
134
+ const imageExtrudeSettings = {
135
+ enabled: values['image-enabled'] ?? false,
136
+ showSettings: false,
137
+ params: {
138
+ ...imageExtrudeDefaults,
139
+ mode: values['image-mode'] || imageExtrudeDefaults.mode,
140
+ height: parseFloat(values['image-height'] || String(imageExtrudeDefaults.height)),
141
+ maxWidth: parseFloat(values['image-max-width'] || String(imageExtrudeDefaults.maxWidth)),
142
+ },
143
+ };
144
+
145
+ // Load image file if specified
146
+ if (values['image-enabled'] && values['image-file']) {
147
+ const imagePath = values['image-file'];
148
+ if (existsSync(imagePath)) {
149
+ const imageData = await readFile(imagePath);
150
+ const base64 = imageData.toString('base64');
151
+ const imageExt = extname(imagePath).toLowerCase();
152
+ const mimeType = imageExt === '.svg' ? 'image/svg+xml' :
153
+ imageExt === '.png' ? 'image/png' :
154
+ imageExt === '.jpg' || imageExt === '.jpeg' ? 'image/jpeg' :
155
+ 'application/octet-stream';
156
+ imageExtrudeSettings.params.imageFile = {
157
+ dataUrl: `data:${mimeType};base64,${base64}`,
158
+ fileType: mimeType,
159
+ fileName: basename(imagePath),
160
+ };
161
+ console.log(`Loaded image: ${imagePath}`);
162
+ } else {
163
+ console.warn(`Warning: Image file not found: ${imagePath}`);
164
+ }
165
+ }
166
+
167
+ const params = {
168
+ radius: parseFloat(values.radius || '20'),
169
+ height: parseFloat(values.height || '3'),
170
+ roundingRadius: parseFloat(values.rounding || '1'),
171
+ centerCircleRadius: parseFloat(values['center-radius'] || '14'),
172
+ assemblyType: values.assembly || 'flat',
173
+ markings: values.markings || 'makerChipV1',
174
+ qrCodeSettings,
175
+ imageExtrudeSettings,
176
+ };
177
+
178
+ console.log('Generating Makerchip with params:', {
179
+ ...params,
180
+ qrCodeSettings: { enabled: qrCodeSettings.enabled, text: qrCodeSettings.params.text },
181
+ imageExtrudeSettings: { enabled: imageExtrudeSettings.enabled, hasImage: !!imageExtrudeSettings.params.imageFile?.dataUrl },
182
+ });
183
+
184
+ if (ext === '.3mf') {
185
+ // Use the 3MF exporter directly
186
+ const { threeMfExport } = await import('./src/threeMfExport');
187
+ const result = await threeMfExport(params as any);
188
+ await writeFile(outputFile, Buffer.from(result.data as ArrayBuffer));
189
+ console.log(`✓ Generated ${outputFile}`);
190
+ } else {
191
+ // Generate the manifold for GLB
192
+ const result = await makerchipModule.default(params) as Manifold;
193
+
194
+ if (!result || typeof (result as any).getMesh !== 'function') {
195
+ console.error('Error: Script did not return a valid Manifold object');
196
+ process.exit(1);
197
+ }
198
+
199
+ console.log('Converting to GLTF document...');
200
+
201
+ // Convert to GLTF document
202
+ const { manifoldToGLTFDoc } = await import('@cadit-app/manifold-3d/lib/scene-builder.js');
203
+ const exportModel = await import('@cadit-app/manifold-3d/lib/export-model.js');
204
+ const doc = await manifoldToGLTFDoc(result as any);
205
+
206
+ console.log(`Exporting to ${outputFile}...`);
207
+
208
+ // Export to file
209
+ await exportModel.writeFile(outputFile, doc);
210
+
211
+ console.log(`✓ Generated ${outputFile}`);
212
+ }
213
+ }
214
+
215
+ main().catch((err) => {
216
+ console.error('Error:', err.message || err);
217
+ process.exit(1);
218
+ });
Binary file
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@cadit-app/maker-chips",
3
+ "version": "0.2.1",
4
+ "description": "Customizable maker chip generator with patterns and embedded QR codes",
5
+ "type": "module",
6
+ "main": "src/main.ts",
7
+ "dependencies": {
8
+ "@cadit-app/script-params": "^0.4.1",
9
+ "@cadit-app/svg-sampler": "^0.1.0",
10
+ "@cadit-app/qr-code": "^0.5.1",
11
+ "@cadit-app/image-extrude": "^0.3.2",
12
+ "@jscadui/3mf-export": "^0.5.0",
13
+ "fflate": "^0.8.2"
14
+ },
15
+ "peerDependencies": {
16
+ "@cadit-app/manifold-3d": "^3.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@cadit-app/manifold-3d": "^3.3.2",
20
+ "@types/node": "^25.0.0",
21
+ "tsx": "^4.0.0",
22
+ "typescript": "^5.0.0"
23
+ },
24
+ "keywords": [
25
+ "cadit",
26
+ "maker-chip",
27
+ "3d-printing",
28
+ "parametric",
29
+ "qr-code"
30
+ ],
31
+ "author": {
32
+ "name": "CADit"
33
+ },
34
+ "license": "MIT",
35
+ "scripts": {
36
+ "typecheck": "tsc --noEmit",
37
+ "build": "tsc",
38
+ "generate": "npx tsx cli.ts",
39
+ "build:glb": "npx tsx cli.ts output.glb",
40
+ "build:3mf": "npx tsx cli.ts output.3mf"
41
+ }
42
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Script to generate embedded SVGs from the images folder
3
+ * Run with: node scripts/generate-embedded-svgs.cjs
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const imagesDir = path.join(__dirname, '../src/images');
10
+ const outputFile = path.join(__dirname, '../src/embeddedSvgs.ts');
11
+
12
+ // Get all SVG files
13
+ const svgFiles = fs.readdirSync(imagesDir)
14
+ .filter(f => f.endsWith('.svg'))
15
+ .sort((a, b) => {
16
+ // Sort numerically by version number
17
+ const numA = parseInt(a.match(/v(\d+)/)?.[1] || '0');
18
+ const numB = parseInt(b.match(/v(\d+)/)?.[1] || '0');
19
+ return numA - numB;
20
+ });
21
+
22
+ // Generate TypeScript content
23
+ let content = `/**
24
+ * Embedded SVG images for maker chip patterns.
25
+ * Auto-generated by scripts/generate-embedded-svgs.cjs
26
+ */
27
+
28
+ export const embeddedSvgs: Record<string, string> = {\n`;
29
+
30
+ for (const file of svgFiles) {
31
+ const svgContent = fs.readFileSync(path.join(imagesDir, file), 'utf-8');
32
+ // Convert filename to key: makerchip-v1.svg -> makerChipV1
33
+ const key = file
34
+ .replace('.svg', '')
35
+ .replace(/makerchip-v(\d+)/, (_, num) => `makerChipV${num}`);
36
+
37
+ // Escape backticks and convert to template literal
38
+ const escapedContent = svgContent.replace(/`/g, '\\`').replace(/\$/g, '\\$');
39
+ content += ` ${key}: \`${escapedContent}\`,\n`;
40
+ }
41
+
42
+ content += `};\n\n`;
43
+
44
+ // Generate data URL versions for button grid display
45
+ content += `/**
46
+ * SVG data URLs for button grid display
47
+ */
48
+ export const svgDataUrls: Record<string, string> = Object.fromEntries(
49
+ Object.entries(embeddedSvgs).map(([key, svg]) => [
50
+ key,
51
+ \`data:image/svg+xml;base64,\${typeof btoa !== 'undefined' ? btoa(svg) : Buffer.from(svg).toString('base64')}\`
52
+ ])
53
+ );
54
+ `;
55
+
56
+ fs.writeFileSync(outputFile, content);
57
+ console.log(`Generated ${outputFile} with ${svgFiles.length} SVGs`);
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Assembly utilities for composing Makerchip shapes.
3
+ */
4
+
5
+ import type { Manifold } from '@cadit-app/manifold-3d';
6
+ import { roundedDisk, generateMarkingShape, generateCenterDisk } from './disk';
7
+ import type { MakerChipParams } from './params';
8
+ import qrCodeMaker from '@cadit-app/qr-code';
9
+ import imageExtrudeMaker from '@cadit-app/image-extrude';
10
+
11
+ export type AssemblyType = 'flat' | 'printable';
12
+
13
+ /**
14
+ * Assembles all shapes for the Makerchip.
15
+ *
16
+ * @param params - The parameters for the Makerchip
17
+ * @param assemblyType - 'flat' for preview/export, 'printable' for 3D printing
18
+ * @returns An array of Manifold shapes
19
+ */
20
+ export async function assembleMakerchipShapes(
21
+ params: MakerChipParams,
22
+ assemblyType: AssemblyType
23
+ ): Promise<Manifold[]> {
24
+ // Create chip base
25
+ const disk = roundedDisk({
26
+ radius: params.radius,
27
+ roundingRadius: params.roundingRadius,
28
+ height: params.height,
29
+ });
30
+
31
+ // Create marking pattern
32
+ const marking = await generateMarkingShape({
33
+ shapeName: params.markings,
34
+ radius: params.radius,
35
+ roundingRadius: params.roundingRadius,
36
+ height: params.height,
37
+ });
38
+
39
+ // Create center disk
40
+ const centerDisk = generateCenterDisk({
41
+ centerCircleRadius: params.centerCircleRadius,
42
+ height: params.height,
43
+ });
44
+
45
+ // Generate QR code if enabled
46
+ let qrCode: Manifold | undefined;
47
+ if (params.qrCodeSettings?.enabled) {
48
+ try {
49
+ // qrCodeMaker is a callable ScriptModule - call it directly with params
50
+ qrCode = await qrCodeMaker(params.qrCodeSettings.params) as Manifold;
51
+ } catch (error) {
52
+ console.error('Error generating QR code:', error);
53
+ }
54
+ }
55
+
56
+ // Generate image extrude if enabled
57
+ let imageExtrude: Manifold | undefined;
58
+ if (params.imageExtrudeSettings?.enabled) {
59
+ try {
60
+ // imageExtrudeMaker is a callable ScriptModule - call it directly with params
61
+ imageExtrude = await imageExtrudeMaker(params.imageExtrudeSettings.params) as Manifold;
62
+ } catch (error) {
63
+ console.error('Error generating image extrude:', error);
64
+ }
65
+ }
66
+
67
+ const allShapes: Manifold[] = [];
68
+
69
+ if (assemblyType === 'flat') {
70
+ // Spread shapes out for preview
71
+ const offset = 2 * params.radius + 1;
72
+ allShapes.push(disk);
73
+ allShapes.push(marking.translate([offset, 0, 0]));
74
+ allShapes.push(centerDisk.translate([0, offset, 0]));
75
+
76
+ if (qrCode) {
77
+ const qrSize = params.qrCodeSettings.params.size || 18;
78
+ allShapes.push(qrCode.translate([-(params.radius + qrSize / 2 + 1), 0, 0]));
79
+ }
80
+
81
+ if (imageExtrude) {
82
+ const bounds = imageExtrude.boundingBox();
83
+ const height = bounds.max[1] - bounds.min[1];
84
+ allShapes.push(imageExtrude.translate([0, -(params.radius + height / 2 + 1), 0]));
85
+ }
86
+ } else if (assemblyType === 'printable') {
87
+ // Stack shapes for printing
88
+ allShapes.push(disk);
89
+ allShapes.push(centerDisk);
90
+ allShapes.push(marking);
91
+
92
+ if (qrCode) {
93
+ // QR code is always on top
94
+ const zTranslate = params.height - qrCode.boundingBox().max[2];
95
+ allShapes.push(qrCode.translate([0, 0, zTranslate]));
96
+ }
97
+
98
+ if (imageExtrude) {
99
+ // Image extrude is always on bottom (flip it)
100
+ allShapes.push(imageExtrude.mirror([1, 0, 0]));
101
+ }
102
+ }
103
+
104
+ return allShapes;
105
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * CrossSection utility functions for scaling and centering.
3
+ */
4
+
5
+ import type { CrossSection, Vec2 } from '@cadit-app/manifold-3d';
6
+
7
+ /**
8
+ * Scales a cross-section to fit within the specified width and height,
9
+ * maintaining aspect ratio and centering the result.
10
+ */
11
+ export function scaleToSizeAndCenter(
12
+ crossSection: CrossSection,
13
+ targetWidth: number,
14
+ targetHeight: number
15
+ ): CrossSection {
16
+ const bounds = crossSection.bounds();
17
+ const currentWidth = bounds.max[0] - bounds.min[0];
18
+ const currentHeight = bounds.max[1] - bounds.min[1];
19
+
20
+ if (currentWidth === 0 || currentHeight === 0) {
21
+ return crossSection;
22
+ }
23
+
24
+ // Calculate scale to fit within target dimensions
25
+ const scaleX = targetWidth / currentWidth;
26
+ const scaleY = targetHeight / currentHeight;
27
+ const scale = Math.min(scaleX, scaleY);
28
+
29
+ // Scale the cross-section
30
+ const scaled = crossSection.scale([scale, scale]);
31
+
32
+ // Get new bounds after scaling
33
+ const scaledBounds = scaled.bounds();
34
+ const scaledWidth = scaledBounds.max[0] - scaledBounds.min[0];
35
+ const scaledHeight = scaledBounds.max[1] - scaledBounds.min[1];
36
+
37
+ // Center the result
38
+ const offsetX = -scaledBounds.min[0] - scaledWidth / 2;
39
+ const offsetY = -scaledBounds.min[1] - scaledHeight / 2;
40
+
41
+ return scaled.translate([offsetX, offsetY]);
42
+ }