Language Reference

Full details on every operation.

Overview

PolyScript is a pipe-based language for parametric CAD modeling. Shapes are created with primitives and transformed through a chain of pipe operations.

box 80 60 10 | fillet 2 | diff cylinder 10 10

When you write multiple shapes on separate lines, they are automatically combined (unioned) into a single shape.

box 10 10 10
sphere 8
# → the two shapes are automatically unioned

To combine shapes explicitly, use union [...].

Primitives

3D

box width height depth
cylinder radius height
sphere radius
cone r1 r2 height
torus r1 r2
wedge dx dy dz ltx

cone r1 r2 h creates a frustum. r1 is the bottom radius, r2 is the top radius, h is the height. Setting r2 to 0 produces a full cone.

cone 10 5 20       # frustum (bottom R10, top R5, height 20)
cone 10 0 20       # full cone

torus r1 r2 creates a donut shape. r1 is the major radius (center to tube center), r2 is the minor (tube) radius.

torus 20 5          # major radius 20, tube radius 5

wedge dx dy dz ltx creates a wedge (tapered box). The base is dx x dz, the top narrows to ltx x dz, and dy is the height.

wedge 20 10 15 5    # base 20x15, top 5x15, height 10

Position Convention

3D primitives and extrude follow different placement rules along the Z axis. Mixing them without care is a classic source of misalignment.

Operation Z-axis placement Example (height 10)
box, cylinder, sphere, cone, torus Centered at origin z = -5..+5
extrude h Bottom-aligned (z=0 upward) z = 0..10

In other words, the bottom face of box 10 10 10 sits at z=-5, while rect 10 10 | extrude 10 starts at z=0. When you put both in the same scene, their bottoms don’t line up.

Three ways to fix this:

  1. | floor – Snap the bottom to z=0 (the easiest)
  2. center:(true,true,false) – Disable centering on the Z axis only
  3. | translate 0 0 h/2 – Manually shift upward
# WRONG: want to stack a lid (extrude) on a body (box), but bottoms mismatch
$body = box 80 60 100             # z = -50..+50
$lid  = rect 80 60 | extrude 20  # z = 0..20
# $lid floats near the middle of the body

# RIGHT: use floor to align the body, then stack
$body = box 80 60 100 | floor               # z = 0..100
$lid  = rect 80 60 | extrude 20            # z = 0..20
$body | union ($lid | translate 0 0 100)    # lid on top

2D

rect width height
circle radius
ellipse rx ry
polygon n r
polyline points
text content size
sketch [segments]

2D primitives produce wire profiles. Use | extrude, | cut, | revolve, or | loft to create solids.

polygon n r creates a regular n-gon inscribed in a circle of radius r. polyline points creates a closed wire from a vertex list.

polygon 6 10            # regular hexagon (inscribed circle radius 10)
polyline [(0,0), (10,0), (5,10)]   # closed wire from vertices

sketch – Composite wire

Combine lines, arcs, and Bezier curves to create a closed 2D profile of any shape. Useful for shapes that rect or circle cannot express.

sketch [
  (5, 0),                              # start point (tuple)
  arc (5, 0) (0, -5) (-5, 0),          # arc: start → through → end
  (0, 7),                              # line (tuple only)
  (5, 0)                               # line (returns to start, auto-close)
]

Segment types:

Syntax Meaning
(x, y) Line from the previous point
arc (sx, sy) (mx, my) (ex, ey) 3-point arc (start, through, end)
arc (sx, sy) (ex, ey) center:(cx, cy) Center arc (start, end, center)
arc (sx, sy) (ex, ey) radius:radius Radius arc (start, end, radius; center is computed automatically)
bezier [(x1,y1), ...] Bezier curve (control point list; start point is implicit from the preceding segment, last element is the end point)

The first element must be a tuple (the start point). If the last segment returns to the start point, the wire closes automatically. When the start point of an arc does not match the end point of the preceding segment, a straight line is automatically inserted to bridge the gap. The result can be turned into a solid with extrude or cut.

Which arc should I use?

