Browse Learn topics

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 values
  • api.param.angle(key, opts) — degrees
  • api.param.boolean(key, opts) — checkbox
  • api.param.select(key, { options: [...] }) — dropdown
  • api.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 header
  • api.info(text) — a non-editable themed note in the parameter area
  • api.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 circle
  • api.rad(deg) / api.deg(rad) — angle conversion
  • api.length(value) — parse a length string (respecting preferred units) into script units
  • api.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 chatter
  • api.addEntity(entity, opts) / api.addEntities(entities, opts) — import sketch-style entities
  • api.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.