Learn/Shape Generator/Authoring & distribution
Create & Share
At a glance
- One .jshape file = a live parametric shape: setup() declares params, generate() draws
- Full geometry API — lines, arcs, polylines, bolt circles, slots, arc-fitting, and the Boxes.py runtime
- Import and export from the dialog; recipients get live parameters, not frozen lines
- Hardened sandbox: no filesystem, no network, no require()
Every shape is a single portable .jshape file — a small JavaScript module that runs in JetCad3's
sandbox. Write one for a bracket you cut every month, hand the file to a teammate, and they get
live parameters, not frozen lines. This page is the full authoring reference.
Anatomy of a .jshape
A shape exports two functions: setup(api) declares what it is and what its parameters are;
generate(api, params) draws it. The filename is just storage — the dialog shows the name,
description, and category declared in setup().
module.exports = {
setup(api) {
api.name('Base Plate')
api.description('Rectangular plate with holes.')
api.category('Fabrication')
api.units('inch')
api.param.length('width', { label: 'Width', default: '6' })
api.param.length('height', { label: 'Height', default: '4' })
api.param.boolean('centerHole', { label: 'Center hole', default: true })
},
generate(api, p) {
api.rectangle(-p.width / 2, -p.height / 2, p.width, p.height, { layer: 'OUTER' })
if (p.centerHole) api.circle(0, 0, 0.5, { layer: 'HOLES' })
},
}Generated geometry is serialized to DXF and imported through the host workspace's normal DXF path, so the result stays fully editable — the same as importing any artwork.
Declaring parameters
Parameters declared in setup() become the dialog's input panel, with live preview on every change.
Parameter types
api.param.length(key, opts)— dimensional input; accepts numbers, fractions (1/2), and unit suffixes (mm,cm,m,in)api.param.number(key, opts)/api.param.integer(key, opts)— plain numeric valuesapi.param.angle(key, opts)— degreesapi.param.boolean(key, opts)— checkboxapi.param.select(key, { options: [...] })— dropdownapi.param.text(key, opts)— free text (e.g. barcode content)
Each takes { label, default } plus type-specific options.
Organizing the dialog
api.section(label)— groups the controls that follow under a headerapi.info(text)— a non-editable themed note in the parameter areaapi.subcategory(label)— places the shape in a collapsible subgroup below its category in the browser (the browser remembers collapsed state, and its width is resizable)
Units and expressions
api.units('inch') or api.units('mm') declares the units the script is authored in — defaults,
parameter values, and geometry coordinates. Users still type in their own preferred units: bare
values follow Settings → Preferred Units (6 = 6 in in inch mode, 6 mm in metric mode) and explicit
suffixes always win (6in and 152.4mm are identical everywhere). The host converts both ways, and
each workspace imports the generated geometry at correct real-world size.
Drawing geometry
generate(api, p) draws through host helpers. Coordinates are in the script's declared units;
{ layer } on any primitive routes it to OUTER, HOLES, MARKING, or 0.
Primitives
api.line(x1, y1, x2, y2, opts)api.arc(cx, cy, radius, startAngleDeg, endAngleDeg, opts)api.circle(cx, cy, radius, opts)api.polyline(points, { closed, layer })api.rectangle(x, y, width, height, opts)api.roundedRectangle(cx, cy, width, height, radius, opts)api.slot(cx1, cy1, cx2, cy2, radius, opts)api.boltCircle(cx, cy, count, radius, holeRadius, opts)
Helpers and math
api.polar(cx, cy, radius, angleDeg)— point on a circleapi.rad(deg)/api.deg(rad)— angle conversionapi.length(value)— parse a length string (respecting preferred units) into script unitsapi.arcFitPolyline(points, { closed, tolerance })— fit sampled points back into true arcs (bulge-encoded), keeping line-straight spans as lines; this is how generated curves survive as real G2/G3 arcs instead of polygon chatterapi.addEntity(entity, opts)/api.addEntities(entities, opts)— import sketch-style entitiesapi.error(message)— stop generation with a clear, user-facing message
The Boxes.py runtime
Ports of Boxes.py generators (and your own finger-jointed
designs) can use the host-injected BoxesRuntime(api, params) inside generate(). It provides the
Boxes.py finger-joint model: drawEdge(char, length), corner(degrees), turtle-style polyline
and moveTo, rectangularWall(width, height, edgeChars, ...), plus trapezoidWall,
polygonWall, disc, flex2D, hole-pattern fills, and parseSections('50:50:100') for
multi-compartment strings. Edge characters e (plain), f/F (finger joints), and h
(finger-hole edge) are modeled, and settings.play mirrors Boxes.py's burn correction so joints
fit off the machine.
Sharing and distribution
- Import / Export from the dialog — a custom library travels as easily as a DXF, except the recipient gets live parameters.
- User shapes live alongside bundled defaults. When the app ships newer built-ins, updated stock definitions are copied forward without touching your files.
- Everything in the shape browser — name, description, category, subcategory — comes from the code, so there are no parallel metadata files to keep in sync.
Editing workflow
Author in JetCad3's built-in editor or any text editor: open the shapes folder from the dialog,
edit, save, and hit Refresh to re-run setup() and rebuild the list. The preview re-generates
on every parameter change, so iteration is immediate. Insert into a Drafting sketch, Design, Laser,
or Plasma from the same dialog.
Sandbox and safety
Shapes run in a hardened sandbox: no filesystem access, no network, no Node require(). A shape
that detects bad input calls api.error() and the user sees a clear message instead of broken
geometry.