Goal Recommended
Specify three points (start, through, end) arc start through end
Draw a precise arc given center and end point arc start end center:(cx,cy)
Round a corner with just radius and end point arc start end radius:radius
# Extrude a teardrop shape
sketch [
  (5, 0),
  arc (5, 0) (0, -5) (-5, 0),
  (0, 7),
  (5, 0)
] | extrude 10

# D-shaped hole
$r = 3
sketch [
  (-$r, $r),
  (-$r, -$r),
  arc (-$r, -$r) (0, -$r - 1) ($r, -$r),
  ($r, $r),
  (-$r, $r)
]

# True quarter arc (center specified)
sketch [
  ($r, 0),
  arc ($r, 0) (0, $r) center:(0, 0),
  (0, 0),
  ($r, 0)
]

# Rounded rectangle (radius form is the most concise)
$w = 20
$h = 10
$cr = 2
sketch [
  ($w/2 - $cr, -$h/2),
  arc ($w/2 - $cr, -$h/2) ($w/2, -$h/2 + $cr) radius:$cr,
  ($w/2, $h/2 - $cr),
  arc ($w/2, $h/2 - $cr) ($w/2 - $cr, $h/2) radius:$cr,
  (-$w/2 + $cr, $h/2),
  arc (-$w/2 + $cr, $h/2) (-$w/2, $h/2 - $cr) radius:$cr,
  (-$w/2, -$h/2 + $cr),
  arc (-$w/2, -$h/2 + $cr) (-$w/2 + $cr, -$h/2) radius:$cr
] | extrude 5

Paths

line start end
arc start through end
arc start end center:(cx,cy)
arc start end radius:radius
bezier points
helix pitch height radius
spline points

spline creates a smooth B-spline curve that passes through the control points. It can be used as a path for sweep.

spline [(0,0,0), (10,5,5), (20,0,10)] | sweep (circle 3)

Note: The list convention for bezier/spline differs by context. As standalone commands (bezier [...] | sweep, etc.), the list includes the start point as its first element. Inside sketch/wire segments, however, the start point is implicitly inherited from the preceding segment’s endpoint (the current point), so the list contains only the control/through-points and the end point. This follows the industry-standard convention used by CadQuery and SVG paths.

Pipe Operations

Operations are chained with |:

box 50 50 10 | fillet 2 | diff cylinder 5 10

Modifiers

| fillet expr              # fillet all edges
| chamfer expr             # chamfer all edges
| shell expr               # hollow out; select face first to remove it
| offset d                 # offset wire or face outline (positive = outward, negative = inward)
box 100 60 40
 | edges =Z | fillet 3
 | faces >Z | shell 2

fillet also works on 2D wires, letting you create rounded 2D profiles:

rect 40 20 | fillet 3 | extrude 10     # extrude a rounded rectangle

offset works in two contexts:

  • Face selection context: Extracts the outline wire of the selected face, creates a workplane, and offsets the outline. Transitions to 2D context.
  • 2D context: Offsets the existing wire. Stays in 2D context.
# Face selection → offset → cut
box 80 60 10
 | faces >Z
 | offset -10
 | cut 3

# 2D → offset
rect 80 60 | offset -10 | extrude 5

Color

Apply color to a shape. Colors are reflected per-part on export (glTF/STEP).

| color "red"                    # named color
| color "#FF0000"                # hex color (string literal)
| color "#F00"                   # hex shorthand
| color 0.8 0.2 0.1             # RGB float (0..1)
| color 255 128 0               # RGB int (0..255, auto-normalized)
| color "red" alpha:0.5         # with transparency

