makeacut is a small, human-readable language for precise two-dimensional
fabrication drawings. A .mac file describes geometry in physical units and
labels it as cutting, scoring, engraving, or guide work. makeacut can render
the result as SVG or PNG, and includes a browser editor with a live preview.
This guide is the manual for the current implementation.
units mm
coordinates cartesian
wall_width = 120
wall_height = 50
door_width = 20
cut wall:
rect wall_width wall_height
cut door:
zero
move (wall_width - door_width) / 2 0
rect door_width 30
score roof_line:
zero
move 0 40
line wall_width 0
The editor can render live or when Run is pressed, zoom the preview, show or hide numbered coordinate rulers while retaining the background grid, and save or open makeacut designs. The preview's Output menu can print the current design or download it as PNG or SVG. With the 3D feature enabled, 3D Printer opens an export dialog with a 3D preview, dimensions and part thicknesses. STEP is the default for further CAD editing; binary STL is available for slicers, with fine, standard and draft detail settings. Import STL in millimetres. Closed cut outlines become solids using the design's thickness defaults and cut-block overrides (5 mm when undeclared), preserving holes and individual part heights. Paper tabs use their straight baselines; scores, engraving, guides and labels do not add material. All parts are saved in one file in their drawing positions. Arrange them and prepare printing in your CAD application or slicer.
The white material shading fills overlapping closed cut outlines without
cancelling their overlap. A fully enclosed outline forms a hole, with further
nested outlines forming islands. Outlines sharing a boundary segment stay filled,
even when one fits inside the other. Separate intersecting outlines remain separate
pieces in 3D. Use add: to merge their actual cut geometry or subtract: to
remove one shape from another; preview shading does not change the cut lines.
Live preview turns off and its control is disabled when you reach your plan's autorun line cutoff (500 by default). Use Run for larger designs. At your plan's maximum line cutoff, Run is also disabled (1,000 lines on Free). A message at the bottom explains the limit. Comments and blank lines count, including the final empty line after a trailing newline, just as in the line numbers. You can keep editing and autosaving; delete lines below the cutoff to re-enable the controls, then select Live preview again if you want autorun.
Choosing Print opens the print planner. A named setup records its output mode, physical page or bed size, units, margins, overlap, and output options. The hosted editor saves setups to the user's account; the standalone editor saves them in the current browser. Paper presets include common ISO and North American sizes; laser presets include several common bed sizes. Custom widths and heights can be entered in millimetres or inches.
The planner divides a design into physical pages and shows the entire job as a two-dimensional page layout before opening the system print dialog. It can add alignment guides, turn glue tabs into their underlying straight lines, turn scores into ordinary lines, omit labels or cuts, add page numbers and a physical-size ruler, and prepend a one-page assembly plan. Omitting cuts is useful for a separate engraving pass on a laser cutter. Use 100% scale and disable any additional margins in the system print dialog so the printed dimensions match the plan and ruler.
The local editor saves .mac files in its configured design directory. The
hosted editor stores designs in flat catalogs. Its Open dialog searches
design names, descriptions, and catalog names across the user's library and
the available system catalogs.
Each hosted user has a read-only Recent Designs catalog containing the ten unique designs they opened most recently. Opening a design again moves it to the top. The history and last-opened catalog are stored server-side, and shared designs remain in the list only while the user can still view them.
Hosted designs have stable URLs that can be copied and shared. The Save dialog controls visibility:
Only the owner can update a design. A signed-in viewer of a hidden or public design opens it read-only and can use Clone to create an independent private design in one of their own catalogs. Anonymous viewers receive a read-only editor and must sign in to clone. Clicking a System Catalog preview also opens the saved design read-only. Use Clone to choose a name and one of your catalogs, then create an editable personal design.
While you edit a hosted design that you own, makeacut automatically saves a private working draft after a short delay. During continuous typing it saves at least every five seconds. A small browser-local backup protects the newest keystrokes if a request or page close interrupts an autosave. Shared and public viewers continue to see the last checkpoint, never the owner's working draft.
Save first flushes the working draft and then creates an immutable checkpoint revision, including its preview and metadata. Revision and draft checks prevent an older browser tab from silently overwriting newer work; reload if the editor reports that autosave stopped because another tab changed the draft. Personal catalogs cannot be nested, and a nonempty catalog must be emptied before it can be deleted.
Deleting a design first moves it to Deleted Designs. From that catalog, choose Restore to return it to its previous catalog, or Delete Forever to remove it permanently. If the previous catalog no longer exists, Restore places the design in My Designs.
For a saved design you own, open Save and choose History to browse its revisions. When the autosaved source differs from the last checkpoint, the history begins with Working draft. Saved revisions show their metadata, source, and captured preview. Browsing history is read-only and does not change the source currently open in the editor. Choose Make Current to copy a selected older snapshot into a new revision without deleting any intervening history. The design remains in its current catalog.
Place the source cursor on a line, score, bezier, rect, triangle, circle, or arc
command, a directional command (right, left, up, down), or a cursor
command (move, zero, angle, turn), then enable Edit geometry above
the preview. The selected shape or cursor control is
highlighted, with handles for its dimensions, endpoint, or Bézier controls.
Its starting position remains fixed.
The inspector below the preview also lets you enter exact values in the
command's units.
Two-value endpoints and explicit controls move freely in the command's
coordinate plane. A one-value endpoint moves along the starting heading;
--tangent adjusts the control distance along that heading and stays positive.
Rotated and reflected planes, units, and scale are accounted for automatically.
Rectangles have width and height handles. Adding --radius gives a rectangle
a corner-radius handle, constrained between zero and half the shorter absolute
dimension. Triangles have base and height handles; --apex adds an apex-offset
handle, and --rounded adds a radius handle constrained to the incircle radius.
Circles have a radius handle;
the three-value circle radius start sweep form also has start and sweep angle
handles. Arcs have a radius handle at their center and a sweep handle at their
endpoint. Dragging a sweep preserves its positive or negative direction; enter
a signed value in the inspector to reverse it. Angles are measured in degrees.
Directional commands have a length handle constrained to their axis. move
and zero have a position handle and x/y inputs; move --zero and zero use
the current origin. Dragging a bare zero adds its two coordinates. Named
references (move --to and zero --at) remain editable in the source.
angle and turn have a rotation handle: angle sets a direction relative to
the coordinate plane, while turn rotates from the incoming heading. The faint
reference ray shows the direction from which the angle is measured. Rotation
inputs are in degrees and retain signed values and complete turns.
The inspector's Round menu controls precision while dragging: None keeps the current precision, 0 rounds to whole numbers, 0.1 to tenths, and 0.01 to hundredths in the command's units. Previewed and saved values use the same rounding. Selecting a command initially matches rounding to its numeric arguments: whole numbers select 0, tenths 0.1, hundredths 0.01, and finer values None. You can override this for that command. Choosing a precision immediately rounds the selected command's numeric arguments as one undoable edit, then applies to subsequent drags. Tangent lengths stay positive, with a minimum of one rounding step. Values entered directly in the inspector keep their exact precision.
Dragging previews the whole design, including geometry that depends on the
edited command. The white area grows or shrinks to fit the geometry and handles,
including points dragged outside its previous boundary. Resizing keeps document
coordinates in the same screen position while dragging.
Release to change its numeric arguments in the source, or press
Escape to cancel. Each completed drag is one Undo step. Options, comments,
indentation, and line continuations are preserved. Expressions and lengths
inherited from a walk remain protected; edit those in the source.
If a command appears in several part placements or repeats, all instances are
highlighted. Choose a reference instance in the inspector or click its
highlighted segment. Changing the command updates every instance. For add
and subtract, handles follow the original command before the boolean operation.
This first version supports lines and Bézier curves. The toggle is unavailable on read-only designs. Handles and the inspector are excluded from printing and exports.
Ctrl+Enter or Command+Enter: renderCtrl+S or Command+S: saveCtrl+F or Command+F while the source editor is focused: open source findCtrl+Z or Command+Z in the source or geometry inspector: undo;
add Shift to redo (Ctrl+Y also redoes)Escape during a geometry drag: cancel the dragEnter or F3 in source find: select the next match; add Shift for the
previous matchEscape in source find: close find and return focus to the editorTab: insert spaces to the next multiple-of-four indent point; with a
selection, indent every selected lineShift+Tab, Ctrl+[, or Command+[: unindent selected linesCtrl+] or Command+]: indent selected linesCtrl+/ or Command+/: toggle comments on the current or selected linesCtrl+K, then Ctrl+C (or Command+K, then Command+C): comment linesCtrl+K, then Ctrl+U (or Command+K, then Command+U): uncomment linesEnter at the end of a line: preserve that line's indentationEscape, then Tab: leave the editor with keyboard focusUnless declared otherwise, a document starts with:
units mm)coordinates page)1:1(0, 0)0°, pointing right#1d5c43) in the 3D previewTop-level geometry is allowed and is treated as an unnamed cut operation.
Using explicit fabrication blocks is recommended for real designs.
The units, coordinates, scale, and defaults declarations must appear
before geometry. They cannot be changed inside a part.
Set the thickness and material color with a document-wide block:
units mm
stock = 2.5
defaults:
thickness stock * 2
color "#1d5c43"
cut panel:
rect 40 20
thickness accepts a positive numeric expression using variables defined above
the block. Explicit thickness uses the document's units and modeling scale.
Without a declaration, thickness remains 5 physical millimetres. Thickness does
not change 2D geometry, printing, or SVG/PNG exports.
color sets the 3D rendered material color. It accepts a quoted six-digit hex
color, such as color "#3366ff"; quotes keep the # from starting a comment.
Omitting it uses green (#1d5c43). 3D lighting shades the chosen color. The 2D
preview and SVG/PNG exports keep their existing fills and operation styles.
A general defaults block supports thickness and color, together or separately.
Declare it before geometry, at the document's top level; defaults inside parts
are not supported.
Expressions are evaluated when the block is read. Reassigning a variable later
does not change a previously evaluated default. Multiple blocks before geometry
are allowed, with later settings replacing the earlier values for the same key.
defaults tab: continues to configure paper tabs separately.
Override the thickness on a cut block when a piece uses different stock:
cut base --thickness 3:
rect 40 20
move 60 0
cut panel --thickness default:
rect 40 20
--thickness accepts a positive numeric expression or the keyword default,
which selects the document's thickness. Omitting the option also uses that
default. Set thickness on the outline that owns the material: holes inherit
their surrounding piece's thickness. A hole-only block cannot override thickness.
Without a Boolean result override, subtraction tools cannot set thickness, and
joined outlines in an add: block must have the same thickness; disconnected
pieces can have different heights. add --thickness expression: and
subtract --thickness expression: explicitly set the resulting material's
thickness, overriding child heights. Both also accept default. Primitive
commands such as rect do not accept --thickness.
The default keyword can also be a part parameter's default value or a placement
argument, and can be forwarded through nested parts:
part panel:
parameters:
thickness default
cut --thickness thickness:
rect 40 20
place panel --thickness 3
move 60 0
place panel --thickness default
The keyword is unquoted; "default" is text, not a thickness value. Numeric
expressions are evaluated when the cut block is read.
units mm
The supported units are mm, cm, m, in, and ft. Choose one per
document. All authored coordinates, dimensions, computed geometry results, and
debug coordinates use that unit. Rendered physical output is converted to
millimetres.
coordinates page
In page coordinates, positive x points right and positive y points down.
coordinates cartesian
In Cartesian coordinates, positive x points right and positive y points up.
move dx dy and line dx dy follow the selected coordinate system. The
directional commands up and down always describe visual directions.
units m
scale 1:160
scale numerator:denominator converts prototype dimensions to model output
dimensions. With the declarations above, an authored 16 m length renders as
100 mm. Both sides may be numeric expressions and must be greater than zero.
Computed results and debug points are still reported in authored units.
makeacut uses one persistent cursor with a position and an angle. Geometry commands are relative to its current position; there is no absolute-coordinate mode. The cursor survives fabrication operation boundaries.
Every cursor and geometry command accepts an optional --name identifier
label. Names must be identifiers. Named drawing commands expose numbered
points for move --to. Straight line, score, directional commands, close,
and tab can also define a local coordinate plane with from or zero --at.
A tab uses its logical baseline. Curves and closed shapes expose point
references but cannot define a straight coordinate plane. Names on cursor
commands are labels and do not expose drawing points:
move 10 5 --name panel_start
line 20 --name bottom_edge
tab 15 --depth 5 --name glue_edge
close --name outline
Command options use --: for example, --name, --score, --zero,
--depth, --taper, --left, --right, --noscore, and --off.
Existing undashed statements are still accepted. When a statement uses a
-- option, use that spelling for all its options. This keeps option words
available as expression variables, as in tab 20 --depth depth.
Block declarations and settings, such as defaults tab: followed by
depth 5, retain their statement syntax.
from named_line:Use the start and direction of an earlier named line as a temporary local coordinate plane:
coordinates cartesian
line 10 10 --name line_1
from line_1:
line 0 10
The named line's start becomes local (0, 0), and its direction becomes the
positive x-axis. The new plane is reflected across that x-axis: its y-axis and
positive turn direction point to the opposite side of the named line from the
plane that created it. This means the same walk body can reuse the shared
edge and unfold a new face on its other side.
All relative geometry in the indented block—including moves, directional
commands, rectangles, arcs, placed parts, and repeats—uses the reflected
plane. zero returns to the local origin, and angle 0 points along the named
line.
The named line must appear before the from block and have non-zero length.
When the block ends, the previous cursor position, angle, and coordinate plane
are restored. Each named line remembers the plane that created it, so a later
from continues unfolding correctly even after that plane's block has ended.
from blocks may also be nested.
zero --at named_lineUse a named line as the new coordinate plane without opening a block:
line 20 --name shared_edge
zero --at shared_edge
line 0 10
This selects the same origin, direction, and reflected side as
from shared_edge:, but the plane remains active for the statements that
follow. A later plain zero returns to the start of shared_edge; another
zero --at selects a different named line. Inside a restoring block such as
group or from, the previous plane is restored when that block ends.
The cursor angle is the current drawing direction, normalized to the range
from 0° through just under 360°. A non-zero coordinate-relative line sets
it to that segment's direction, while an arc or Bézier leaves it at the ending tangent.
An angle of 0° points right. In page coordinates, 90° points down; in
Cartesian coordinates, 90° points up. move, rect, triangle, and circle preserve
the angle. turn changes the angle relatively, while angle sets it within
the current coordinate plane. The cursor angle supplies the starting tangent
for arc and the direction for a one-value line; changing the cursor angle
does not itself rotate other movement or shape commands.
To address a position from the document origin, reset and move:
zero
move 50 20
Every command argument can be a numeric expression.
Add --name to a drawing command, then use move --to name.p1, name.p2,
and so on to reach its saved points. Numbering follows the command's logical
geometry, independently of how the outline is rendered or flattened:
| Drawing command | Numbered points | Other point references |
|---|---|---|
line, score, right, left, up, down |
p1: start; p2: end |
start = p1; end = p2 |
triangle |
p1: base start at the cursor; p2: base end; p3: apex |
— |
rect |
p1: cursor corner; p2: width corner; p3: opposite corner; p4: height corner |
— |
Quadratic bezier |
p1: start; p2: control; p3: end |
start = p1; end = p3 |
Cubic bezier |
p1: start; p2, p3: controls in order; p4: end |
start = p1; end = p4 |
arc |
p1: start; p2: end |
start, end, center |
Partial circle |
p1: start; p2: end |
start, end, center |
Complete circle |
p1: positive x; p2: positive y; p3: negative x; p4: negative y |
center |
tab |
p1, p2: baseline start and end; p3, p4: outer corners near the start and end |
start = p1; end = p2 |
close |
p1: cursor before closing; p2: original path start |
start = p1; end = p2 |
coordinates cartesian
triangle 40 30 --apex 0 --name face
move --to face.p3
circle 2
Here the circle is centered at the triangle's apex. move --to moves without
drawing and preserves the cursor heading and current coordinate plane.
Rounded triangles and rectangles retain the original sharp vertices as their
numbered points, even when a corner is no longer on the rounded outline.
Negative dimensions preserve the same authored order. Bézier numbering
includes a control generated by --tangent; quadratic and cubic end aliases
always refer to their final endpoint. A tab drawn with --off or with tabs
disabled behaves as a line and exposes only p1, p2, start, and end.
Circle cardinal points follow the current coordinate plane, including its
rotation and reflection. Positive y points up in Cartesian coordinates and
down in page coordinates. A complete circle always has four cardinal points,
including the three-argument form with a sweep of 360 or -360; its authored
start angle does not change the numbering. A full-turn arc still exposes its
segment endpoints, which coincide, and its center.
References are saved when the drawing command executes and follow units,
modeling scale, and part placement. They keep their positions after leaving a
from or restoring block and after boolean operations. Reusing a name replaces
its earlier point references. Placed parts export their named drawings to the
caller; later placements of the same named drawing replace earlier ones.
Sequential repeats retain the latest execution's points. Array repeats use
the prototype's points, at the first copy's position, as existing named-line
references do.
The name must already exist and the selector must belong to that drawing.
Errors list the available points. Closed triangles, rectangles, and complete
circles use numbered points without start or end aliases.
The named point samples demonstrate each drawing type, including control points and rounded corners.
move dx dy or move --to name.pointMove the cursor without drawing or changing its angle. A later line starts a
new path there.
move 10 5
To move directly to a saved point of an existing named drawing, use --to:
move --to B1_1.start
move --to B1_1.end
This changes only the cursor position. It draws nothing and preserves the
cursor angle, coordinate plane, and orientation. The named drawing must already
exist. Numbered selectors such as .p1 and .p3, and .center for circular
geometry, work the same way. --to is mutually exclusive with positional
coordinates and requires a point selector.
Add --zero to reset the cursor before applying the move:
move 10 5 --zero
This is equivalent to zero 10 5, or to zero followed by move 10 5. It
resets both position and angle, then moves from the current document or part
origin without changing the reset angle. The option may also precede the
coordinates, as in move --zero 10 5.
line distance or line dx dyWith one value, draw forward by that distance in the cursor's current angular direction:
line 20
arc 10 90
line 30
A positive distance preserves the angle. A negative distance draws backward
and reverses the cursor angle by 180°, because the angle always describes
the direction of the last non-zero line. A zero distance leaves position and
angle unchanged.
With two values, draw by the given coordinate offset. This form sets the cursor angle to the segment's direction:
line 20 0
line 3.5 4.2
line 0 -6
Diagonal movement needs no separate command: give both x and y an offset. Both forms move the cursor to the segment endpoint. If no path is active, the path begins at the current cursor.
Use score with the same one- or two-value forms to emit a score rather than
a cut:
cut outline:
line 40 0
score 0 20 --name fold
line -40 0
close
The scored segment remains part of the logical path: it moves the cursor,
updates the angle and result.*, and is included in result.path_length.
Later cut segments continue from its endpoint, and close still returns to
the beginning of the complete path. The option form line 0 20 --score
produces the same scoring behavior. The older line 0 20 score spelling
remains accepted for existing statements.
score distance or score dx dyDraw a score line using the cursor and angle behavior of line. With one
value, it draws that distance in the current direction. With two values, it
draws by the given coordinate offset and updates the cursor angle.
The command is distinct from a score operation block: score 20 draws one
segment, while score fold: begins a named block of score geometry.
right distance, left distance, up distance, down distanceDraw in a visual direction and move the cursor. These commands are convenient for orthogonal paths.
right 40
down 20
left 40
up 20
right and left affect x. up always moves visually upward and down
always moves visually downward, in both coordinate systems.
turn sweep_angleAdd an angle to the cursor's current direction without moving or drawing:
line 20
turn 90
line 10
Positive values increase the angle and negative values decrease it. The result
is normalized to the range from 0° through just under 360°. turn does not
interrupt an active path or replace result.*.
angle absolute_angleSet the cursor to an absolute direction without moving or drawing:
angle 45
line 20
The supplied angle is normalized to the range from 0° through just under
360°. angle does not interrupt an active path or replace result.*.
label "text"Add a centered text annotation to the nearest enclosing scope:
group row_1:
label "Row 1"
cut face:
label "1"
line E
turn 120
score E
turn 120
tab E
A label inside a fabrication operation is centered on that operation. A label directly inside a group is centered across all geometry created by the group. At the top level, it is centered on the complete drawing. Labels may appear before the geometry because their positions are resolved when their scope ends.
Closed faces use their area centroid, which gives triangles and other asymmetric faces a visually balanced label position. Open geometry falls back to the center of its logical bounding box. Tab protrusions are ignored, so a face label remains centered on the face rather than the complete tab outline. Labels use a fixed physical height of 4 mm and move with placed parts and repeated geometry. They appear in the preview, print output, PNG, and SVG, but are annotations rather than cut or score paths.
Label text must use double quotes. JSON-style escapes are accepted, and #
inside the quoted text is not treated as a comment.
add [--thickness expression|default]:Merge the closed cut outlines of all child shapes. The outer boundary of the combined material remains a cut; child edges inside that material become scores. Shared interior edges are emitted only once, including partial overlaps. Identical exterior outlines remain a single cut rather than becoming scores.
add:
rect 20 15
move 20 0
rect 20 15
This produces a 40 by 15 rectangle with a score along the shared 15-unit edge. Overlapping shapes are split at their intersections: exposed edge portions stay cuts and covered portions become scores. A shape contained entirely inside another contributes an interior score outline. Disconnected shapes remain separate islands. Circles and circular arcs retain their native curves in fabrication output.
Use --thickness to choose the thickness of every resulting solid, including
disconnected pieces. It accepts a positive expression in the document's units
and modeling scale, or default for the document's thickness. The value is
evaluated when the header is read. An explicit override resolves differing
child thicknesses, including nested Boolean blocks and placed parts:
stock = 3
add --thickness stock * 2:
cut --thickness 3:
rect 40 20
move 20 0
cut --thickness 8:
rect 40 20
This produces one solid with a thickness of 6, without changing the 2D geometry. Without the option, joined child outlines must have the same thickness and disconnected pieces retain their individual heights. An enclosing Boolean block's explicit override determines the final result's thickness.
The block supports ordinary drawing commands, cut operations, placed parts,
repeats, and nested groups or add: blocks. It restores the cursor position,
angle, and coordinate plane on exit, just like group:. It can also appear inside
a fabrication operation or a walk: block. Geometry outside the block is not
merged. Explicit scores, engravings, guides, labels, and debug points are retained.
At least one closed cut outline is required. Open cut lines are split at the
material boundary: portions inside become scores and portions outside remain
cuts. add: is not supported inside measure:. Dimensions obey the document's
units, scale, and coordinate system. Calculations use a small numerical tolerance
for shared endpoints and intersections.
Unlike group scored:, which recognizes duplicate complete line segments,
add: merges actual filled shapes and handles intersecting boundaries. Scored
groups inside an add: block participate in the enclosing merge.
subtract [--thickness expression|default]:Remove every subsequent closed cut shape from the first one, in drawing order. A cutter fully inside the first shape creates a hole; a cutter crossing its edge creates a notch. Cutters outside the first shape have no effect. Overlapping cutters remove their combined area, and identical shapes leave no material.
subtract:
rect 40 30
move 10 10
rect 20 10
This produces a 40 by 30 rectangle with a 20 by 10 rectangular hole. All resulting boundaries are cuts. Subtraction does not automatically create scores.
--thickness sets the thickness of all remaining material. It accepts the same
expressions and default value as add: and cut:. With an explicit result
override, base and cutter thicknesses are ignored; cutters only determine the
removed geometry. Without the option, the base retains its thickness and
cutters cannot set thickness.
subtract --thickness 6:
rect 80 40
move 20 20
circle 6
move 40 0
circle 6
This produces a plate with two through holes and a thickness of 6. Use
subtract --thickness default: to select the document's thickness explicitly.
Each ordinary closed outline is a shape, including outlines drawn with line,
arc, and close. A nested add: or subtract: result counts as one shape,
including any islands and holes. To use several shapes together as the base,
combine them inside a first child add: block:
subtract:
add:
rect 20 20
move 20 0
rect 20 20
move 15 5
rect 10 10
Circles and circular arcs remain native curves. Explicit score, engraving, and guide geometry authored inside the block is clipped to the remaining material; operation names and kinds are retained. Labels and debug markers remain authoring annotations. Geometry outside the block is unchanged.
Like add:, subtract: restores the cursor position, angle, and coordinate plane
on exit. It supports parts, repeats, nested groups, and use inside fabrication
operations and walk:. Units, scale, and coordinate systems apply normally.
Children must have closed cut outlines; open lines cannot define subtraction
areas. A single child retains its shape. An empty block is an error, and
subtract: is not supported inside measure:. A fully removed result contains
no drawable geometry.
group [label] [scored]:Run a block of drawing commands, then restore the position and angle the cursor had on entry. An optional identifier can label the group for clarity:
move 20 10
group detail:
rect 15 8
move 4 2
circle 1
line 30
The rectangle and circle remain in the output, while line 30 begins from
(20, 10) with the original cursor angle. The label does not change rendering
or cursor behavior. Debug points and result.* values produced inside the
group also remain. Ending a group ends its active path, so drawing after the
group starts a new path at the restored cursor. Groups can be nested and can
be used inside fabrication operations, parts, and measure: blocks.
Add the scored modifier to derive shared score lines from cut geometry. It
can follow an optional group label, or be used by itself:
group row_A scored:
zero 0 10
cut left_face:
walk --turn -90 --stride 10:
tab
line
tab
tab
zero 10 10
cut right_face:
walk --turn -90 --stride 10:
line
line
line
line
Within one group scored: scope, two or more cut-line segments with the same
endpoints are treated as one interior edge. Direction does not matter and a
small coordinate tolerance accommodates trigonometric rounding. All copies of
the shared edge are removed from the cut paths and one score segment is
emitted in their place. Cut lines contributed by placed parts participate too,
including parts placed inside nested groups and walks. Unique lines remain
cuts. Tab outlines remain cuts, and explicit score commands and tab score
settings continue to work normally. Separate group scored: scopes do not
interact.
A group may also declare one or more experimental browser backgrounds:
coordinates cartesian
group artwork:
background "placeholder":
at 0 0
clip
tile
cut panel:
rect 80 50
The quoted value is a logical image name. During this experiment every name
resolves to the bundled background-1.jpg. The image box initially has the
group's logical width and height and uses a centered cover-style fit. at x y
positions its anchor in the group's coordinate plane using authored units. In
page coordinates that anchor is the image's top-left corner; in Cartesian
coordinates it is the bottom-left corner, so positive y still points visually
upward. The defaults are at 0 0 and clip shape.
Bare clip is shorthand for clip shape. It clips to the union of the closed
logical cut outlines in the group, so adjacent faces remain one continuous
region and score lines do not create gaps. Tab protrusions are excluded.
clip bounds retains rectangular logical-bounds clipping. clip none and
noclip are equivalent and allow the image box to extend outside the group,
expanding the browser preview as needed.
tile repeats the current image box horizontally and vertically as many times
as needed to cover the group. With either clipping mode, partial edge tiles are
cropped; without clipping, complete edge tiles remain visible. Without tile,
one image is drawn. Backgrounds render beneath fabrication geometry. They
currently appear only in hosted browser previews, are omitted from PNG and SVG
exports, and are not yet supported inside reusable parts.
pattern kind:Patterns are repeating vector surface markings attached to the closed cut
shapes in their containing block. They always tile and remain editable as a
pattern; there is no image asset and no tile setting.
units mm
defaults pattern:
color "#aaaaaa"
line-width 0.15 mm
groove-width 0.4 mm
depth 0.3 mm
group wall:
subtract:
rect 80 50
move 30 15
rect 20 25
pattern bricks:
size 8 3
stagger 0.5
at 0 0
angle 0
margin 1
Declare a pattern directly inside a part, cut:, group:, add:, or
subtract: block, with one or more indented settings. The entire block's
closed cut geometry is its target, regardless of whether the pattern appears
before or after the geometry. A pattern does
not move the drawing cursor, affect measurements, or change the cut outline.
It clips to the finished material, including holes, curved outlines, and later
boolean subtractions. Paper glue tabs are excluded using their baselines.
| Pattern | Layout setting | Built-in size in authored units |
|---|---|---|
lines |
spacing distance or size width height (height is the spacing) |
5 |
crosshatch |
spacing distance or size width height |
5 × 5 |
tiles |
size width height |
5 × 5 |
bricks |
size width height, optional stagger fraction |
8 × 3 |
floorboards |
size length width, optional stagger fraction |
20 × 3 |
Bricks shift alternate courses; floorboards advance their end joints by the
stagger fraction on each successive row. The default stagger is 0.5; values
must be at least zero and less than one. Size and spacing must be positive,
and cannot both be declared. Dimensions and settings accept expressions.
Changing the shape size reveals more repeats without stretching the pattern.
at x y offsets the repeat origin from the containing block's entry cursor,
in its local coordinate plane. It defaults to at 0 0. angle degrees rotates the pattern
in that plane. Both page and Cartesian coordinates are supported. An origin
and angle shared by adjacent groups can align their joints. A single group
covering several adjacent faces has one continuous pattern across those faces.
In a part, the origin is the part's local (0, 0); placing the part moves
the pattern and geometry together. In a cut, the origin is the cursor at
the cut header. Moves inside either block do not change the pattern origin.
Reusable parts do not need a wrapper group around their patterns:
part pillar:
pattern bricks:
size 10 1.25
at 0 0
cut --thickness 2:
rect 1.5 11
place pillar
move 19.5 0
place pillar
A pattern inside a cut: applies only to that cut. Patterns in enclosing
parts or groups also apply, so nested patterns can be combined. Score, guide,
and engraving operations do not provide material surfaces for patterns.
margin distance leaves a clear strip around outside edges and holes, without
introducing gaps at internal seams. The margin is measured to the widest of
the paper stroke and the groove footprint. It defaults to zero. Curved margin
boundaries use conservative sampling with up to 0.01 mm tolerance; ordinary
pattern clipping retains native curves.
The following appearance settings can be set per pattern or inherited from a
defaults pattern: block declared before geometry:
| Setting | Built-in physical default | Output |
|---|---|---|
color "#rrggbb" |
"#aaaaaa" |
Paper, SVG/PNG, and 2D preview |
line-width distance |
0.15 mm | Paper and vector engraving display |
groove-width distance |
0.4 mm | 3D groove width |
depth distance |
0.3 mm | 3D groove depth |
margin distance |
0 | Clear strip around edges and holes |
Unqualified distances follow units and scale. An explicit suffix such as
depth 0.3 mm or groove-width 0.04 cm fixes that distance in physical output
units, independent of modeling scale. This lets prototype brick dimensions
scale down while grooves remain manufacturable. at, size, and spacing
always use authored units; angles and stagger fractions are dimensionless.
Patterns work inside reusable parts and retain their placement transforms.
Placed patterned parts can be used in flow right: or repeat count every
spacing: blocks. A sequential repeat count: can repeat a group containing
its own pattern. Cut operations with patterns can also be used directly in
spaced repeats. Multiple patterns on a block are allowed and are combined.
Paper output uses light lines. Laser mode renders the same paths as a separate
engraving group; power, speed, and passes remain settings in the laser's
software. Exported SVGs contain real clipped paths with engrave pattern
classes and depth/width metadata, rather than SVG repeat fills or bitmap
images. SVG and PNG downloads include patterns.
3D output subtracts rectangular grooves from the top face. Crossings do not
accumulate depth. Each groove must be shallower than its target material;
through-cutting depths produce a source diagnostic. Both STEP and STL export
use the same solids. The 3D preview defaults to Surface lines, drawing the
clipped pattern strokes on each part's top face for faster interaction.
Select Grooves in the 3D Printer dialog to see the actual
grooved solids. Surface lines show the pattern color and layout, with thin
screen-space strokes rather than physical groove width or depth. STEP and
STL exports always include the grooves, whichever preview mode is selected.
The 2D and surface-line previews construct grooved solids only when a
Grooves preview or export is requested. Existing handwritten engrave geometry
retains its current behavior.
To bound geometry generation, patterns are limited to 5,000 vector segments per design and 1,000 groove segments per material part for grooved 3D output. Increase size or spacing when a density diagnostic appears; details are never silently omitted.
arc radius sweep_angleDraw a circular arc beginning at the cursor and tangent to the cursor angle. The arc moves the cursor to its endpoint and adds the sweep to the cursor angle.
line 20 0 # cursor angle is 0°
arc 10 90 # radius 10; turn through +90°
line 20 # continues along the ending tangent
The radius may be zero or positive. A negative radius is invalid. The sweep is measured in degrees, must be non-zero, and cannot have an absolute value greater than 360. A positive sweep increases the cursor angle; a negative sweep decreases it. Therefore a positive sweep is visually clockwise in page coordinates and counterclockwise in Cartesian coordinates.
With a radius of zero, arc 0 sweep_angle is a pure turn: it is equivalent to
turn sweep_angle, emits no geometry, preserves the cursor position and active
path, and does not replace result.*. It retains the arc command's non-zero,
at-most-360° sweep restriction.
Starting at 0°, arc 10 90 ends at offset (10, 10) in authored
coordinates and leaves the cursor angle at 90°. arc 10 -90 ends at
(10, -10) and leaves the angle at 270°.
A sweep of 360 or -360 draws a complete circle tangent to the cursor,
returns to the same position, and returns to the same normalized angle. This
differs from circle, whose centre is the cursor and which never moves it.
bezier distance|dx dy --control cx cy [cx cy]Draw a quadratic Bézier with one control point, or a cubic Bézier with two.
Endpoint arguments follow line: one value places the endpoint that distance
along the current cursor angle; two values give an offset in the current
coordinate plane. Every explicit control point is also an offset from the
curve's starting cursor, including the second control of a cubic:
bezier 40 0 --control 20 30
bezier 60 0 --control 20 30 40 -30
Controls follow the selected coordinate system and any reflected from
plane. All lengths support expressions, authored units, and modeling scale.
Changing the cursor angle affects a one-value endpoint, but does not rotate
explicit control offsets.
bezier distance|dx dy --tangent length [--control cx cy]Use --tangent to place the first control point along the current cursor angle.
Its positive length determines the first handle's reach. Without --control,
this draws a quadratic. With one explicit second control, it draws a cubic:
line 20 0
bezier 40 20 --tangent 15 --control 25 20
line 15
The curve starts in the line's direction and leaves the cursor pointing along
its ending tangent. A following one-value line or tangent-based Bézier
continues in that direction. Use angle or turn beforehand to choose a
different starting tangent. Matching tangents gives matching direction at the
join, but does not guarantee matching curvature.
If the final control equals the endpoint, the ending direction uses the nearest distinct preceding control or start point. A curve with every point equal preserves the cursor angle. Curves can return to their start, forming loops; an endpoint offset of zero does not by itself mean zero curve length.
Both forms accept --score and --name identifier, in the same positions as
line options. Scoring moves the cursor and contributes to the logical path
length; later close still returns to the original path start. Named curves
support move --to name.start|end and debug labels. from and zero --at
require a straight baseline. Curves cannot be converted to tabs.
SVG export preserves native quadratic and cubic paths. PNG rendering samples
curves to pixel accuracy. Curve lengths are calculated numerically; bounds use
the curve extrema, excluding unused control-point overhangs. add and
subtract preserve Bézier fragments at intersections with lines, circles,
arcs, and other Béziers. Background clipping and label centroids use sampled
curved outlines.
See the eight progressively more complex samples for complete designs.
closeClose the active path with a segment back to its first point. The path must already contain at least one drawn segment. After closing, the cursor is at the path's first point and, when the closing segment is non-zero, its angle is the direction of that segment.
line 20 0
line 0 10
line -20 0
close
zeroReset the cursor to the document origin (0, 0) and its angle to 0° without
drawing.
zero
Supply coordinates to move immediately after resetting:
zero 10 5
This is equivalent to move 10 5 --zero. Inside a part, both forms use that
part's local origin. Coordinates follow the selected page or Cartesian
coordinate system.
rect width height [--radius value]Draw a rectangle at the cursor. The cursor position and heading stay unchanged.
move 10 20
rect 30 40
In page coordinates, positive width extends right and positive height extends down. In Cartesian coordinates, positive height extends up. Negative width or height is allowed and extends in the opposite direction. Neither dimension may be zero.
move 7.45 4.2
rect -0.55 -4
Add --radius to round all four corners inward. Width and height describe the
full outer dimensions; the cursor anchors the same bounding-box corner as an
ordinary rectangle, even though that corner is no longer on the outline.
The rectangle follows the current coordinate plane, independently of the
cursor heading.
rect 80 50 --radius 6
rect WIDTH HEIGHT --radius CORNER_RADIUS
rect -40 25 --radius 4
The radius takes a numeric expression in the current units and follows the
modeling scale. It must be zero or positive and cannot exceed
min(abs(width) abs(height)) / 2. Invalid radii produce an error rather than
being silently reduced. Omitting --radius or setting it to zero draws an
ordinary rectangle. Options such as --radius and --name may appear in any
order.
A radius equal to half the shorter absolute dimension produces a capsule; when both dimensions have the same magnitude, it produces a circle within the rectangle's bounds:
cut slot:
rect 30 10 --radius 5
Rounded rectangles are closed outlines with straight edges and tangent
quarter-circle corners. SVG and fabrication output retain native circular
arcs, including through add and subtract. Like other shapes, they do not
replace result.*.
triangle base height [--apex offset] [--rounded radius]Draw a closed triangle at the cursor, with its base along the x axis of the
current coordinate plane. The base runs from the cursor to base; the third
vertex is at x offset base / 2 and y offset height. Use --apex to set a
different x offset, measured from the cursor. The offset may lie outside the
base, and an offset of zero or base produces a right triangle.
triangle 40 30 # isosceles: apex at (20, 30)
For a right triangle, set --apex 0. The right angle is at the cursor, and
base and height are the lengths of the two perpendicular legs. Set the apex
offset equal to the base to put the right angle at the other end:
triangle 40 30 --apex 0
move 55 0
triangle 40 30 --apex 40
Choose another offset to draw a scalene triangle:
triangle 50 30 --apex 12
Positive height extends down in page coordinates and up in Cartesian
coordinates. Base and height may be negative, but neither may be zero.
The triangle follows the current coordinate plane independently of the cursor
heading, and preserves the cursor position, heading, and result.* values.
coordinates cartesian
triangle 40 -30 --apex 0 # extends downward from the base
Add --rounded radius to round all three corners inward with tangent circular
arcs. The dimensions and apex describe the original sharp triangle. Rounding
removes its tips, so the rounded outline may have smaller bounds. The radius
must be zero or positive and cannot exceed the triangle's incircle radius:
abs(base * height) / perimeter, where perimeter is the sum of its three side
lengths. Larger radii produce an error rather than being silently reduced.
At the maximum radius the outline becomes the incircle. Omitting --rounded
or setting it to zero produces a sharp triangle.
triangle 40 30 --apex 0 --rounded 3
For this right triangle, the hypotenuse is 50, so the maximum radius is
40 * 30 / (40 + 30 + 50), or 10 units. Setting --rounded 10 produces its
incircle.
All values accept numeric expressions in the current units and follow the
modeling scale. --apex, --rounded, and --name may appear in any order.
SVG and fabrication output retain native circular arcs, including through
add and subtract.
The triangle samples cover centered and off-center apexes, right angles at either end of the base, and rounded corners compared with a sharp guide outline.
circle radius [start_angle sweep_angle]Draw a circle centered on the cursor. The radius must be greater than zero, and the cursor does not move.
move 25 25
circle 10
Add a start angle and sweep angle to draw only part of the circle:
circle 10 0 90 # quarter circle
circle 10 0 180 # semicircle
circle 10 45 270 # three-quarter circle
circle 10 180 -90 # sweep in the opposite direction
Angles are measured in degrees, with 0 pointing right along positive x. A
positive sweep follows the selected coordinate system: clockwise in page
coordinates and counterclockwise in Cartesian coordinates. A negative sweep
goes the other way. The sweep must be non-zero and its absolute value cannot
exceed 360 degrees. A sweep of 360 or -360 produces a complete circle.
Use either one argument or all three; circle radius start_angle by itself is
not valid. Partial circles remain centered shapes and do not move the cursor.
An operation block records fabrication intent. Its optional name must be an identifier: letters, digits, and underscores, beginning with a letter or underscore.
cut outline:
rect 100 50
score fold_line:
zero
move 0 10
line 100 0
engrave serial_number:
zero
move 5 5
rect 20 8
guide center:
zero
move 50 0
line 0 50
Inside a part, --id can take a string parameter and use its value as the
operation name:
part triangle_up:
parameters:
label
cut --id label:
line 20
place triangle_up --label "A01"
This produces the same named operation as cut A01:. The resolved value must
be text and must follow the normal identifier rules. A quoted literal is also
accepted, such as cut --id "A01":, although the ordinary cut A01: spelling
is shorter for a fixed name. --id works with cut, score, engrave, and
guide blocks.
The four operation keywords are:
cut: material is cut through; red solid lines in the previewscore: fold or crease lines; blue dashed linesengrave: surface marks; dark solid linesguide: construction-only geometry; grey dashed linesGeometry inside an operation block is indented with spaces. Tabs are not accepted for language indentation.
A named cut operation automatically names every descendant line, score,
tab, and bezier in encounter order, regardless of whether it is direct or nested in
a group, from, sequential repeat, or walk block:
cut T2:
score E # name T2_1
group:
score E # name T2_2
line E # name T2_3
Generated straight-segment names can be used by later from T2_1: blocks;
generated curve names support endpoint moves and debug labels. Explicit
--name identifier options are preserved instead of being replaced, while the
following automatic name retains its encounter-order number. Other commands
and debug statements do not consume a number.
When an operation belongs to a placed part, its implicit and explicit segment
names are exported into the enclosing document as soon as the place
statement runs. Their coordinates and orientation follow the placement, so a
later from B1_1: or zero --at B1_1 works across groups, walks, nested parts,
and reflected coordinate planes.
Declare tabs before the first segment in a cut operation to convert selected
line segments into tabs:
cut A4:
tabs 2 3
turn 60
walk --turn 120 --stride E:
line # A4_1 remains a line
line # A4_2 becomes a tab
line # A4_3 becomes a tab
The numbers use the same descendant encounter order as implicit edge names,
including segments inside walk, group, from, and sequential repeat
blocks. Labels, debug points, turns, and other non-segment commands do not
consume a number. Converted tabs use the active defaults tab: settings and
retain their implicit or explicit segment names.
Use an explicit empty selection when no lines should be converted:
cut A4:
tabs none
# ...
tabs none does not change explicitly authored tab commands. Omitting the
declaration currently has the same rendering behavior. A cut operation may
contain only one tabs declaration, and none cannot be combined with segment
numbers. Selecting an existing tab is harmless; selecting a score or a segment
number that does not exist is an error. Placement-level --notabs still
converts all resulting tabs back into ordinary lines.
A part can expose the selection as a parameter. tabs is a contextual keyword,
so it may also be used as the parameter name:
part triangle_up:
parameters:
label
tabs none
cut --id label:
label label
tabs tabs
turn 60
walk --turn 120 --stride E:
line
line
line
place triangle_up --label "A4" --tabs 2 3
The default none supplies an empty selection. A placement may supply one or
more unique positive segment numbers to --tabs; the operation validates the
resolved selection using the same rules as a literal tabs declaration.
Assign a numeric expression with name = expression:
wall_width = 120
door_width = 20
margin = (wall_width - door_width) / 2
Variable names follow the same identifier rules as operation names. They are
available after their assignment. The operators are +, -, *, and /;
parentheses control grouping. Decimal and scientific-notation numbers are
supported.
Shape command keywords cannot be used as variable, part, or part parameter names.
This includes triangle, like rect and circle. The words ellipse,
polygon, slot, star, sector, and ring are also reserved for future
shape commands; they do not draw shapes yet. Longer identifiers such as
triangular_face and ring_width remain valid. Operation names, --name
labels, and quoted text may still use these words.
Whitespace separates command arguments, so parentheses are the clearest way to make a multi-token expression one argument:
move (wall_width - door_width) / 2 10
rect door_width 30
A backslash continues a logical statement on the next physical line:
opening_width = wall_width \
- left_margin \
- right_margin
Everything after # is a comment:
# Front elevation
rect 120 50 # outside edge
Function arguments are normally separated by whitespace. Commas are accepted but optional.
| Function | Result |
|---|---|
abs(value) |
Absolute value |
sqrt(value) |
Square root of a non-negative value |
sin(angle) |
Sine of an angle in degrees |
cos(angle) |
Cosine of an angle in degrees |
length(dx dy) |
Length of a vector from its x and y offsets |
distance(x1 y1 x2 y2) |
Distance between two points |
min(value ...) |
Smallest of one or more values |
max(value ...) |
Largest of one or more values |
round(value) |
Nearest integer, with halves away from zero |
round(value places) |
Round to an integer number of decimal places |
Examples:
SLOPE = length(SHORT_WIDTH / 2 ROOF_HEIGHT)
root = sqrt(81)
rise = sin(30) * length
run = cos(30) * length
gap = distance(0 0 3 4)
safe_width = max(10 requested_width)
display_width = round(safe_width 2)
Functions can be nested:
size = max(abs(-3) length(6 8) / 2)
After line, arc, bezier, a directional command, or close, makeacut publishes
these read-only values:
| Value | Meaning |
|---|---|
result.length |
Length of the most recent segment |
result.path_length |
Total length of the current path, including the closing segment after close |
result.dx |
Signed x offset of the most recent segment |
result.dy |
Signed y offset in the selected coordinate system |
result.ax |
Endpoint x coordinate from the current origin |
result.ay |
Endpoint y coordinate in the selected coordinate system |
The values use authored units, even when modeling scale is active. Save a result in a regular variable before later drawing replaces it:
line 20 0
line 0 30
close
diagonal = result.length
perimeter = result.path_length
zero
move 50 0
rect diagonal diagonal
move, turn, angle, a zero-radius arc, zero, shapes, and debug do
not replace the last segment result.
Draw a glue tab along a baseline with the same one- or two-value forms as
line:
tab 20
tab 20 5
The one-value form advances in the cursor's current direction. The two-value
form uses dx dy and updates the cursor angle to that baseline direction. The
cursor finishes where the equivalent line would finish, but the uncut baseline
is replaced by the tab's tapered outer edge.
Less common behavior uses named options, which may appear in any order:
tab 20 --depth 8 --taper 3 --right --noscore
tab WIDTH --depth TAB_DEPTH --left --score
--depth and --taper take numeric expressions. --left and --right select the
side relative to the direction of travel. --score adds the omitted baseline as
a separate score line, while --noscore suppresses it. The opposing side and
score options cannot be combined in one command.
Add --off to convert a tab into an ordinary line along its baseline. Tab
geometry and its automatic score line are both suppressed; cursor movement,
angle, and result.* values are exactly those of the equivalent line:
tab 20 --off
tab 0 WALL_HEIGHT --name glue_edge --off
Tabs currently default to a physical depth of 10 mm, a physical taper of 5 mm at each end, the left side, and an automatic score line. Explicit dimensions use the document's authored units and modeling scale. Taper must be non-negative and cannot exceed half the baseline length; exactly half produces a triangular tab.
Change any of those defaults for the entire document with a top-level block:
defaults tab:
depth 5
taper 2.5
noscore
The settings are depth value, taper value, left, right, score, and
noscore. A block may contain only the settings that need to change; omitted
settings retain their current values. More than one block is allowed before
geometry, with later blocks updating only the settings they contain. Numeric
settings accept expressions and use the document's authored units and modeling
scale, just as options written directly on a tab command do.
Options on an individual tab take precedence over document defaults:
defaults tab:
depth 5
taper 2.5
noscore
tab 20 # depth 5, taper 2.5, left, noscore
tab 20 --right # depth 5, taper 2.5, right, noscore
tab 20 --score --depth 8 # depth 8, taper 2.5, left, score
Placed parts inherit the active document defaults. A defaults block cannot be declared inside a part.
For results, result.length, result.dx, and result.dy describe the tab's
baseline. result.path_length includes the actual tapered cut outline.
A measure: block evaluates path commands in private coordinates that start
at (0, 0). It publishes result.* values but emits no geometry and restores
the document cursor afterward.
SHORT_WIDTH = 24
ROOF_HEIGHT = 10
measure:
line SHORT_WIDTH / 2 ROOF_HEIGHT
SLOPE = result.length
A measure block accepts move, line, tab, right, left, up, down, turn,
angle, arc, bezier, close, and zero. It must draw at least one segment. Shapes,
placed parts, and debug points are not allowed inside it.
Define a part with a parameters: block. A name by itself is required; a value
after the name is its default. Required parameters must come first:
part window:
parameters:
label
width
height 20
cut opening:
label label
rect width height
score center:
move width / 2 0
line 0 height
Each part has private coordinates beginning at (0, 0). Place it at the
document cursor with named arguments. Arguments may be numeric expressions,
quoted text, true, false, default, or the empty-selection value none where the
receiving command supports it:
move 10 20
place window --label "Front" --width 30
Defaults are evaluated when the part is placed, in declaration order. A later default can therefore use an earlier numeric parameter. Named arguments may be given in any order. Missing required arguments, unknown names, and supplying a parameter more than once are errors.
Positional arguments remain supported and must precede named arguments:
place window "Front" 30
place window "Back" --width 40 --height 25
The original header form remains valid for required parameters:
part window width height:
cut:
rect width height
Placement translates the part's operations and debug points but does not change the caller's cursor position or angle. Parts can place other parts, may be used before their definition, and cannot recursively place themselves. A part must contain geometry when it is placed.
Use the notabs placement option to turn every tab produced by that placement
into its equivalent line. This also applies to tabs in nested parts and removes
their automatic score lines:
place short_side --notabs
place panel WIDTH HEIGHT --notabs
The legacy undashed notabs spelling is also accepted.
flow right: places parts consecutively using each part's logical horizontal
extent, then advances the cursor by the total distance:
part short_side:
cut:
rect 10 8
part long_side:
cut:
rect 25 12
flow right:
place short_side
place long_side
place short_side
place long_side
Only place statements are allowed inside the block. Every placed part must
extend to the right of its local origin. Geometry that overhangs to the left of
that origin does not add a gap after the part. Tabs on either side are measured
using their original baselines rather than their protruding cut outlines, so a
right-facing tab does not push the following part to the right. Ordinary
place leaves the cursor unchanged; the cursor advance is specific to
flow right:.
walk applies a cursor step after every drawing command, placed part, or group
in its body. Drawing commands and placements keep their own dimensions and
options:
walk 0 0 TURN:
score E
debug first_edge
line E
line E
line E
tab E
The positional forms remain available:
walk distance [debug]:
walk dx dy [debug]:
walk dx dy angle [debug]:
The same behavior can be written with UNIX-style long options:
walk --step distance:
walk --move dx dy:
walk --move dx dy --turn angle:
walk --turn angle:
Positional values must appear before the first named option. After that, named
options may appear in any order. Each option has a fixed number of values:
--move takes two expressions; --step, --turn, and --stride each take
one; and --debug takes none. --step and --move are alternatives and
cannot both be used. A named option may follow positional values, so
walk 0 0 --turn 90: is valid. Repeating an option or specifying the same
setting both positionally and by name is an error. The older undashed named
clauses remain accepted for compatibility with existing designs.
The optional --stride length option supplies the length for any line,
score, tab, right, left, up, or down command in the body that has
no positional value of its own:
cut:
walk --turn 120 --stride E:
score
line
line
An explicit child length overrides --stride, including a two-value vector.
Options do not count as a length, so forms such as line --score,
tab --left --noscore, and score --name fold receive the stride. --stride must
be greater than zero and does not apply to arc, close, rect, or circle.
The optional --debug flag adds a debug point at the start of every drawing
command in the body, numbered 1, 2, 3, and so on. Explicit debug
statements are not included in that count. For example:
walk 0 0 90 --debug:
line 20
line 20
line 20
line 20
This marks the starting corners of the four edges as 1 through 4 in the
browser preview.
walk distance: moves forward by distance in the current cursor direction
after each drawing command or placement. walk dx dy: performs the same
coordinate-relative movement as move dx dy. The three-value form performs
that movement and then turns relatively by angle. Both actions happen after
the final drawing command or placement as well as between items.
For example, each body entry in walk DX DY ANGLE: behaves like:
line E
move DX DY
turn ANGLE
A zero displacement is a true no-op and does not split the active path. This
makes walk 0 0 angle: a concise way to turn after each edge while preserving
one continuous logical path.
At the top level or inside a part, a walk may place parts:
walk --move 30 0:
place triangle_up --label "A1" --tabs 3
place triangle_up --label "A2" --tabs 2 3
Each place is one walk item. The part is placed at the current cursor, then
the walk applies its movement and turn. A placement does not receive
--stride; without --step or --move, consecutive placements use the same
origin. Walk --debug numbers placement origins along with drawing-command
starts. A walk containing place cannot be used inside a fabrication operation
or another scope where placement is already prohibited. Walk turns change the
movement heading but do not rotate placed parts.
A group can combine several statements into one walk item. Its children share the current step origin, the group restores that origin when it ends, and the walk advances only once after the complete group:
walk --move E 0:
place triangle_down --label "B1"
group:
place triangle_up --label "B2"
place triangle_down --label "B3"
place triangle_up --label "B4"
Here B2 and B3 are placed together at E, 0, while B4 begins at
2 * E, 0. With --debug, the group receives one number at its starting
point.
Walk bodies may contain drawing commands: line, score, tab, right,
left, up, down, arc, bezier, close, rect, and circle. They may also
contain place, group, debug, or debug label statements. A debug
statement records the cursor at that exact point in the sequence; it does not
apply an additional move or turn.
All direct walk statements must align at one indentation level; a nested
group's children are indented beneath its header. Header expressions, including
stride, are evaluated once when the walk begins. A walk may be used anywhere
all of its children are valid. Drawing-only walks work in fabrication
operations, parts, groups, measure: blocks, and sequential repeats;
placement walks work at the top level and inside parts.
repeat count: executes geometry sequentially. Every iteration begins with
the position and angle left by the previous iteration:
repeat 6:
line 20
arc 5 60
This produces a rounded six-sided path. Sequential repeats may appear at the
top level, inside a fabrication operation, inside a part, or inside a
measure: block. Their bodies may contain geometry commands, debug, and
group [label]: or walk ...: blocks. A top-level sequential repeat may also
contain array repeats.
After the block, result.* describes the final command of the final iteration.
Because placing a part does not move the cursor, a sequential repeat can use an array repeat to build rows and then move between them:
repeat 4:
repeat 4 every WIDTH * 3:
place hex_pair
move 0 10
This produces four rows of four placed parts. The inner array leaves the
cursor at the start of its row; only move 0 10 advances it.
The existing repeat count every spacing: form instead duplicates a
prototype along x:
repeat 4 every 25:
cut window:
move 20 15
rect 15 20
For both forms, the count is an expression that must produce a positive
integer no greater than 10,000. For the array form, spacing is an expression
and must be non-zero. The repeated prototype starts at the current document
cursor. Its first copy uses the geometry as authored, and each later copy is
offset by another spacing in x. An array repeat may contain fabrication
operations, placed parts, or debug points. An array repeat cannot directly
contain another repeat block.
Record the current cursor with an optional identifier label:
move 7.45 4.2
debug track_start
or:
debug
Debug points do not appear in SVG or PNG exports. In the browser preview they
appear as clickable dots showing the label, authored coordinates, unit, cursor
angle, source line, and any names assigned to the most recently rendered
segment. Segment names survive intervening turn and angle commands, making
them visible for debug statements inside a walk. Debug points inside parts and
repeats are translated with their geometry. The debug display rounds the angle
to two decimal places; the cursor retains its full internal precision.
| Syntax | Effect |
|---|---|
units mm\|cm\|m\|in\|ft |
Select authored units |
coordinates page\|cartesian |
Select y-axis direction |
scale numerator:denominator |
Set modeling scale |
defaults: followed by thickness expression |
Set document-wide 3D thickness |
defaults tab: |
Set document-wide defaults for tab options |
defaults pattern: |
Set document-wide surface-pattern appearance |
name = expression |
Assign a numeric variable |
move dx dy [--zero] |
Move relatively, optionally resetting to the origin first |
move --to name.p1\|start\|end\|center |
Move to a saved drawing point without changing the cursor angle or coordinate plane |
line distance [--score] |
Draw forward as a cut, or score with --score |
line dx dy [--score] |
Draw a coordinate-relative cut, or score with --score |
bezier distance\|dx dy --control cx cy [cx cy] [--score] |
Draw a quadratic or cubic Bézier with explicit control offsets |
bezier distance\|dx dy --tangent length [--control cx cy] [--score] |
Draw a Bézier starting along the cursor angle |
score distance |
Draw a score forward in the current direction |
score dx dy |
Draw a coordinate-relative score segment |
tab distance [options...] [--off] |
Draw a tab forward, or its baseline when off |
tab dx dy [options...] [--off] |
Draw a coordinate-relative tab, or its baseline when off |
tabs number... / tabs none |
Select cut-operation segments to convert from lines into tabs |
right distance |
Draw visually right |
left distance |
Draw visually left |
up distance |
Draw visually up |
down distance |
Draw visually down |
turn sweep_angle |
Add to the cursor angle without moving or drawing |
angle absolute_angle |
Set the cursor angle without moving or drawing |
label "text" |
Center a text annotation in the nearest operation, group, or document |
group [label]: |
Run drawing commands, then restore cursor position and angle |
add [--thickness expression\|default]: |
Merge child cut shapes; convert interior edges into scores; optionally override 3D thickness |
subtract [--thickness expression\|default]: |
Remove subsequent closed shapes from the first shape; optionally override 3D thickness |
group [label] scored: |
Convert duplicate cut lines in the group into one shared score |
background "name": |
Add an experimental browser background directly inside a group |
pattern kind: |
Tile vector surface markings over a part, cut, or group’s closed shapes |
from named_line: |
Draw in a local plane based at the start of a named line |
walk distance [debug]: |
Move forward after every drawing command or placement; optionally number their starting points |
walk dx dy [angle] [debug]: |
Move, then optionally turn, after every drawing command or placement; optionally number their starts |
walk [positionals...] [--step distance\|--move dx dy] [--turn angle] [--stride length] [--debug]: |
Use named, order-independent options after any positional values; --stride supplies omitted child lengths |
arc radius sweep_angle |
Draw a tangent circular arc and update the cursor angle |
close |
Close the active path |
zero [dx dy] |
Reset the cursor, optionally moving from the origin afterward |
zero --at named_line |
Persistently use a named line as the current local coordinate plane |
rect width height [--radius value] |
Draw a rectangle with optional rounded corners at the cursor |
triangle base height [--apex offset] [--rounded radius] |
Draw a triangle with an optional apex offset and rounded corners at the cursor |
circle radius [start_angle sweep_angle] |
Draw a complete or partial circle centered at the cursor |
cut [name\|--id string] [--thickness expression\|default]: |
Begin a cut block, optionally overriding 3D thickness |
score [name|--id string]: |
Begin a score operation block |
engrave [name|--id string]: |
Begin an engrave operation block |
guide [name|--id string]: |
Begin a guide operation block |
measure: |
Compute path results without emitting geometry |
part name: / parameters: |
Define reusable local geometry with required or defaulted parameters |
place name [arguments...] [--name value...] [--notabs] |
Place a part, optionally converting its tabs to lines |
flow right: |
Place parts consecutively along x |
repeat count: |
Execute geometry sequentially, carrying cursor state |
repeat count every spacing: |
Duplicate operations along x |
debug [label] |
Mark the cursor in the web preview |
Square brackets in the table mean optional syntax; do not type the brackets.
Every cursor and geometry command in the table may also include
--name identifier.
SVG output is tightly fitted to the geometry and uses millimetres for physical size. PNG output has a white background, includes physical DPI metadata, and adds enough pixel padding to keep boundary strokes visible. Both formats use operation-specific colors and line styles.
The browser preview adds a 1 mm viewport margin, includes debug points in its bounds, and supports zoom from 25% to 400%. These preview conveniences do not alter exported files.
Printing from the browser hides the editor controls, grid, and debug markers, and uses the SVG's physical dimensions rather than the preview zoom level.
from cutwright import parse, parse_file, render_png, render_svg
document = parse("cut panel:\n rect 100 50\n")
svg_text = render_svg(document)
png_bytes = render_png(document, dpi=300)
document_from_disk = parse_file("drawing.mac")
The implemented language intentionally has no absolute positioning commands:
use zero dx dy when a known origin-relative location is needed. Arbitrary
freeform transforms beyond named-line coordinate planes,
layers, automatic tabs, page nesting, and 3D operations are not implemented
yet.
The file extension is .mac.
Sign in and click the heart on another user's design or a system design to like it. Click the filled heart again to remove your like. You cannot like your own designs. The number beside the heart is the total like count; other people's liking history is private.
My Liked Designs in the library lists the original designs, most recently liked first, with links to their owners. It always opens the current version. Liking does not grant access: unavailable or deleted designs disappear from this folder. The folder cannot be renamed or used as a save destination.
Use Public Designs and choose Most liked to explore popular public designs. User profiles also offer a Most liked sort. Hidden and private designs are excluded from public listings.
Other people's designs open with read-only source. Use Clone to choose a name and one of your catalogs, then create your own editable design. Clones are private by default. The original design remains unchanged; likes do not transfer.
Each clone records its original design and a snapshot of that design's name, owner, and revision in the database. The snapshot remains if the original is later deleted. Cloning system designs records the same origin information.