@invisra/printspec 0.1.3 → 0.1.5
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/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -2
- package/dist/bundle.browser.js +4 -2
- package/dist/bundle.core.d.ts +14 -1
- package/dist/bundle.core.js +7 -9
- package/dist/bundle.js +4 -2
- package/dist/generators/cadquery.browser.d.ts +2 -0
- package/dist/generators/cadquery.browser.js +5 -0
- package/dist/generators/cadquery.core.d.ts +4 -0
- package/dist/generators/cadquery.core.js +38 -0
- package/dist/generators/cadquery.js +2 -26
- package/dist/generators/openscad.browser.d.ts +2 -0
- package/dist/generators/openscad.browser.js +5 -0
- package/dist/generators/openscad.core.d.ts +4 -0
- package/dist/generators/openscad.core.js +39 -0
- package/dist/generators/openscad.js +2 -27
- package/package.json +3 -3
package/dist/browser.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { validatePrintSpec, validatePartFamilySpec, validateComposablePartSpec, validateProjectSpec, } from './validate.browser.js';
|
|
2
2
|
export { normalizePrintSpec } from './normalize.js';
|
|
3
3
|
export { extractBom, bomToMarkdown, bomToCsv, bomToSupplierOrderList, } from './bom.js';
|
|
4
|
-
export { generateOpenScad } from './generators/openscad.js';
|
|
5
|
-
export { generateCadQuery } from './generators/cadquery.js';
|
|
4
|
+
export { generateOpenScad } from './generators/openscad.browser.js';
|
|
5
|
+
export { generateCadQuery } from './generators/cadquery.browser.js';
|
|
6
6
|
export { createBundle } from './bundle.browser.js';
|
|
7
7
|
export { getPartFamilyFormMetadata, listPartFamilies } from './forms.browser.js';
|
package/dist/browser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { validatePrintSpec, validatePartFamilySpec, validateComposablePartSpec, validateProjectSpec, } from './validate.browser.js';
|
|
2
2
|
export { normalizePrintSpec } from './normalize.js';
|
|
3
3
|
export { extractBom, bomToMarkdown, bomToCsv, bomToSupplierOrderList, } from './bom.js';
|
|
4
|
-
export { generateOpenScad } from './generators/openscad.js';
|
|
5
|
-
export { generateCadQuery } from './generators/cadquery.js';
|
|
4
|
+
export { generateOpenScad } from './generators/openscad.browser.js';
|
|
5
|
+
export { generateCadQuery } from './generators/cadquery.browser.js';
|
|
6
6
|
export { createBundle } from './bundle.browser.js';
|
|
7
7
|
export { getPartFamilyFormMetadata, listPartFamilies } from './forms.browser.js';
|
package/dist/bundle.browser.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { validatePrintSpec } from './validate.browser.js';
|
|
2
|
-
import {
|
|
2
|
+
import { generateOpenScad } from './generators/openscad.browser.js';
|
|
3
|
+
import { generateCadQuery } from './generators/cadquery.browser.js';
|
|
4
|
+
import { createBundleWithDeps } from './bundle.core.js';
|
|
3
5
|
export function createBundle(input, options = {}) {
|
|
4
|
-
return
|
|
6
|
+
return createBundleWithDeps({ validatePrintSpec, generateOpenScad, generateCadQuery }, input, options);
|
|
5
7
|
}
|
package/dist/bundle.core.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PrintSpec } from './types.js';
|
|
1
2
|
import type { ValidationResult } from './types.js';
|
|
2
3
|
export type BundleFile = {
|
|
3
4
|
path: string;
|
|
@@ -24,4 +25,16 @@ export type BundleOptions = {
|
|
|
24
25
|
export type WriteBundleOptions = {
|
|
25
26
|
overwrite?: boolean;
|
|
26
27
|
};
|
|
27
|
-
|
|
28
|
+
type GenerateCad = (spec: PrintSpec) => {
|
|
29
|
+
supported: boolean;
|
|
30
|
+
code: string;
|
|
31
|
+
message?: string;
|
|
32
|
+
warnings?: string[];
|
|
33
|
+
};
|
|
34
|
+
export type BundleDeps = {
|
|
35
|
+
validatePrintSpec: (spec: unknown) => ValidationResult;
|
|
36
|
+
generateOpenScad: GenerateCad;
|
|
37
|
+
generateCadQuery: GenerateCad;
|
|
38
|
+
};
|
|
39
|
+
export declare function createBundleWithDeps(deps: BundleDeps, input: unknown, options?: BundleOptions): BundleResult;
|
|
40
|
+
export {};
|
package/dist/bundle.core.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { normalizePrintSpec } from './normalize.js';
|
|
2
|
-
import { generateOpenScad } from './generators/openscad.js';
|
|
3
|
-
import { generateCadQuery } from './generators/cadquery.js';
|
|
4
2
|
import { extractBom, bomToMarkdown, bomToCsv, bomToSupplierOrderList } from './bom.js';
|
|
5
3
|
const VERSION = '0.1.0';
|
|
6
4
|
const jsonMedia = 'application/json';
|
|
@@ -10,8 +8,8 @@ function warn(warnings, message, path) { warnings.push(path ? { path, message }
|
|
|
10
8
|
function safePartId(id) { return id.replace(/[^A-Za-z0-9._-]/g, '-'); }
|
|
11
9
|
function bomFiles(files, base, bom) { if (!bom.length)
|
|
12
10
|
return; add(files, `${base}bom.md`, bomToMarkdown(bom) + '\n', 'text/markdown', 'bom-markdown'); add(files, `${base}bom.csv`, bomToCsv(bom) + '\n', 'text/csv', 'bom-csv'); add(files, `${base}supplier-order-list.txt`, bomToSupplierOrderList(bom) + '\n', 'text/plain', 'supplier-order-list'); }
|
|
13
|
-
function cadForPart(spec, base, files, warnings, opts) { if (opts.includeOpenScad) {
|
|
14
|
-
const g = generateOpenScad(spec);
|
|
11
|
+
function cadForPart(deps, spec, base, files, warnings, opts) { if (opts.includeOpenScad) {
|
|
12
|
+
const g = deps.generateOpenScad(spec);
|
|
15
13
|
if (g.supported) {
|
|
16
14
|
add(files, `${base}cad/model.scad`, g.code, 'text/plain', 'openscad-source');
|
|
17
15
|
for (const m of g.warnings ?? [])
|
|
@@ -20,7 +18,7 @@ function cadForPart(spec, base, files, warnings, opts) { if (opts.includeOpenSca
|
|
|
20
18
|
else
|
|
21
19
|
warn(warnings, g.message ?? 'OpenSCAD generator unsupported', `${base}cad/model.scad`);
|
|
22
20
|
} if (opts.includeCadQuery) {
|
|
23
|
-
const g = generateCadQuery(spec);
|
|
21
|
+
const g = deps.generateCadQuery(spec);
|
|
24
22
|
if (g.supported) {
|
|
25
23
|
add(files, `${base}cad/model.py`, g.code, 'text/x-python', 'cadquery-source');
|
|
26
24
|
for (const m of g.warnings ?? [])
|
|
@@ -42,8 +40,8 @@ function partcad(spec, files) { const cq = files.filter(f => f.role === 'cadquer
|
|
|
42
40
|
} if (!cq.length)
|
|
43
41
|
lines.push(' []'); return lines.join('\n') + '\n'; }
|
|
44
42
|
function manifest(kind, files, warnings, spec) { const entries = files.map(f => ({ path: f.path, mediaType: f.mediaType, role: f.role })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0); entries.push({ path: 'bundle-manifest.json', mediaType: jsonMedia, role: 'bundle-manifest' }); entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0); return j({ bundleVersion: VERSION, createdBy: 'printspec', printspecVersion: spec.printspecVersion ?? VERSION, kind, files: entries, warnings }, true); }
|
|
45
|
-
export function
|
|
46
|
-
const r = validatePrintSpec(input);
|
|
43
|
+
export function createBundleWithDeps(deps, input, options = {}) {
|
|
44
|
+
const r = deps.validatePrintSpec(input);
|
|
47
45
|
if (!r.valid)
|
|
48
46
|
return { supported: false, files: [], warnings: [], message: `Validation failed: ${r.errors.slice(0, 3).join('; ')}` };
|
|
49
47
|
const spec = normalizePrintSpec(input);
|
|
@@ -53,7 +51,7 @@ export function createBundleWithValidator(validatePrintSpec, input, options = {}
|
|
|
53
51
|
const kind = spec.project ? 'project' : 'part';
|
|
54
52
|
add(files, 'printspec.json', j(spec, opts.prettyJson), jsonMedia, 'source-spec');
|
|
55
53
|
if (kind === 'part') {
|
|
56
|
-
cadForPart(spec, '', files, warnings, opts);
|
|
54
|
+
cadForPart(deps, spec, '', files, warnings, opts);
|
|
57
55
|
if (opts.includeBom)
|
|
58
56
|
bomFiles(files, 'bom/', extractBom(spec));
|
|
59
57
|
}
|
|
@@ -63,7 +61,7 @@ export function createBundleWithValidator(validatePrintSpec, input, options = {}
|
|
|
63
61
|
if (p.spec) {
|
|
64
62
|
const ps = normalizePrintSpec(p.spec);
|
|
65
63
|
add(files, `parts/${id}/printspec.json`, j(ps, opts.prettyJson), jsonMedia, 'part-source-spec');
|
|
66
|
-
cadForPart(ps, `parts/${id}/`, files, warnings, opts);
|
|
64
|
+
cadForPart(deps, ps, `parts/${id}/`, files, warnings, opts);
|
|
67
65
|
}
|
|
68
66
|
else if (p.specPath)
|
|
69
67
|
warn(warnings, `External specPath references are not bundled yet: ${p.specPath}`, `parts/${id}/printspec.json`);
|
package/dist/bundle.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { validatePrintSpec } from './validate.js';
|
|
4
|
-
import {
|
|
4
|
+
import { generateOpenScad } from './generators/openscad.js';
|
|
5
|
+
import { generateCadQuery } from './generators/cadquery.js';
|
|
6
|
+
import { createBundleWithDeps } from './bundle.core.js';
|
|
5
7
|
export function createBundle(input, options = {}) {
|
|
6
|
-
return
|
|
8
|
+
return createBundleWithDeps({ validatePrintSpec, generateOpenScad, generateCadQuery }, input, options);
|
|
7
9
|
}
|
|
8
10
|
function assertSafe(p) { if (!p || path.isAbsolute(p) || p.split(/[\/]+/).includes('..'))
|
|
9
11
|
throw new Error(`Unsafe bundle path: ${p}`); }
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { GeneratorResult, PrintSpec, ValidationResult } from '../types.js';
|
|
2
|
+
type ValidatePrintSpec = (spec: unknown) => ValidationResult;
|
|
3
|
+
export declare function generateCadQueryWithValidator(validatePrintSpec: ValidatePrintSpec, spec: PrintSpec): GeneratorResult;
|
|
4
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const header = '# Generated by printspec 0.1.0\n# Review generated CAD before manufacturing.\nimport cadquery as cq\n\n';
|
|
2
|
+
function invalid(validatePrintSpec, spec) {
|
|
3
|
+
const r = validatePrintSpec(spec);
|
|
4
|
+
return r.valid ? null : { supported: false, code: '', message: `Validation failed: ${r.errors.slice(0, 3).join('; ')}`, warnings: [] };
|
|
5
|
+
}
|
|
6
|
+
function warnings(a) {
|
|
7
|
+
const w = [];
|
|
8
|
+
if (a.chamfer != null)
|
|
9
|
+
w.push('chamfer requested but not implemented');
|
|
10
|
+
if (a.fillet != null)
|
|
11
|
+
w.push('fillet requested but not implemented');
|
|
12
|
+
return w;
|
|
13
|
+
}
|
|
14
|
+
function holeLines(hs = []) {
|
|
15
|
+
return hs.map((h) => `part = part.faces(">Z").workplane().pushPoints([(${h.x}, ${h.y})]).hole(${h.diameter})`).join('\n');
|
|
16
|
+
}
|
|
17
|
+
export function generateCadQueryWithValidator(validatePrintSpec, spec) {
|
|
18
|
+
const bad = invalid(validatePrintSpec, spec);
|
|
19
|
+
if (bad)
|
|
20
|
+
return bad;
|
|
21
|
+
const p = spec.part;
|
|
22
|
+
if (!p)
|
|
23
|
+
return { supported: false, code: '', message: 'Only part specs are supported.', warnings: [] };
|
|
24
|
+
const a = p.parameters;
|
|
25
|
+
if (p.type === 'rounded_rectangular_plate')
|
|
26
|
+
return { supported: true, warnings: [], code: `${header}length = ${a.length}\nwidth = ${a.width}\nheight = ${a.thickness}\ncorner_radius = ${a.cornerRadius}\n\npart = cq.Workplane("XY").rounded_rect(length, width, corner_radius).extrude(height)\n${holeLines(a.holes)}\n` };
|
|
27
|
+
if (p.type === 'spacer_block')
|
|
28
|
+
return { supported: true, warnings: [], code: `${header}length = ${a.length}\nwidth = ${a.width}\nheight = ${a.height}\n\npart = cq.Workplane("XY").box(length, width, height, centered=(True, True, False))\n${holeLines(a.holes)}\n` };
|
|
29
|
+
if (p.type === 'round_spacer') {
|
|
30
|
+
const w = warnings(a);
|
|
31
|
+
return { supported: true, warnings: w, code: `${header}outer_diameter = ${a.outerDiameter}\nheight = ${a.height}${a.innerDiameter != null ? `\ninner_diameter = ${a.innerDiameter}` : ''}\n\npart = cq.Workplane("XY").circle(outer_diameter / 2).extrude(height)${a.innerDiameter != null ? '\npart = part.faces(">Z").workplane().hole(inner_diameter)' : ''}\n` };
|
|
32
|
+
}
|
|
33
|
+
if (p.type === 'electronics_standoff') {
|
|
34
|
+
const hasBase = a.baseDiameter != null && a.baseHeight != null;
|
|
35
|
+
return { supported: true, warnings: [], code: `${header}outer_diameter = ${a.outerDiameter}\nheight = ${a.height}\nhole_diameter = ${a.holeDiameter}${hasBase ? `\nbase_diameter = ${a.baseDiameter}\nbase_height = ${a.baseHeight}` : ''}\n\n${hasBase ? `base = cq.Workplane("XY").circle(base_diameter / 2).extrude(base_height)\nstandoff = cq.Workplane("XY").workplane(offset=base_height).circle(outer_diameter / 2).extrude(height)\npart = base.union(standoff)` : `part = cq.Workplane("XY").circle(outer_diameter / 2).extrude(height)`}\npart = part.faces(">Z").workplane().hole(hole_diameter)\n` };
|
|
36
|
+
}
|
|
37
|
+
return { supported: false, code: '', message: `Unsupported part family: ${p.type}`, warnings: [] };
|
|
38
|
+
}
|
|
@@ -1,29 +1,5 @@
|
|
|
1
1
|
import { validatePrintSpec } from '../validate.js';
|
|
2
|
-
|
|
3
|
-
const header = '# Generated by printspec 0.1.0\n# Review generated CAD before manufacturing.\nimport cadquery as cq\n\n';
|
|
4
|
-
function warnings(a) { const w = []; if (a.chamfer != null)
|
|
5
|
-
w.push('chamfer requested but not implemented'); if (a.fillet != null)
|
|
6
|
-
w.push('fillet requested but not implemented'); return w; }
|
|
7
|
-
function holeLines(hs = []) { return hs.map(h => `part = part.faces(">Z").workplane().pushPoints([(${h.x}, ${h.y})]).hole(${h.diameter})`).join('\n'); }
|
|
2
|
+
import { generateCadQueryWithValidator } from './cadquery.core.js';
|
|
8
3
|
export function generateCadQuery(spec) {
|
|
9
|
-
|
|
10
|
-
if (bad)
|
|
11
|
-
return bad;
|
|
12
|
-
const p = spec.part;
|
|
13
|
-
if (!p)
|
|
14
|
-
return { supported: false, code: '', message: 'Only part specs are supported.', warnings: [] };
|
|
15
|
-
const a = p.parameters;
|
|
16
|
-
if (p.type === 'rounded_rectangular_plate')
|
|
17
|
-
return { supported: true, warnings: [], code: `${header}length = ${a.length}\nwidth = ${a.width}\nheight = ${a.thickness}\ncorner_radius = ${a.cornerRadius}\n\npart = cq.Workplane("XY").rounded_rect(length, width, corner_radius).extrude(height)\n${holeLines(a.holes)}\n` };
|
|
18
|
-
if (p.type === 'spacer_block')
|
|
19
|
-
return { supported: true, warnings: [], code: `${header}length = ${a.length}\nwidth = ${a.width}\nheight = ${a.height}\n\npart = cq.Workplane("XY").box(length, width, height, centered=(True, True, False))\n${holeLines(a.holes)}\n` };
|
|
20
|
-
if (p.type === 'round_spacer') {
|
|
21
|
-
const w = warnings(a);
|
|
22
|
-
return { supported: true, warnings: w, code: `${header}outer_diameter = ${a.outerDiameter}\nheight = ${a.height}${a.innerDiameter != null ? `\ninner_diameter = ${a.innerDiameter}` : ''}\n\npart = cq.Workplane("XY").circle(outer_diameter / 2).extrude(height)${a.innerDiameter != null ? '\npart = part.faces(">Z").workplane().hole(inner_diameter)' : ''}\n` };
|
|
23
|
-
}
|
|
24
|
-
if (p.type === 'electronics_standoff') {
|
|
25
|
-
const hasBase = a.baseDiameter != null && a.baseHeight != null;
|
|
26
|
-
return { supported: true, warnings: [], code: `${header}outer_diameter = ${a.outerDiameter}\nheight = ${a.height}\nhole_diameter = ${a.holeDiameter}${hasBase ? `\nbase_diameter = ${a.baseDiameter}\nbase_height = ${a.baseHeight}` : ''}\n\n${hasBase ? `base = cq.Workplane("XY").circle(base_diameter / 2).extrude(base_height)\nstandoff = cq.Workplane("XY").workplane(offset=base_height).circle(outer_diameter / 2).extrude(height)\npart = base.union(standoff)` : `part = cq.Workplane("XY").circle(outer_diameter / 2).extrude(height)`}\npart = part.faces(">Z").workplane().hole(hole_diameter)\n` };
|
|
27
|
-
}
|
|
28
|
-
return { supported: false, code: '', message: `Unsupported part family: ${p.type}`, warnings: [] };
|
|
4
|
+
return generateCadQueryWithValidator(validatePrintSpec, spec);
|
|
29
5
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { GeneratorResult, PrintSpec, ValidationResult } from '../types.js';
|
|
2
|
+
type ValidatePrintSpec = (spec: unknown) => ValidationResult;
|
|
3
|
+
export declare function generateOpenScadWithValidator(validatePrintSpec: ValidatePrintSpec, spec: PrintSpec): GeneratorResult;
|
|
4
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const header = '// Generated by printspec 0.1.0\n// Review generated CAD before manufacturing.\n';
|
|
2
|
+
function invalid(validatePrintSpec, spec) {
|
|
3
|
+
const r = validatePrintSpec(spec);
|
|
4
|
+
return r.valid ? null : { supported: false, code: '', message: `Validation failed: ${r.errors.slice(0, 3).join('; ')}`, warnings: [] };
|
|
5
|
+
}
|
|
6
|
+
function warnings(a) {
|
|
7
|
+
const w = [];
|
|
8
|
+
if (a.chamfer != null)
|
|
9
|
+
w.push('chamfer requested but not implemented');
|
|
10
|
+
if (a.fillet != null)
|
|
11
|
+
w.push('fillet requested but not implemented');
|
|
12
|
+
return w;
|
|
13
|
+
}
|
|
14
|
+
function holes(hs = []) {
|
|
15
|
+
return hs.map((h) => ` translate([${h.x}, ${h.y}, -0.1]) cylinder(h = height + 0.2, d = ${h.diameter}, $fn = 48);`).join('\n');
|
|
16
|
+
}
|
|
17
|
+
export function generateOpenScadWithValidator(validatePrintSpec, spec) {
|
|
18
|
+
const bad = invalid(validatePrintSpec, spec);
|
|
19
|
+
if (bad)
|
|
20
|
+
return bad;
|
|
21
|
+
const p = spec.part;
|
|
22
|
+
if (!p)
|
|
23
|
+
return { supported: false, code: '', message: 'Only part specs are supported.', warnings: [] };
|
|
24
|
+
const a = p.parameters;
|
|
25
|
+
if (p.type === 'rounded_rectangular_plate')
|
|
26
|
+
return { supported: true, warnings: [], code: `${header}length = ${a.length};\nwidth = ${a.width};\nheight = ${a.thickness};\ncorner_radius = ${a.cornerRadius};\n\nmodule rounded_plate() {\n hull() {\n for (x = [-length/2 + corner_radius, length/2 - corner_radius])\n for (y = [-width/2 + corner_radius, width/2 - corner_radius])\n translate([x, y, 0]) cylinder(h = height, r = corner_radius, $fn = 32);\n }\n}\n\ndifference() {\n rounded_plate();\n${holes(a.holes)}\n}\n` };
|
|
27
|
+
if (p.type === 'spacer_block')
|
|
28
|
+
return { supported: true, warnings: [], code: `${header}length = ${a.length};\nwidth = ${a.width};\nheight = ${a.height};\n\ndifference() {\n translate([-length/2, -width/2, 0]) cube([length, width, height]);\n${holes(a.holes)}\n}\n` };
|
|
29
|
+
if (p.type === 'round_spacer') {
|
|
30
|
+
const w = warnings(a);
|
|
31
|
+
const inner = a.innerDiameter != null ? `\n translate([0, 0, -0.1]) cylinder(h = height + 0.2, d = inner_diameter, $fn = 64);` : '';
|
|
32
|
+
return { supported: true, warnings: w, code: `${header}outer_diameter = ${a.outerDiameter};\nheight = ${a.height};${a.innerDiameter != null ? `\ninner_diameter = ${a.innerDiameter};` : ''}\n\ndifference() {\n cylinder(h = height, d = outer_diameter, $fn = 64);${inner}\n}\n` };
|
|
33
|
+
}
|
|
34
|
+
if (p.type === 'electronics_standoff') {
|
|
35
|
+
const hasBase = a.baseDiameter != null && a.baseHeight != null;
|
|
36
|
+
return { supported: true, warnings: [], code: `${header}outer_diameter = ${a.outerDiameter};\nheight = ${a.height};\nhole_diameter = ${a.holeDiameter};${hasBase ? `\nbase_diameter = ${a.baseDiameter};\nbase_height = ${a.baseHeight};` : ''}\n\ndifference() {\n ${hasBase ? `union() {\n cylinder(h = base_height, d = base_diameter, $fn = 64);\n translate([0, 0, base_height]) cylinder(h = height, d = outer_diameter, $fn = 64);\n }` : `cylinder(h = height, d = outer_diameter, $fn = 64);`}\n translate([0, 0, -0.1]) cylinder(h = ${hasBase ? 'base_height + height' : 'height'} + 0.2, d = hole_diameter, $fn = 64);\n}\n` };
|
|
37
|
+
}
|
|
38
|
+
return { supported: false, code: '', message: `Unsupported part family: ${p.type}`, warnings: [] };
|
|
39
|
+
}
|
|
@@ -1,30 +1,5 @@
|
|
|
1
1
|
import { validatePrintSpec } from '../validate.js';
|
|
2
|
-
|
|
3
|
-
const header = '// Generated by printspec 0.1.0\n// Review generated CAD before manufacturing.\n';
|
|
4
|
-
function warnings(a) { const w = []; if (a.chamfer != null)
|
|
5
|
-
w.push('chamfer requested but not implemented'); if (a.fillet != null)
|
|
6
|
-
w.push('fillet requested but not implemented'); return w; }
|
|
7
|
-
function holes(hs = []) { return hs.map(h => ` translate([${h.x}, ${h.y}, -0.1]) cylinder(h = height + 0.2, d = ${h.diameter}, $fn = 48);`).join('\n'); }
|
|
2
|
+
import { generateOpenScadWithValidator } from './openscad.core.js';
|
|
8
3
|
export function generateOpenScad(spec) {
|
|
9
|
-
|
|
10
|
-
if (bad)
|
|
11
|
-
return bad;
|
|
12
|
-
const p = spec.part;
|
|
13
|
-
if (!p)
|
|
14
|
-
return { supported: false, code: '', message: 'Only part specs are supported.', warnings: [] };
|
|
15
|
-
const a = p.parameters;
|
|
16
|
-
if (p.type === 'rounded_rectangular_plate')
|
|
17
|
-
return { supported: true, warnings: [], code: `${header}length = ${a.length};\nwidth = ${a.width};\nheight = ${a.thickness};\ncorner_radius = ${a.cornerRadius};\n\nmodule rounded_plate() {\n hull() {\n for (x = [-length/2 + corner_radius, length/2 - corner_radius])\n for (y = [-width/2 + corner_radius, width/2 - corner_radius])\n translate([x, y, 0]) cylinder(h = height, r = corner_radius, $fn = 32);\n }\n}\n\ndifference() {\n rounded_plate();\n${holes(a.holes)}\n}\n` };
|
|
18
|
-
if (p.type === 'spacer_block')
|
|
19
|
-
return { supported: true, warnings: [], code: `${header}length = ${a.length};\nwidth = ${a.width};\nheight = ${a.height};\n\ndifference() {\n translate([-length/2, -width/2, 0]) cube([length, width, height]);\n${holes(a.holes)}\n}\n` };
|
|
20
|
-
if (p.type === 'round_spacer') {
|
|
21
|
-
const w = warnings(a);
|
|
22
|
-
const inner = a.innerDiameter != null ? `\n translate([0, 0, -0.1]) cylinder(h = height + 0.2, d = inner_diameter, $fn = 64);` : '';
|
|
23
|
-
return { supported: true, warnings: w, code: `${header}outer_diameter = ${a.outerDiameter};\nheight = ${a.height};${a.innerDiameter != null ? `\ninner_diameter = ${a.innerDiameter};` : ''}\n\ndifference() {\n cylinder(h = height, d = outer_diameter, $fn = 64);${inner}\n}\n` };
|
|
24
|
-
}
|
|
25
|
-
if (p.type === 'electronics_standoff') {
|
|
26
|
-
const hasBase = a.baseDiameter != null && a.baseHeight != null;
|
|
27
|
-
return { supported: true, warnings: [], code: `${header}outer_diameter = ${a.outerDiameter};\nheight = ${a.height};\nhole_diameter = ${a.holeDiameter};${hasBase ? `\nbase_diameter = ${a.baseDiameter};\nbase_height = ${a.baseHeight};` : ''}\n\ndifference() {\n ${hasBase ? `union() {\n cylinder(h = base_height, d = base_diameter, $fn = 64);\n translate([0, 0, base_height]) cylinder(h = height, d = outer_diameter, $fn = 64);\n }` : `cylinder(h = height, d = outer_diameter, $fn = 64);`}\n translate([0, 0, -0.1]) cylinder(h = ${hasBase ? 'base_height + height' : 'height'} + 0.2, d = hole_diameter, $fn = 64);\n}\n` };
|
|
28
|
-
}
|
|
29
|
-
return { supported: false, code: '', message: `Unsupported part family: ${p.type}`, warnings: [] };
|
|
4
|
+
return generateOpenScadWithValidator(validatePrintSpec, spec);
|
|
30
5
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@invisra/printspec",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "JSON schemas, validators, BOM helpers, CLI commands, and starter generators for practical parametric 3D-printable parts.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Invisra",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"ajv-formats": "^3.0.1"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@types/node": "^
|
|
62
|
-
"typescript": "^5.
|
|
61
|
+
"@types/node": "^20.0.0",
|
|
62
|
+
"typescript": "^5.0.0"
|
|
63
63
|
},
|
|
64
64
|
"bin": {
|
|
65
65
|
"printspec": "dist/cli.js"
|