Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Statements

Every line in an .fsl file starts with a keyword or tier name. You can keep that one-statement-per-line style or add optional semicolons to chain a couple of statements on the same line—the parser treats ; the same way it treats a newline. This chapter explains what each statement does, why you would reach for it, and where it appears in the example at the end.

set – project-wide settings

Use set to update metadata (set name, set ri) or machine configuration (set gear, set girdle). The change applies immediately and carries forward to later lines.

CommandValue syntaxWhat it controls
set name "Brilliant Oval"Quoted stringUpdates the project title used in the viewer, analyzer panels, and printable exports.
set ri 1.760Plain number or optimizable literal (for example [1.76, 1.70, 1.80])Defines the refractive index the renderer, analyzer, and optimizer use for light calculations.
set color #ffd166Hex literal (#fff, #ffaa33), named color (red, cyan, ...).Sets the simulated material color in raytraces.
set absorption 0.6Number from 0.0–1.0Controls how strongly the gem colour tints the interactive WebGL preview.
set gear 96 cwPositive integer followed by optional cw or ccwSets the index gear tooth count and dop rotation direction for every later facet.
set girdle 4.5Number representing a percentage of the stone’s average widthAdjusts how thick the girdle is when interpreting +/- girdle offsets.
set cube 3.0Number interpreted as the cube’s edge lengthRebuilds the starting blank as a cube of the given size. Useful when you need extra “rough” after running out of material mid-cut.

let – save a value for later

let pt = mp(G1) stores a point or number in a variable named pt so you can reuse it further down. Inside macros, the variable disappears when the macro finishes; elsewhere, it is available until the end of the file.

Need a list of numbers? Wrap them in braces to build an array literal: let angles = {41.8, 43.0, 32.2}. Every element can be any numeric expression—including optimizable literals such as [41.8]—and the interpreter remembers the evaluated values at the moment the line runs. Read a value back with zero-based indexing (let pavilion = angles[0]), and the interpreter will flag an error immediately if you stray outside the array’s bounds.

The edge(...) helper is a special case because it always returns two points: the start and end of an edge. Because of this, you must provide two variable names to store these two points. Attempting to assign both points to a single variable, like let edge_ref = edge(...), will result in an error.

The correct syntax provides a variable for each point: let point_a, point_b = edge(...).

This is useful when you need both endpoints of an edge. If you only need one of the points, you can use an underscore (_) to discard the one you don't need. For example:

set name "Edge destructuring"
set gear 96

P1 0 @ 41.8 : 0.18 x8

let start_point, end_point = edge(P1:0, P1:12)
let keep_this_one, _ = edge(P1:12, P1:24)

Those bindings behave like any other point variable—you can aim facets at them, drop debug markers, or pass them into macros.

show – drop a marker (or edge) in the viewer

