Pegmatite Pegmatite
pegmatite@1.6.1 ↗

Patterns span their reference with inset

  • linear, grid and mirror take inset. It takes the place of the distance or gap: the pattern spans what it is placed against, its outermost centres the margin in from the edges and the rest evenly between, whatever size the iterations are. on #top grid 2 2 inset 9 -cylinder 5.5 10 drills a plate's four corner holes 9 mm in, where each needed its own x left:center 9 y back:center -9 before. linear inset 10 3 makes a row and mirror x inset 8 a pair; a second margin sets a grid's Y inset, grid 3 2 inset 10 8.
pegmatite@1.6.0 ↗

Shells open any face, or none

  • shell takes the faces to open. cube 40 | shell 2 open [#top #front] opens both faces in one shell, and pyramid 4 50 40 | shell 2 open #f1 opens a slope. Faces are named as on names them. shell 2 closed leaves a sealed hollow. Without either, a shell still opens the face that points up.

Faces as shapes build on their face

  • A profile drawn from a bare face is built on that face. #f2 | extrude 2 now covers #f2, as on #f2 #f2 | extrude 2 did; before, the outline was built on the top. This also applies under outline modifiers and to a tiling's area, voronoi 1.5 cells 20 in #f2 | extrude 2. An on written out still decides, and a face read through a label, box #top, stays an outline placed wherever the statement goes.

Directions name real sides

  • Outside on, #top, #front and the other directions name a flat side the solid has in that face of its box. A sphere has none, a cylinder only #top and #bottom, a dodecahedron only #top and #bottom. A face shape, an edge selector or rotate about naming a missing one is an error that lists the sides the solid offers. on #front still builds against the box's plane on any solid, so spheres still stack on on #top, and #bounds.front names the box's face everywhere.

CLI version

  • --version prints the CLI's version. pegmatite --version writes it alone on one line and exits.

1.5.0

Breaking change
pegmatite@1.5.0 ↗

// starts a comment

  • Text from // to the end of the line is a comment. color red // the colour of the cube. Block comments between /* and */ still work, and Ctrl+/ in the editor now toggles // on the selected lines.
  • No floor division. // no longer divides and rounds down; write floor(a / b). The remainder % is unchanged and agrees with it: a is floor(a / b) * b + a % b.

Labels and setters before on

  • A label and one-shot setters may stand before the on of their solid. plate: color red on #front cube 10 is on #front plate: color red cube 10, so a label can start its line. Setters before a face list reach every item; a label cannot stand there, since a face list builds a volume on each face.

Build planes inside braces are named through root

  • Before the first solid of a compound or macro body, on root #front selects its build plane. A bare on #front there is now an error: it read as the front of the volume outside the braces, which on never reaches. To place the whole compound against that face, write on #front { … }. At the top level the bare form still works.

STL export from the CLI

  • -o model.stl writes a binary STL. It holds geometry only, with no colors or part names; use 3MF to keep them.

Slow shells wait to be asked for

  • A shell that needs a slow general computation is left out, not waited for. The CLI builds the solid unhollowed and warns on the shell's line; --slow builds it, which can take minutes. With --strict the warning fails, so a checked model is never one short of a shell.
  • The editor preview goes on past such a shell too, so the volumes after it still show; Render builds the whole model.

Updating older scripts: replace a // b with floor(a / b). Left as it is, the rest of the line becomes a comment. Replace a bare face that begins a compound or macro body, such as { on #front …, with on root #front.

pegmatite@1.4.1 ↗

Chamfers of two distances

  • A chamfer takes an optional second distance. block 40 30 20 | chamfer 3 1 at #top cuts 3 across the top and 1 down the sides. Swap the numbers to flip the cut.
  • Which face takes the first distance: the one that looks further up, then further to the front, then further left. On the bottom ring the walls look further up than the base, so a top ring and a bottom ring cut the same way are written chamfer 3 1 at #top and chamfer 1 3 at #bottom. Each face is checked against the distance that lands on it, and STEP export cuts the same side as the preview.
  • fillet still takes a single radius.

A sign needs no parentheses

  • A - negates any argument, as it does a number. -$inset, -($inset * 2), -sqrt($a), -base.width and -pi no longer need parentheses around them: x -$inset cube 4.
  • The sign must touch what it negates, so - $inset is not an argument. A difference still begins with a command, a macro, a brace or a face, so cube 10 -cube 4 cuts as before.

Measures reach across braces

  • A measure reads a label written outside a compound. base: cube 20 @front linear 5 4 { cylinder 4 base.height } now works in a braced pattern body, a compound or a conditional branch. A size is the same in any frame, so only on stays inside the braces.
  • A label written inside the braces wins over one of the same name outside. A macro body still sees no outside label: pass the size as an argument.

1.4.0

Breaking change
pegmatite@1.4.0 ↗

Edges are selected with at

  • Chamfer and fillet select edges with at. cube 20 | fillet 5 rounds every edge, and cube 20 | chamfer 2 at #top only those of the top face. The selectors are the same as before; only the word in front of them changed.
  • Why: on also begins a statement, so an optional on clause took the on of the next line. cube 50 | fillet 5 followed by on #top cube 40 rounded only the top edges, and the second cube was built on the default reference instead of the top. No statement begins with at, so the clause can stay optional and the next line keeps its meaning. on now only places, and at only selects.
  • No all selector. Every edge is what a chamfer or fillet treats without at, and all and X is just X, so the word had nothing left to do.

Shell opens whatever points up

  • shell takes no face. | shell 2 opens the flat face that points up where the shell stands in the pipeline. After a right-angle turn that is the face now on top: cube 20 | rotate 90 about x | shell 2 opens the former front, where on #top used to fail.
  • Why: a shell has only ever opened its top, so the clause only chose between two names for it, #top and #bounds.top, and it had the same trap with the next line's on. A clause can come back once a shell can open other faces.
  • A label named like an option word, such as round: on the line after | offset 1 or shrink: after a revolve, is now read as a label instead of a syntax error.

Updating older scripts: after chamfer and fillet, drop on all and write at for any other on: | fillet 2 on parallel z becomes | fillet 2 at parallel z. After shell, drop on #top or on #bounds.top.

pegmatite@1.3.1 ↗

CLI check lists placement

  • --check now lists every labelled volume with its size, its span on each axis and its line, grouped by part, so an agent can check where things are without rendering an image. The report goes to stdout instead of stderr. A negative volume that removes no material is a new warning, in the editor too.
  • Fixed STEP export of 2D shapes changed by round or offset, and of shape groups: their curves now export as real arcs and cylinders instead of large faceted surfaces.
pegmatite@1.3.0 ↗

More functions

  • Exponent and logarithm. exp raises e to a value and log gives the natural logarithm; Euler's number is exp(1).
  • Blending values. lerp(from, to, t) gives the point t of the way from one value to the other.
  • Smooth curves. curve passes smoothly through values spaced evenly from a t of 0 to 1, without overshooting, so a varying profile swells and narrows with no formula to work out: % $t circle curve(20, 44, 16, $t) | extrude 60.
  • Shape measures. width(…) and height(…) measure how far a 2D shape reaches along x or y, or across at a given height: width(circle 20, 5). Measured at every level of a sweep, a silhouette sizes a varying profile.
  • Numbers stand apart. A number cannot touch a word: 2pi is an error; write (2 * pi).
pegmatite@1.2.1 ↗

Outlines that change as they rise

  • Twist and size. extrude takes twist and size, which turn and resize the outline about its own origin as it rises: star 6 40 28 | extrude 40 twist 60 size 0.5.
  • Curves through several values. A list gives the value at evenly spaced levels, joined smoothly without overshooting, so a vase swells and narrows again: size [1 1.3 0.85 1].
  • Walls keep their thickness. A walled extrude that twists or grows is drawn with the same thickness at every level.
  • Varying profiles. A shape macro called before a sweep with its last argument left off is drawn anew as the sweep goes, that argument running from 0 to 1: %petal | extrude 40 rises from %petal 0 to %petal 1. It works with every sweep, so a horn, a fading thread or a handle that thins towards its end is one line.
  • Written in place. A varying profile used once needs no name: % $t circle (8 + 12 * $t) | revolve 50 angle 270.
  • Exact in STEP. Twisted and resized extrudes and varying profiles, walled ones included, are written as smooth lofts through their levels.
  • Fast shells. A shell of a twisted, resized or varying extrude is worked out level by level, in a fraction of a second where it used to take minutes, and is exported exactly.
  • Shape macros stay curved. A circle or an arc drawn by a shape macro is exported as a true curve, no longer as the segments the preview draws it with.
pegmatite@1.1.0 ↗

STEP export

  • Export STEP. The export menu writes the model as a STEP file for CAD programs, with its curves exact: a cylinder is a true cylinder and a fillet a true round, not a mesh of facets.
  • Exact almost everywhere. Primitives, extrudes, revolves, sweeps, helices, tapers, walls, lofts between round outlines, fillets, chamfers, shells, uneven scales and booleans all keep their surfaces. What has no exact counterpart yet is written as the flat facets of its mesh.
  • Named and colored. Each body carries the name of its part, or of the model, and the color the script gives it.
  • Checked before you get it. Every solid is compared with the preview's mesh and checked for validity before it is written. A large export shows how far it has got on the button.
  • On the command line too. An output ending in .step or .stp writes STEP. OpenCascade, which writes it, is a 49 MB package the CLI leaves out unless asked for: npx -p pegmatite -p replicad-opencascadejs@1.1.0 pegmatite model.pegm -o model.step.

A clearer view

  • Shaded with edges. The new default view mode draws a solid's creases over its faces in a shade of its own color. Shaded without edges, Translucent and Edges only are one click away.
  • Lighting that keeps its shape. The lights turn with the camera, so a model seen from above or below is lit as well as from the side. Surfaces are matte, so colors stay even without glossy highlights, and a very dark color is drawn a little lighter on screen, so a black solid still shows its form; the script and its exports keep the exact color.
pegmatite@1.0.1 ↗

Clearer errors and smoother lofts

  • Errors name what is missing. A command written without a required argument now reports it by name, as in fillet is missing radius, at the command itself rather than at the end of the line.
  • Twisted lofts are smooth. In extrude … to between outlines of the same number of corners, each corner of the top joins the nearest corner of the base by a straight edge, and a twisted side is a smooth surface instead of zig-zag triangles. A plain taper stays flat.
  • Turning a group of shapes. A group in braces followed by a step only a shape takes, such as { circle 20 - circle 10 } | rotate 45, is drawn as a sketch instead of failing.
  • A polygon has three corners. polygon points now asks for at least three points.
  • The legacy ~ is gone. The ~ older scripts wrote in front of a pattern, read with a warning since 0.0.6, is no longer accepted; delete it.

An editor that keeps up with what you type

  • Unfinished commands preview. A command still missing its arguments is no longer an error while you type: the editor fills them with defaults and draws the result as an orange wireframe.
  • A richer command bar. Commands are grouped into categories with icons, and modifiers, patterns, polygons, tilings and 2D operations each have a menu and an inline hint with a rendered picture. A hint can be picked with the mouse.
  • Keyboard shortcuts. ⌘/Ctrl+/ comments lines out and back, ⌥/Alt+↑ and ⌥/Alt+↓ move them, and ⌘/Ctrl+O, S, D and E open a script, download it, export a 3MF and send the model to slice.
  • Model names. A model carries a name, typed in the editor or taken from its parts, which titles the page and names downloaded and exported files.
  • Slice online. The export button can send the model to EasyPrint to slice in the browser instead of downloading the 3MF, and remembers the choice.
  • Sharing and layout. A share button copies a link to the script, the controls move into a sidebar that works on phones, and the view modes are now called Shaded, Translucent and Edges only. Export, download and share are disabled while the script has an error.
pegmatite@1.0.0 ↗

Profile first: sweeps read the way a part is made

This release changes how every swept solid is written, and scripts written for 0.0.x need updating.

  • The profile comes first. Draw the shape, then pipe it into the sweep: rect 40 30 | round 5 | extrude 10 | fillet 1 on #top replaces extrude 10 rect 40 30 | round 5 | fillet 1 on #top. The same holds for revolve, helix and sweep, and rect 30 30 | extrude 30 to circle 20 now reads from the base to the top.
  • sweep along. A sweep names its route with along: circle 4 | sweep along path "M0,0 H30 V20".
  • The sweep splits the pipeline. Every step before the sweep treats the flat shape and every step after it the solid, so a profile can be turned, mirrored and resized on its own: rect 10 10 | scale 2 | extrude 5 grows the outline and not the height.
  • Shape arguments take braces to be modified. The top of an extrude and the route of a sweep take no modifiers bare; write to { circle 20 | round 2 }.
  • Sketches. A shape with no sweep after it is a sketch: the editor draws it flat where the swept solid would stand, and it builds nothing, takes no slot and leaves the next solid where it was. Picking a shape in the editor writes a sketch, and picking a sweep then turns the sketch at the cursor into a solid.

Macros where they belong and one statement on many faces

  • Definitions anywhere. A def no longer has to open the script; it stands among the statements, next to the variables it uses. A macro is known from its definition on, and two macros can call each other once both are defined.
  • Macros see the variables above them. A body reads the variables set above its definition, with the values they had there, as well as its parameters. A later set does not change what a call builds, and a parameter of the same name stands over the variable.
  • One item per face. List faces after on to build the same statement on each of them: on box [#f2 #f3 #f4] -cylinder 10 3 is the list with one on box #f… item per face. Without a label the faces are those of the volume the statement follows.

Parts: a model printed in pieces

  • part "Lid". A script can now be divided into named parts: the material built after a part, up to the next one, belongs to it. Placement, labels, settings and variables carry on across a part, so a lid is still built on its box. What comes before the first part is a part with no name.
  • One 3MF object per part. Export writes each part as an object of its own, carrying the part's name, even where it touches another part. A script without parts exports as before.
  • Show and hide parts. The editor lists the parts of the model beside the preview, and each can be hidden to look inside the parts around it.
  • Booleans stay in their part. A part is a construction context of its own, so a hole cut in the lid never reaches the box.

A command bar and ghosts in the editor

  • Command bar. The editor's top bar offers solids, modifiers, patterns, anchors and alignment, boolean operations, 2D shapes, 2D modifiers and profile solids, each with a rendered picture. A pick writes the command at the cursor: a solid on a new line, a modifier or sweep onto the pipeline, a pattern, placement or sign in front of the solid there.
  • Ghosts of what the model never shows. The operand of a negative volume and the room a slot holds are drawn as a ghost, in the style of the current view mode. Inside a negative compound each command has a ghost of its own, so the cursor shows just the part it is on. The new Ghosts control draws them always, only for the command under the cursor while editing (the default), or never.
  • Sharper chamfers on many-sided solids. A chamfer or fillet around the rim of a finely faceted solid, such as a prism of many sides, now shades as sharply as the sides beside it.
pegmatite@0.0.8 ↗

2D modifiers, shape groups and edges on any solid

  • 2D modifiers. Treat a flat shape before it is swept: | offset grows or shrinks its outline, | round rounds its corners and | fill fills its holes. They work on every shape, under extrude, revolve, helix and sweep, and come before the solid's own modifiers, as in extrude 10 rect 40 30 | round 5 | fillet 1 on #top.
  • 2D groups. Braces combine shapes into one, placed and combined by the rules solids follow: extrude 2 { circle 20 - circle 10 } makes a washer. Shapes in a group can be anchored, aligned, cut, turned with | rotate, mirrored with | flip and resized with | scale, and a shape macro can name a whole group.
  • Stripes. The new stripes tiling lays parallel slots over an area, for example stripes 4 2 in rect 60 40.
  • Chamfer and fillet on any edge. chamfer and fillet now work on edges that meet at any angle, such as the sides of a taper or the uprights of a hexagonal prism.
  • Labelled faces keep their outline. A face shape read through a label is the face as that statement built it, so a later cut no longer changes it. Label a compound to read the surface left after a cut.
pegmatite@0.0.7 ↗

Fonts, string escapes and free names

  • Choose a font. The new font command draws text in any Google Fonts family, for example font "Roboto" 700, or in a TrueType, OpenType, WOFF or WOFF2 file from an https URL. The command-line tool also reads font files from disk, relative to the script. Like font-size, it applies to the next solid, ! makes it persistent and unset returns to the built-in font.
  • String escapes and multi-line text. Strings may span lines, and a backslash starts an escape: \n, \", \\ and \u{B0} for any Unicode character. Each line of text starts below the one before it. The editor highlights escape sequences.
  • Missing glyphs are reported. A character the font has no glyph for is left out with a warning, instead of being drawn as ?.
  • No reserved words. Any name can be a label, so cube: cube 10 is valid. Only root remains taken.
  • Models stand on the ground. A solid hung below another no longer sinks under the ground plane; the whole model is lifted to rest on it.
pegmatite@0.0.6 ↗

Conditional models and measurements

  • Conditional construction. Use if, else if and else to make parts optional or choose between designs. Tests support comparisons and and, or and not. Use test ? value : otherwise inside expressions to choose a dimension or another value.
  • Measure labelled volumes. Read base.width, base.depth and base.height in expressions to size a part from an existing volume's placement slot.
  • Simpler pattern syntax. Write repeat, linear, circular, grid and mirror directly. The old ~ prefix still works, but now produces a warning.
  • Zero-sized volumes. A zero dimension now creates an empty volume that keeps its placement slot, so dimensions can reach zero without breaking a model. Anchors, transforms and boolean operations handle these empty volumes consistently.
pegmatite@0.0.5 ↗

Consistent pattern compounds and mirrors

  • Braced pattern bodies are compounds. Each iteration fuses and places its body as one volume. Variables, labels and persistent settings inside the braces stay local to that iteration, and boolean operations affect only its own material.
  • Mirrors place halves side by side. The original and reflected copy meet at the mirror plane by default. An optional gap separates them; a negative gap overlaps them. The pair is placed as one volume, including mirrored pairs of holes.
  • Clearer pattern guidance. Updated examples and editor hints explain compound placement, local scope and the mirror gap. A new warning flags placement on the first solid inside a compound when it has no effect.

Updating older scripts: move setters that place an iteration before its opening brace. To cut a compound-shaped hole into surrounding material, negate the whole compound with -{ … }. Use the mirror gap argument to space the halves.

pegmatite@0.0.4 ↗

Lofted extrusions and mirror patterns

  • Extrude between two shapes. The new to option joins a base profile to a different top outline. For example, extrude 30 to circle 20 rect 30 30 transitions from a square base to a round neck. Combine it with wall to make hollow funnels, adapters and ducts.
  • Mirror patterns. Introduced ~ mirror x, ~ mirror y and ~ mirror z to build a body and its reflected copy. Nested mirrors create symmetry across multiple planes, and holes are reflected too.