There are three ways to specify colors:

  • Named colors: "red", "blue", "steel", etc.
  • Hex colors: "#FF0000" or shorthand "#F00" (must be written as a string literal since # is the comment character)
  • RGB values: Three numbers. If all are 1 or below, they are treated as 0..1 range; if any exceeds 1, they are auto-normalized as 0..255 range

The alpha: keyword argument sets transparency (0..1, default 1.0).

You can also assign different colors to individual parts:

union [
  box 10 10 10 | color "red"
  cylinder 15 3 | color "blue"
]

Named color palette

Three tiers of color names are available:

Tier Color names
Basic (16 colors) red, green, blue, yellow, cyan, magenta, orange, purple, white, black, gray/grey, brown, pink, lime, navy, teal
CAD material colors silver, gold, steel, copper, brass, aluminum, darkgray/darkgrey, lightgray/lightgrey
CSS Named Colors steelblue, coral, darkslategray, and all other CSS-compliant color names

An unknown color name will produce an error.

Boolean

| diff shape               # subtract shape
| union shape              # add shape
| inter shape              # intersect with shape

Shapes can be placed with at::

box 50 50 10
 | diff cylinder 3 10 at:15 15

Place

Place a 2D shape stored in a variable into the pipeline. Like diff/union/inter, it accepts variable references or inline 2D primitives as arguments.

| place $shape                # place a 2D shape from a variable
| place (circle 3)            # inline 2D primitive

After selecting a face, you can place a predefined profile for cutting or extruding. Useful when reusing the same shape multiple times.

$s = sketch [(5,0), arc (5,0) (0,-5) (-5,0), (0,7), (5,0)]
box 10 10 10 | faces >Z | place $s | cut
box 10 10 10 | faces >Z | place $s | extrude 5

$profile = rect 5 5
box 10 10 10 | faces >Z | place $profile | cut

box 10 10 10 | faces >Z | place (circle 3) | cut

Extrude / Revolve / Sweep / Loft

| extrude height draft:angle
| revolve axis [deg]         # axis: X/Y/Z, deg defaults to 360
| sweep profile              # pipeline subject is the path (spine); argument is the profile
| loft [sections] height
| loft [sections] [offsets]
| loft [sections] height ruled:true
rect 60 40 | extrude 15
rect 10 30 at:(15, 0) | revolve Y
circle 5 | loft [rect 8 8] 10
circle 5 | loft [rect 8 8, circle 3] [5, 15]

draft: specifies a draft angle (taper) in degrees. A positive value widens outward in the extrusion direction.

ruled:true interpolates between sections with straight lines (the default is a smooth surface).

revolve – Solid of revolution

Revolves a 2D profile around the specified axis to create a solid.

| revolve axis         # full 360° revolution (axis: X, Y, or Z)
| revolve axis deg     # revolve by specified angle

The profile must be placed entirely on one side of the rotation axis. A profile that straddles the axis (e.g., circle 20 centered at the origin) will produce a degenerate shape error. Use at:(x, y) to offset the profile from the origin, or draw a wire on one side of the axis with sketch.

# Ring (torus-like)
rect 10 30 at:(15, 0) | revolve Y

# Vase / bowl (half revolution)
sketch [(0, 0), (15, 0), (15, 3), (3, 20), (0, 20)]
  | revolve Y 180

# Revolution around the X axis
circle 5 at:(0, 20) | revolve X

sweep – Path sweep

Sweeps the argument profile (cross-section) along the pipeline path (spine) to create a 3D shape. Both the path and the profile can be any wire.

Syntax: path | sweep profile

  • Open wires: line, arc, bezier, spline, helix
  • Closed wires: circle, rect, ellipse, polygon, polyline (closed form), sketch [...]

The combination of path and profile determines the resulting shape:

path profile Result
open closed Tubular solid (primary use case)
closed closed Closed-loop tube (torus-like)
open open Strip surface (thread grooves, etc.)
# Tubular solid: sweep a circle along a curved path
arc (0, -25) (25, 0) center:(0, 0) | sweep (circle 5)

# Torus: major radius 50, minor radius 10
circle 50 | sweep (circle 10)

# Pipe along a spline
spline [(0,0,0), (10,5,5), (20,0,10)] | sweep (circle 3)

Note: sketch [...] always auto-closes (it produces a closed wire), so it cannot be used as an open path for sweep. When you need an open multi-segment path, use the wire [...] literal or path primitives such as line/arc/spline.

path – Open wire literal

The open counterpart to sketch [...]. Joins multiple segments into an open wire (no auto-close). Perfect for building complex sweep paths (spines) in a single expression.

wire [
  segment1,
  segment2,
  ...
]

Segments use the same syntax as sketch (except tarc). Both 2D and 3D coordinates are supported.

Syntax Meaning
(x, y) or (x, y, z) Line from the previous point
line (start) (end) Line segment
arc (start) (through) (end) 3-point arc
arc (start) (end) center:(cx,cy) Center arc
arc (start) (end) radius:radius Radius arc
bezier [...] Bezier curve (start point is implicit from the preceding segment; list contains control points and the end point)
spline [...] B-spline curve (start point is implicit from the preceding segment; list contains through-points and the end point)

How path differs from sketch:

sketch path
Auto-close yes no
Output Closed wire (face) Open wire
Coordinates 2D 2D/3D
Primary use extrude/revolve/cut sweep path
# Basic: line + arc combined into a path
wire [
  (0, 0),
  (10, 0),
  arc (10, 0) (15, 5) radius:5,
  (15, 20)
]

# Combined with sweep: L-shaped pipe (wire is the path, circle is the profile)
wire [
  (0, 0),
  (10, 0),
  arc (10, 0) (15, 5) radius:5
] | sweep (circle 5)

# 3D path -- just use 3D tuples
wire [(0, 0, 0), (10, 0, 5), (20, 10, 10)]

# Workplane + path: draw a path on the XZ plane, then sweep with a circle profile
workplane XZ | wire [(0,0),(10,0),(10,10),(0,10),(0,0)] | sweep (circle 2)

Like sketch, wire can receive a workplane via pipe. This lets you draw paths on any plane, not just the default XY.

Sweep usage guide

Purpose Recommended
Path (spine, single segment) line/arc/helix/spline (path primitives)
Path (spine, multiple segments) wire [...] literal
Profile (closed cross-section) circle/rect/… or sketch [...]
Profile (open cross-section) polyline (non-closing)

Cut / Hole

| cut depth                # cut through or to depth
| hole radius depth:d      # drill hole (radius, not diameter)

hole can be used from both FaceSelection and PointSelection.

  • From FaceSelection: Drills a hole at the center of the selected face. faces >Z | hole 5 is equivalent to faces >Z | circle 5 | cut. When multiple faces are selected, a hole is drilled at the center of each.
  • From PointSelection: Drills a hole at each point position (the traditional behavior).

Omitting depth: creates a through-hole.

Use at: to specify the hole position. Use origin: to control the coordinate reference (see “Coordinate Reference” below).

# From face selection: hole at face center
box 80 60 10 | faces >Z | hole 5

# From point selection: hole at each point
box 80 60 10 | faces >Z | points (polar 4 15) | hole 5

# Hole at workplane coordinate (10,20)
box 80 60 10 | faces >Z | hole 5 at: 10 20

# Hole at world origin, projected onto face
box 80 60 10 | faces >Z | hole 5 at: 0 0 origin:"world"

# Hole at world coordinate (10,20,0) — 3 components = world
box 80 60 10 | faces >Z | hole 5 at: 10 20 0
rect 60 40 | extrude 15
 | faces top
 | rect 40 20 | cut 10

Face / Edge / Vertex Selection

| faces selector           # select faces
| edges selector           # select edges
| verts selector           # select vertices

Selectors use short symbols or name aliases:

Symbol Meaning Name alias
>Z maximum (top) top
<Z minimum (bottom) bottom
>X maximum (right) right
<X minimum (left) left
>Y maximum (back) back
<Y minimum (front) front
=Z parallel to Z
=X parallel to X
=Y parallel to Y
+Z perpendicular to Z
+X perpendicular to X
+Y perpendicular to Y

Compound selectors

Multiple selectors separated by spaces act as AND (intersection):

| edges >Z >X          # edges at both Z-max and X-max

Wrap selectors in list syntax for OR (union):

| edges [>Z, <Z]       # edges at Z-max or Z-min

Tagging with as

You can name faces or edges with as for later reference:

| faces <X as $left
| edges =Z as $top_edges

Face selection creates an implicit workplane, so you can pipe 2D primitives directly:

box 50 50 10
 | faces top
 | circle 5 | cut
 | edges <Z | fillet 1

In a 2D context, verts returns the vertices of the current shape as a Vertex selection. Vertex selection supports both 2D and 3D primitives – each primitive is placed at every vertex position:

box 80 60 10
 | faces top
 | rect 70 50 | verts | circle 1 | cut

3D primitives work the same way. Each vertex becomes a placement point:

rect 100 100 | verts | box 1 1 1       # place a box at each of the 4 vertices
rect 100 100 | verts | sphere 2         # place a sphere at each of the 4 vertices

Vertex selection supports translate to offset all vertex positions while staying in the selection context. Points selection (points) also supports this:

rect 80 60 | verts | translate 10 10 10 | cone 2 0 6
box 80 60 10 | faces >Z | points (polar 4 15) | translate 5 5 0 | hole 3

Workplane / Points

Face selection implicitly creates a workplane for 2D operations. Use explicit workplane only when you need a specific orientation:

| workplane XZ              # explicit workplane with axis
| points (polar 6 20)       # arrange points in circle
| points (grid nx ny 20)    # arrange points in grid (3rd arg = pitch)

As a source command, workplane sets the initial plane for 2D primitives, sketch, and wire:

workplane XZ | circle 10 | extrude 10       # circle on the XZ plane
workplane XZ | sketch [...] | extrude 5     # sketch on the XZ plane
workplane XZ | wire [...] | sweep (circle 2) # path on the XZ plane
# hole from face selection (hole at face center)
cylinder 30 5 | faces top | hole 3

# hole from point selection (hole at each point)
cylinder 30 5
 | faces top
 | points (polar 4 10)
 | hole 3

polar / grid shorthand

In a FaceSelection or PointSelection context, you can pipe polar / grid directly, omitting points.

# shorthand (usable after face selection)
cylinder 30 5 | faces top | polar 4 10 | hole 3
box 80 60 10 | faces top | grid 3 2 15 | circle 2 | cut

The above is equivalent to:

cylinder 30 5 | faces top | points (polar 4 10) | hole 3
box 80 60 10 | faces top | points (grid 3 2 15) | circle 2 | cut

In a 3D context, polar / grid behave as array duplication as before. Note that the meaning changes depending on the context.

Transform

| translate x y z
| rotate rx ry rz
| scale sx sy sz             # non-uniform scale (per axis)
| scale s                    # uniform scale (all axes)
| mirror "X"/"Y"/"Z"
| floor                      # align bottom face to z=0
| move dx dy           # 2D offset on workplane
| moveto x y           # 2D absolute position

mirror reflects across the plane perpendicular to the specified axis. "X" mirrors across the YZ plane, "Y" across the XZ plane, "Z" across the XY plane.

box 30 20 10 | mirror "X"    # mirror across YZ plane

floor – Align bottom to z=0

Translates any 3D shape so that its bounding box minimum Z (zmin) sits at z=0. Takes no arguments.

| floor

Internally this is equivalent to translate 0 0 -bbox.zmin. When you want to bottom-align a centered primitive, | floor is a one-pipe alternative to center:(true,true,false).

# Bottom-align a centered box
box 10 10 10 | floor           # z=-5..+5 → z=0..10

# Works on spheres too
sphere 15 | floor              # z=-15..+15 → z=0..30

# Works on compound shapes
union [box 20 20 10, cylinder 5 20] | floor

Note: floor is a pipe operation (it moves a shape). It is not the same as the math function floor(x) (which truncates a number toward negative infinity).

move offsets the drawing position relative to the current point on the workplane. moveto moves to an absolute position. Use origin: to change the coordinate reference (see “Coordinate Reference” below).

box 80 60 10
 | faces top
 | move 10 10 | circle 5 | cut      # hole offset (10,10) from face center
 | moveto 30 20 | rect 5 5 | cut    # pocket at (30,20) from workplane origin
# World XY coordinates projected onto face
| moveto 10 20 origin:"world"

# Arbitrary world point as new origin, then relative move
| moveto 5 5 origin:(10,20,0)

# World XY delta
| move 10 0 origin:"world"

origin: keyword argument

translate, rotate, and scale accept the origin: keyword argument to specify the reference point.

Value Meaning Default
"world" Use world origin (0,0,0) yes
"local" Use object bounding box center
(x, y, z) Use an arbitrary point
# Rotate around world origin (default)
box 10 10 10 | translate 20 0 0 | rotate 0 0 45

# Rotate around object center
box 10 10 10 | translate 20 0 0 | rotate 0 0 45 origin:"local"

# Rotate around an arbitrary point
box 10 10 10 | rotate 0 0 45 origin:(10, 20, 0)

# Uniform scale (2x from origin)
box 10 10 10 | scale 2

# Non-uniform scale
box 10 10 10 | scale 2 1 0.5

# Scale from object center
box 10 10 10 | translate 20 0 0 | scale 3 origin:"local"

move, moveto, hole, and primitive at: also accept origin:. See the next section for details.

Coordinate Reference (at: and origin:)

When specifying a position with at:, the coordinate interpretation depends on the number of components by default. Use origin: to override explicitly.

Default reference (when origin: is omitted):

Components Reference Example
2-component at: x y Workplane coordinates (WP origin) circle 5 at: 10 20
3-component at: x y z World coordinates (projected onto face normal) circle 5 at: 10 20 0

Explicit override:

origin: value Meaning
"world" Use world origin (0,0,0). Even 2-component coordinates are interpreted as world coordinates
"local" Use workplane origin (same as the 2-component default)
(ox, oy, oz) Use an arbitrary world point as the new origin; at: is interpreted as relative WP coordinates from that point

World coordinates (3-component, or 2-component with origin:"world") are projected onto the current workplane face along its normal direction.

origin: can be used with the following operations:

  • Primitives with at:: circle, rect, ellipse, polygon, box, cylinder, sphere, cone, torus, wedge
  • Hole drilling: hole (combined with at:)
  • Cursor movement: moveto, move
  • Transform: translate, rotate, scale
# 2-component at: uses workplane origin (default)
faces >Z | circle 5 at: 10 20                   # placed at WP (10,20)

# 3-component at: uses world coordinates (default)
faces >Z | circle 5 at: 10 20 0                 # world (10,20,0) projected

# origin:"world" makes 2-component world-relative too
faces >Z | circle 5 at: 10 20 origin:"world"    # world XY (10,20) projected

# origin:(ox,oy,oz) sets an arbitrary world point as origin
faces >Z | circle 5 at: 5 5 origin:(10,20,0)    # relative WP (5,5) from (10,20,0)

# Same applies to hole
faces >Z | hole 5 at: 0 0 origin:"world"        # hole at world origin

Variables

The $ prefix on variable names is optional. Both $w = 80 and w = 80 work. Reserved words (box, cylinder, if, for, etc.) cannot be used as variable names.

w = 80
h = 60
box w h 10

# $-prefixed form also works (backward compatible)
$w = 80
box $w $h 10

@param Annotation

Adding @param to a variable turns it into a parameter that can be controlled via a GUI customizer as a slider or dropdown. Write it on the line immediately before the variable declaration.

@param 10..200 step:5 desc:"Box width"
$width = 80

box $width 60 10

This alone displays a slider in the GUI ranging from 10 to 200 in increments of 5.

Basic syntax

@param options...
$variable_name = default_value

Range shorthand

A concise way to write common min/max/step patterns:

Notation Meaning
@param 1..100 min:1 max:100
@param 1..100..0.5 min:1 max:100 step:0.5
@param -10..10..1 min:-10 max:10 step:1

You can also append options after the shorthand:

@param 1..100 desc:"Height"

Option reference

Key Type Description
min number Minimum value (slider lower bound)
max number Maximum value (slider upper bound)
step number Step size (slider increment)
label string Display label (defaults to variable name if omitted)
desc string Description text (tooltip)
choices list Choice list (dropdown)
group string GUI group name (default: "General")
type string Type hint: "int", "float", "string", "bool"
hidden boolean true to hide from GUI

If type is omitted, it is inferred from the default value (42 → int, 2.5 → float, "PLA" → string, true → bool).

Choices (dropdown)

Use choices to display a dropdown menu:

@param choices:["M3", "M4", "M5", "M6"] desc:"Bolt size"
$bolt = "M4"

Practical example

@param 40..200 step:5 group:"Dimensions" desc:"Case width"
$case_w = 100

@param 30..150 step:5 group:"Dimensions" desc:"Case depth"
$case_d = 60

@param 1..5 step:0.5 group:"Wall" desc:"Wall thickness"
$wall = 2

@param type:"bool" group:"Features" desc:"Add ventilation holes"
$vents = true

box $case_w $case_d 40
 | color "steel"
 | faces >Z | shell $wall

Parameter sets (JSON)

For a PolyScript file model.ps, you can place a model.ps.params.json file in the same directory to define parameter metadata and presets externally:

{
  "params": {
    "width": { "min": 10, "max": 200, "step": 5 }
  },
  "parameterSets": {
    "Default": {},
    "Small": { "width": 30, "height": 20 },
    "Large": { "width": 150, "height": 80 }
  }
}

Settings in the JSON file take precedence over @param annotations in the source. However, parameters defined only in the JSON without a corresponding variable declaration in the source are ignored.

CLI Override

The poly build command’s -D key=value option lets you override parameters at build time. Use --params-file to load values from a JSON file.

poly build model.poly -D width=100 -D height=50
poly build model.poly --params-file presets/large.json

-D values are type-inferred (100 -> int, 1.5 -> float, true -> bool, PLA -> string). When the same key is specified in multiple sources, the precedence order is:

  1. CLI -D
  2. --params-file
  3. .poly.params.json parameterSets.default
  4. @param default values

Passing an unknown parameter name via -D produces a warning but does not stop execution.

See Getting Started for a hands-on walkthrough.

Functions

Single-expression functions with def:

def standoff($r, $h, $hole_r) = cylinder $r $h | diff cylinder $hole_r $h

box 80 60 3
 | union standoff 4 10 1.5 at:10 10

Import

Load function definitions from another .poly file:

import "gear"

spur_gear 12 2 | extrude 8

Imports are resolved relative to the importing file’s directory.

Expressions

Arithmetic

+  -  *  /  //  %  **

Note: The minus sign - must be attached to the number. -1 is valid, but - 1 (with a space) is an error.

Comparison and Logic

==  !=  <  >  <=  >=
and  or

Conditional

if condition then expr else expr

if/then/else is an expression, so it can be used inline:

$use_round = true
$shape = if $use_round then circle 10 else rect 20 15
$shape | extrude 5

List Comprehension

[$expr for $var in range($n)]

Practical examples:

# Generate 6 boxes spaced 15mm apart
union [box 10 10 10 at:($i * 15, 0) for $i in range(6)]

# Concentric circles
[$r * 5 for $r in range(1, 6)]

Math Functions

sin, cos, tan, asin, acos, atan, atan2, sqrt, radians (alias: rad), degrees (alias: deg), floor, ceil

Constant: pi

Tuples and Lists

(10, 20, 30)              # tuple
[(0,0), (10,5), (20,0)]   # list

Boolean Operations (Source Commands)

union, diff, and inter can be used as source commands (without a pipe) to combine multiple shapes:

union [box 10 10 10, sphere 7]     # combine shapes
diff [box 10 10 10, sphere 7]      # subtract second from first
inter [box 10 10 10, sphere 7]     # keep only the intersection

They also work as pipe operations, as before:

box 10 10 10 | union (sphere 7)
box 10 10 10 | diff (cylinder 3 20)

Note: [] is always a list literal. To combine shapes, use union [...] explicitly.

Placement

at: is a named argument that places a shape at the specified position. Write it with a colon : immediately after at.

Parentheses rules:

  • Simple coordinates (literals/variables): parentheses can be omitted (recommended). at:20 0 0
  • With expressions: parentheses required. at:($x+1, $y+1)
  • Coordinate list: brackets required. at:[(0,0), (10,0)]
  • Parenthesized at:(x, y) is always valid.
  • For array duplication, use the | grid or | polar pipe operations.
sphere 5 at:20 0 0                      # simple position (omit parentheses)
sphere 5 at:$x $y                       # variables are fine too
sphere 5 at:($x+1, $y+1)               # expressions require parentheses
sphere 5 at:[(0,0), (10,0), (20,0)]    # multiple positions (brackets required)
sphere 5 | polar 6 20                   # circular array (pipe operation)
sphere 5 | grid 3 3 20                  # grid array (pipe operation)

Similarly, the angle: named argument can be used to rotate a shape:

rect 10 5 angle:45                      # rectangle rotated 45 degrees

Comments

# This is a comment