show mp(P1) drops a marker using the default highlight colour (#ffd166). Add up or down (right after show) if you want to force the side, and optionally supply a colour: either one of the named options (cyan, orange, etc.) or a hex literal such as #ffe422 or #fff. The web studio keeps a small picker next to the editor so you can drop in hex values without memorising them.

Need to highlight a segment instead of a single point? Feed show edge(Tier:Index, Tier:Index) and the viewer/printable exports draw the coloured line. Just like the let statement, show does not accept edge variables directly—call edge(...) inline or reuse the destructured point pair.

debug – log a value or point to the console

debug statements are the quickest way to inspect a calculation without editing macros or sprinkling show markers everywhere. The interpreter prints the evaluated value to the browser/CLI console together with useful metadata such as the current side and symmetry. Examples:

set name "Debug example"
set gear 96

P1 0 @ 41.8 : 0.18 x8

let angle = 41.8

debug mp(P1) "entry point"
debug (deg2rad(angle) - 5.0), "angle offset"
debug ep(mp(P1), mp(P1) at (12), 0.5)

If you omit the label, the log uses a default one. When the value resolves to a number, the interpreter reports the raw value plus convenient conversions (abs, mod360, deg, rad, and gear turns). When the value resolves to a point, it logs the Cartesian coordinates, length, and azimuth. Open your browser’s developer console (or the CLI output when running the interpreter directly) to read the [FSLDbg] … lines.

stop – freeze execution for inspection

Drop stop on its own line to halt the interpreter immediately while keeping everything it has done so far: gemstone geometry, variables, debug markers, and the cut log. It is a handy escape hatch when debugging—pair it with show/debug, inspect the partial stone, and keep experiments below the stop line without running them.

set name "Stop demo"
P1 0 @ 41.8 : 0.18 x8
stop
P2 0 @ 43 : 0.18 x8  // skipped

Anything after stop is parsed but skipped at runtime, even when stop fires inside a macro body.

Macros – reuse a favourite sequence

define wraps statements so you can replay them with a single command. Every parameter is named ($Macro(tier = "C1")), and defaults live either in the header or inline using ${param = ...}. They are an advanced feature, often used for complex gem outlines.

Facet commands – the main event

When a line starts with a tier name (P1, Star, etc.) you are cutting a facet. Read it like a simple recipe:

Tier [up|down] index @ angle : target xN ["note"] frosted

Go left to right:

  • Tier – the label that will appear in the printable diagram.
  • up / down – crown or pavilion. Leave it off when the tier already implies the side (for example, a C tier lives on the crown, a P tier is pavilion).
  • index @ angle – which tooth on the gear and which angle to lock in.
  • : target – how far to cut. This can be a depth / distance (: 0.18), a meet point (: mp(G1)), or the special word size for auto-depth on 90° girdle cuts. The distance is measured between the cutting plane and the center of the stone (0,0,0).
  • xN – how many times to echo the facet around the dop. Use xxN when you want mirror-image pairs (for example, left/right star facets).
  • "note" / frosted – optional add-ons: custom printable text or mark a tier as frosted.

A pavilion example:

P1 0 @ 41.8 : 0.18 x8
  • P1 — name in the legend. The side is auto detected to be the pavilion.
  • 0 @ 41.8 — tooth 0, 41.8° on the dial.
  • : 0.18 — stop when at 0.18 units from the center of teh stone.
  • x8 — repeat eight times (every 45°).

Once you remember the pattern, you only need to choose which target style fits:

  1. Angle + depthP1 0 @ 41.8 : 0.18
  2. Angle + pointC1 0 @ 32 : mp(G1) . Aim the facet at a meet point or helper.
  3. Point pairC2 0 : mp(G1) : mp(C2). Define the plane using two points instead of an angle.

Tier naming, sides, and base index rules

Every tier label must be a single string with no whitespace inside it, and it has to start with an alphanumeric character. Reserved keywords (set, let, show, etc.) are off limits as tier names. The optional up/down flag tells the interpreter which side of the stone to use, but most of the time the engine can infer it:

  • Names that start with P default to the pavilion.
  • Names that start with C default to the crown.
  • If you omit the side and the previous cut specified one, that side carries forward.

That’s why a table named T typically doesn’t need up—it inherits the current crown context. You can always override the inference by writing the side explicitly.

The base_index argument is zero-based and must stay below gear / symmetry. Until you dig into the Symmetry section later in this guide, use this mental model:

  • With a 96 index gear and 4-fold symmetry, the valid base index range is 0 .. (96 / 4 - 1)0 .. 23.
  • You can also provide an explicit index set using an array literal, for example {4, 7, 11}, in place of the single base index. When you do that, symmetry is ignored entirely and the interpreter cuts exactly the indices in the set, in the order given. The first element is treated as the “base” index for meetpoint/girdle searches.

Some quick examples:

set name "Tier naming rules"
set gear 96

P1 1 @ 41.4 : 0.18 x8      // Pavilion cut using base index 1
G2 1 @ 90 : size x8        // Girdle tier; inherits the pavilion side from P1
C1 up 3 @ 32 : 0.12 x8     // Crown cut using base index 3
P2 {4, 7, 11} @ 43 : gp    // Explicit index set; symmetry skipped, base index is 4 for mp/gp

Need to force a non-standard label onto the pavilion? Drop down right after the tier name—X8 down 5 @ 44 : 0.10 x2 guarantees the interpreter stays on the lower side even if the previous cut was on the crown.