@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 +107 -0
- package/cadit.json +28 -0
- package/cli.ts +218 -0
- package/images/preview.png +0 -0
- package/package.json +42 -0
- package/scripts/generate-embedded-svgs.cjs +57 -0
- package/src/assembly.ts +105 -0
- package/src/crossSectionUtils.ts +42 -0
- package/src/disk.ts +136 -0
- package/src/embeddedSvgs.ts +131 -0
- package/src/images/makerchip-v1.svg +5 -0
- package/src/images/makerchip-v10.svg +5 -0
- package/src/images/makerchip-v11.svg +5 -0
- package/src/images/makerchip-v12.svg +5 -0
- package/src/images/makerchip-v13.svg +5 -0
- package/src/images/makerchip-v14.svg +3 -0
- package/src/images/makerchip-v15.svg +3 -0
- package/src/images/makerchip-v16.svg +3 -0
- package/src/images/makerchip-v17.svg +5 -0
- package/src/images/makerchip-v18.svg +5 -0
- package/src/images/makerchip-v19.svg +5 -0
- package/src/images/makerchip-v2.svg +5 -0
- package/src/images/makerchip-v20.svg +5 -0
- package/src/images/makerchip-v3.svg +5 -0
- package/src/images/makerchip-v4.svg +5 -0
- package/src/images/makerchip-v5.svg +5 -0
- package/src/images/makerchip-v6.svg +5 -0
- package/src/images/makerchip-v7.svg +5 -0
- package/src/images/makerchip-v8.svg +5 -0
- package/src/images/makerchip-v9.svg +5 -0
- package/src/main.ts +36 -0
- package/src/params.ts +110 -0
- package/src/threeMfExport.ts +123 -0
- package/src/utils.ts +32 -0
- package/tsconfig.json +19 -0
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
|
+

|
|
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`);
|
package/src/assembly.ts
ADDED
|
@@ -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
|
+
}
|