Preface
ProFacet was created for cutters who want to understand every facet of their craft. That vision led to FSL—a new faceting specification language shaped by practical experience and refined through collaboration with expert designers and gem cutters.
Standard Faceting diagrams show you the result of the design, but not the path to get there or how to adapt when things change. It’s like the difference between a photo of a cake and the recipe behind it. FSL gives you the recipe—the structure and freedom to recreate, adjust, and invent.
This image is made in ProFacet using the upcoming path tracer. Explore more designs in our Gallery.
The interactive slicer in ProFacet is your primary tool. Drag facets, adjust girdles, and watch the studio write FSL in real time. For most designs you never need to type a command, yet the language is always there when a peculiar cut or unconventional meet point demands it. That balance keeps experimentation fun without hiding the machinery that makes a stone reproducible.
When it is time to push performance, the Optimizer takes over. It speaks FSL natively, so you can tag any parameter and let the engine chase brightness, contrast, and scintillation using a modern metaheuristic evolutionary search. Under the hood, ultra-optimized GPU algorithms evaluate candidates faster than a human can pour water into a drip tank, but the controls stay simple: pick targets that matter, set sensible bounds, and let the system converge.
Contact
If you have questions, feedback, or need support, reach out to us at jon@profacet.com.
Timeline
- Test Phase: now through 1 Mar 2026—iterate, gather feedback, and refine the tools and documentation while everything is still in flux.
- Contest Window: 1 Mar – 1 May 2026—run the launch performance contest and keep the Hobbyist tier free so every cutter can participate.
- Paid Phase: begins 1 May 2026—introduce billing for the Analyzer, Optimizer, Pathtracer and Cloud Sync. The "offline" modus without these tools will stay free to use.
Don't be discouraged by the apparent complexity of FSL. Those advanced constructs are there to support complicated, parametrized girdle outlines (like ovals) or intricate patterns like checkerboards. Most designs never use any of that. FSL allows you to describe any faceted convex shape, and that power comes with some inherent complexity.
Quick Start: Interactive Slicer
This quick start focuses on learning the Interactive Slicer by walking through the first pavilion tier, girdle, crown, and table. Keep the studio open in another tab so you can follow along.
Tip: The studio autosaves in your browser. Close the tab and come back later—your last design reopens automatically.
1. Open the studio and click New
Launching the studio drops you into the 3D stage with the editor below. Hit New to load the blank template:
You might need to zoom (scroll wheel or pinch) to see the entire cube. This is the starting stock we’ll carve away.
2. Set up the first pavilion tier
Open the Interactive Slicer panel:
- Choose Down for the side (we normally begin on the pavilion).
- Set Symmetry to
8. - Enter Angle
42.00°and Base Index0. - Pick Center Point (
cp) as the target.
The slicing plane animates into view. Hovering over Preview shows a preview of the cut.
3. Commit the cut
Press Commit to apply the tier. The editor now contains the first line of FSL and the viewer redraws:
Rotate the viewer and you’ll see the pavilion facets meeting at the center. More steps will follow, but getting comfortable with this rhythm—choose parameters, preview, commit—is the foundation for building complete stones.
4. Establish the girdle depth
Next, set up the girdle ring:
- Keep Side on Down, but change the Angle to
90.00°. - Switch the target to Depth and enter
1.00for the value. - Leave symmetry at
8and base index at0, then click Commit again.
You now have the pavilion facets and the girdle defined—a solid foundation before working through additional tiers.
5. Cut the first crown tier
Now flip to the crown side:
- Change Side to Up.
- Set Angle
25.00°, Symmetry8, and keep Base Index0. - Pick Proper Meetpoint as the target. The slicer will highlight multiple nodes—click the meetpoint where the girdle and pavilion facets touch.
- Check Girdle Offset so the cut keeps the girdle at the right thickness.
- Hit Commit to apply the crown tier.
At this point the pavilion, girdle, and first crown tier are in place.
6. Add the table
Finish the crown by cutting the table:
- Keep Side on Up but change the Angle to
0.00°. - Select Depth as the target and drag the slider (or type) to your preferred table height.
- Press Commit to flatten the top.
You now have a basic pavilion, girdle, crown, and table—enough to explore the diagram (printable instructions) or continue refining each tier.
What’s next?
- Open the Diagrams panel to generate printable SVGs once you finish a few tiers.
- Explore the Interactive Slicer guide in Tooling → Interactive Slicer for advanced targets and meetpoints.
Workspaces & Tools
ProFacet puts every workflow in the browser. These chapters explain what each panel does, why you might use it, and how the pieces work together.
- Web Studio - tour the editor, diagnostics, camera controls, and printable instructions.
- Analyzer - peek under the hood of the light-return charts and learn how to read them.
- Optimizer - understand the sliders, presets, and progress meters before you launch a run.
- Interactive Slicer - preview a candidate facet, inspect meetpoints, and drop the generated command into the editor.
- Symmetry Assistant - define and discover symmetrical patterns with forward and reverse modes.
- Cloud Sync & Accounts - optional online capabilities for sharing designs across devices.
Browse in whatever order fits your session. Every walkthrough assumes you are using the live app and focuses on what you will see and click there.
Web Studio
Launch the browser studio (see Start Here) to get an editor, 3D viewer, printable instructions, and performance tools in a single workspace. Nothing to install - everything runs locally in your browser.
Editing workflow
- The studio auto-saves shortly after you stop typing, keeping a private copy of every design in your browser.
- Use Open... to browse your saved designs, restore deleted entries, or export a backup. New loads the default template.
- Errors and warnings appear directly under the editor with line numbers. Fix them before running the Analyzer or Optimizer - the performance tools stay locked until the last run succeeds.
Navigating the 3D viewer
- Rotate with a left-button drag (or a single-finger drag on touch).
- Pan with the right or middle button; on touch devices use a two-finger drag.
- Zoom with the mouse wheel or a pinch gesture. Double-click the canvas to reset the default view.
- Use the toolbar buttons to toggle overlays, cycle view modes (Monochrome -> Palette -> Raytraced), reveal wireframe edges, display debug points, flip between crown and pavilion, or show the angle guide.
Interactive Slicing mirrors your current design: the panel slices the model, shows meetpoint indices, and reacts to Control/Cmd held down while you move the mouse for quick previews.
Printable instructions
Open Diagram to generate the same packet your printer will see:
- Pick a theme and image size (Small or Large) without leaving the studio.
- Toggle Compact to tighten table spacing, and have crown and pavilion side by side.
- Click Print to switch into a browser print preview; the rest of the UI hides automatically.
Performance tools
- Analyzer simulates 19 tilt positions on the GPU (see Analyzer for details). The panel stays disabled until the current design processes without errors and WebGPU is available.
- Optimizer adjusts every bracketed value, reuses the Analyzer scores, and keeps the best candidate ready for comparison (see Optimizer).
Both panels share the same analyzer engine, so once WebGPU initialises successfully the buttons unlock together.
Analyzer
Before you start
- Process the design first so the interpreter, viewer, and Analyzer are working on the same geometry.
- Clear every FSL error. The Analyzer is disabled until the last run finished without errors.
- The studio needs WebGPU and the Analyzer feature on your account.
Run an analysis
- Open a design and click Analyzer in the side bar.
- When the run finishes the metrics, charts, and timings update immediately.
Understand the results
- Metric cards list Average Brightness, Contrast Density, Contrast Intensity, Scintillation Score, and Compactness. Each value is shown as a percentage relative to the Hearts & Arrows brilliant reference that ships with the studio.
- Average Brightness is the mean return light over every valid pixel in the sweep.
- Contrast Density measures the amount of contrast areas visible on the stone. It describes the spatial distribution of light and dark zones.
- Contrast Intensity measures the strength of the contrast in areas where it exists.
- Scintillation Score counts blink events between angles and normalises them by the number of pixels and tilt steps.
- Compactness comes from the current mesh and reports how much of the stone's footprint-and-height envelope is filled by the actual volume.
- Shannon Entropy New! measures the information content or "richness" of the light distribution. A higher entropy score means the stone returns a complex, varied range of light levels—avoiding "flat" or monotone appearances.
- Per-angle sparklines ("Brightness by Angle", "Contrast Density", "Contrast Intensity") plot the same metrics for each of the 19 tilt angles so you can see where the stone rises or falls off.
- Lighting sweep renders each frame with cosine-weighted lighting and a standard head shadow. The 19 tilt positions start 45° down on the X axis, move through the upright view, and finish 45° toward the Y axis. Step sizes tighten near upright so more samples are taken where the stone spends most of its time, making every average a weighted view of the sweep.
Try it now
Paste the snippet below, process the design, and run the Analyzer to watch how tiny angle tweaks change the sparkline profiles.
set name "Analyzer Demo"
set gear 96
set girdle 4.5
P1 0 @ 41.8 : 0.18 x8
P2 6 @ 39.6 : mp(P1) x8
G1 0 @ 90 : size x16
C1 0 @ 32.2 : mp(G1,P1) + girdle x16
T 0 @ 0 : 0.86
Optimizer
The Optimizer is the cornerstone of the ProFacet workflow and the feature that truly distinguishes it from other design tools. Uniquely enabled by the Facet Specification Language (FSL), it allows you to move beyond manual iteration and mathematically refine your design for peak performance.
By simply bracketing values within your FSL script, you define a search space for the engine to explore. The Optimizer then leverages WebGPU acceleration to evaluate thousands of variations in real-time, scoring each one against professional-grade metrics for brightness, contrast, and scintillation. It is a seamless, powerful bridge between your creative intent and optical perfection.
Prepare adjustable values
- Wrap any numeric literal you want to expose with square brackets:
[ value ], or[ value, min, max ]. minandmaxare optional. When you leave them out the Optimizer uses a +/-10% window around the starting value.- Brackets work anywhere the interpreter accepts a literal:
setstatements, facet commands, macro arguments, and expressions.
Example block ready for optimization:
set name "Optimizer Demo"
set gear 96
P1 0 @ [41.0, 40.0, 42.5] : 0.18 x8
P2 6 @ [39.5, 38.0, 41.0] : mp(P1)
P3 12 @ 38.2 : 0.16
G1 0 @ 90 : size x16
C1 0 @ [32.0, 30.0, 33.0] : mp(G1,P1) + girdle
T 0 @ 0 : .9
If no brackets are present the engine returns "No optimizable parameters found" and stays idle.
Choose your weights
- Open the Optimizer panel with Open Optimizer.
- Pick a preset to load weights for Average Brightness, Contrast Density, Contrast Intensity, Scintillation Score, and Compactness. The built-in presets are Balanced Default, Brilliance Boost, Contrast Focus, Precision Cut, and 2026 Launch Contest.
- Adjust any slider between 0 and 10. Press Save Preset to store the current mix; the studio keeps a local copy and syncs it to your account when cloud features are available.
Launch and monitor a run
- Process the design and clear every FSL error. The Optimizer checks the same run status as the Analyzer and will show the reason if it stays locked.
- Press Start Optimization. The button switches to "Optimization Running", the cancel button becomes Stop & Keep Best.
- The progress panel lists the current status, iteration, best score (calculated from your weights on Hearts & Arrows-normalised metrics), analyser throughput in pixels per second, and metric deltas versus the starting design.
- Stopping keeps the strongest candidate so far and turns the start button into Resume Optimization; you can pick up right where you left off without losing that snapshot.
- When you resume after stopping, the percent deltas still reference the very first score from that session—stopping does not reset the baseline. If you apply or save the optimized version to the editor, the next run treats that updated design as the new baseline.
Behind the scenes the Optimizer explores nearby combinations automatically and never strays outside the min and max you set.
Compare and apply
- As soon as a valid candidate exists the comparison panel opens with side-by-side viewports labeled Original and Optimized plus a contribution table that shows how each metric moved.
- The Replace Editor with Optimized button swaps the editor contents with the current best parameters. Save Optimized as New Version writes a revision while preserving the original file.
- Apply actions stay hidden until you stop a run. Resume after applying if you want to explore further from the updated baseline.
- Stopping caches the best parameters and the best score. Hitting Resume seeds the next run with that candidate so the population starts from where you left off instead of restarting from the original design. If you apply/save the optimized design first, the next run treats that new file as the baseline.
Interactive Slicer
The Interactive Slicer panel (the “Interactive Slicing” column under the viewer) lets you preview a candidate facet, inspect the exact meetpoints it would hit, and then paste the generated FSL command into the editor with one click. It is powered by the same renderer and meetpoint pipeline that the studio uses during normal execution, so the preview reflects the current geometry, symmetry, and girdle settings.
Panel layout

- Side – choose
Up(crown) orDown(pavilion). The side to cut. - Symmetry / Mirror – set the repeat count (
xN) and whether to emit mirrored pairs (xxN). The slicer syncs the starting value from the current machine snapshot; it only appendsx/xxto the preview command when you change it. - Target – selects what the facet should touch:
None (hide preview)disables all overlays and buttons.Center pointusescp(the centre axis) and asks ProFacet for the exact centre when context is available.Girdlereturns the girdle contact at the current index/angle using thegphelper.Proper meetpointpulls clustered meetpoints fromlistMeetpoints; use ◀/▶ to cycle through candidates.Depthexposes a depth slider (0–1.00) for classic angle + depth cuts.
- Angle / Index – slider + number pairs that map directly to the machine angle (0–90°) and index (0–gear). Drag the slider for coarse changes, or type numbers for precise edits. While the pointer is over a slider, hold
Ctrl(Cmdon macOS) to activate fine-grained mode. - Girdle offset – when the target is
cp,gp, orProper meetpoint, adds+girdleon the crown or-girdleon the pavilion automatically.
Below the controls you will see:
- A meetpoint navigator (visible only for
Proper meetpoint). Each candidate shows its label, order, and whether it is a fallback. When the selected point lies on the girdle, the preview command switches togp. - A live preview string that mirrors the facet command that would be added to the editor.
- The floating Preview badge, plus Cancel and Commit buttons.

Preview behaviour
- Dragging the angle or index slider, hovering the Preview badge, or holding
Ctrl/Cmdkeeps the preview active. - The viewer shows:
- A plane overlay and cap polygon sliced through the current mesh.
- Markers for the selected meetpoint (and neighbouring candidates), the current
cporgp, and any girdle candidate provided by ProFacet.- The angle guide if it is enabled in the viewer.
- When you release the slider or let go of the modifier key, the preview snaps back to the base mesh.
Committing or cancelling
- Commit appends the preview line to the editor, choosing the next sequential tier name (
P,C, orGbased on side/angle), and reruns the interpreter so the viewer stays in sync. - Cancel clears the preview state and resets the target to
None.
Meetpoints
Proper meetpointfilters out fallback candidates and anything that falls on the opposite side of the plane. The panel remembers your selection as you change the angle or index.
Symmetry and warnings
- The symmetry count must divide the machine gear. If it does not (or if the index exceeds
gear / symmetry), the input fields highlight in red and a warning appears above the preview string. - Mirroring uses
xxand produces left/right pairs, matching the way the runtime interpretsxxN.
Tips
- Precision mode (
Ctrl/Cmdwhile the pointer is over a slider) lets you make sub-degree or sub-index adjustments without typing numbers. - Hold
Ctrl/Cmdwhile tapping arrow keys in the numeric boxes to keep the preview active. - The preview command already includes any
+girdle, symmetry, or meetpoint specifier adjustments, so the inserted line is ready to run as-is. You can still edit it manually in the editor after committing if you need to tweak the tier name or add notes.
Symmetry Assistant
The Symmetry Assistant is a tool designed to help you define and discover symmetrical patterns for facet indexing. It provides two primary modes of operation: Forward and Reverse.
- Forward Mode allows you to specify symmetry parameters (symmetry count, index, and whether it's mirrored) and see the full list of resulting facet indices. This is useful when you know the symmetry you want to apply and need to visualize the result.
- Reverse Mode is a powerful predictive tool. You can type a few indices from a sequence, and the assistant will deduce the underlying symmetry parameters, complete the sequence for you, and even suggest alternative interpretations. This is invaluable when you have a partial pattern in mind and want to establish the full symmetrical loop.
Accessing the Assistant
The Symmetry Assistant can be opened from the user interface:
From the Interactive Slicer: A small "?" button appears next to the "Mirrored" checkbox in the main symmetry controls. Clicking this will open the assistant, pre-filled with the current settings from the widget.

When opened, the assistant syncs with the slicer's current gear count.
Core Concepts
Before diving into the modes, let's define some key terms:
- Gear: The total number of available index positions on the gear (e.g., 96 for a standard faceting machine).
- Symmetry: The number of times a pattern repeats around the gear. The
gearcount must be perfectly divisible by thesymmetrynumber. - Index: The base or starting index from which a symmetrical pattern is generated.
- Mirrored: If enabled, the pattern is reflected across the 0-axis. For every generated index
p, its negative counterpart-p(modulo the gear size) is also included. - Step: The distance between each symmetrical index. It is calculated as
Gear / Symmetry. - Base Offset: The effective starting index of the pattern, calculated as
Index % Step. - Indices: The final, sorted list of unique index positions generated from the symmetry parameters.
Forward Mode
Forward mode is for expanding a known symmetry into a full set of indices.
How to Use
- Open the Assistant: It will default to Forward mode.
- Enter Parameters:
- Index: Set the base index for the calculation.
- Symmetry: Set the desired symmetry count. This must be a divisor of the current gear count.
- Mirrored: Check this box to create a mirrored pattern.
- Review the Output:
- The assistant immediately calculates and displays the full list of
Indices. - Below the inputs, metadata is shown, including the calculated
step,symmetry,mirroredstatus, andbase offset. - If there's an error (e.g., "Symmetry must divide the gear"), a message will appear.
- The assistant immediately calculates and displays the full list of
- Apply the Symmetry:
- Click "Send to slicer" to apply the generated symmetry to the main application. The assistant will close, and the slicer's symmetry settings will be updated.
Example
With a gear of 96:
Index: 2Symmetry: 8Mirrored: false
The assistant calculates a step of 96 / 8 = 12. It generates the indices: 2, 14, 26, 38, 50, 62, 74, 86.
Reverse Mode
Reverse mode is for discovering symmetry parameters from a partial sequence of indices. It's like asking the assistant, "What symmetry creates a pattern that starts like this?"
How to Use
- Switch to Reverse Mode: Click the "Reverse" tab at the top.
- Enter a Prefix:
- In the "Indices prefix" field, start typing the first few indices of your desired pattern, separated by commas or spaces (e.g.,
2, 10). The indices must be in ascending order.
- In the "Indices prefix" field, start typing the first few indices of your desired pattern, separated by commas or spaces (e.g.,
- Review the Prediction:
- As you type, the assistant analyzes the prefix and finds the best-fitting symmetry parameters.
- Ghost Text: The input field shows a "ghost" completion of the full sequence based on the best prediction.
- Completion: The "Completion" box displays the full list of indices. The numbers you typed are locked, while the predicted numbers are clickable, allowing you to add them to your prefix.
- Next Number Chips: Below the input, clickable "chips" suggest the most likely next numbers to add to your sequence. This is useful for exploring different symmetry possibilities.
- Metadata: The predicted
step,symmetry,mirroredstatus, andbase offsetare displayed.
- Explore Alternatives:
- If your prefix can be interpreted in multiple ways, a section titled "Other interpretations" will appear.
- You can expand this section to see alternative symmetry solutions that also match your prefix. Clicking on an alternative's index list will preview it as the main solution.
- Apply the Symmetry:
- Once you are satisfied with a predicted sequence, click "Send to slicer". This applies the selected symmetry parameters to the main application.
Example
With a gear of 96, you type 2, 10 into the prefix field.
-
The assistant immediately predicts a mirrored symmetry of 8.
- Parameters:
step=12,symmetry=8,mirrored=true,base=2. - Completion: It shows the full sequence:
2, 10, 14, 22, 26, 34, 38, 46, 50, 58, 62, 70, 74, 82, 86, 94. - Ghost Text: Your input of
2, 10is followed by the ghost text, 14, 22, .... - Next Chips: It might suggest
14as the next logical number.
- Parameters:
-
At the same time, it might find an alternative: a non-mirrored symmetry of 12.
- Parameters:
step=8,symmetry=12,mirrored=false,base=2. - Completion:
2, 10, 18, 26, 34, 42, 50, 58, 66, 74, 82, 90. - This will be listed under "Other interpretations".
- Parameters:
Scoring Model
The score is a weighted sum of five well-defined metrics. Contrast density and contrast intensity combine into a single contrast term because strong contrast requires both; the remaining terms are linear for clarity.
What goes in
- Metrics — Average Brightness, Contrast Density, Contrast Intensity, Scintillation Score, Compactness, and Shannon Entropy. Each is compared to the Hearts & Arrows reference; values above 1.0 mean “better than the reference”.
- Weights — The six sliders (
averageBrightness,contrastDensity,contrastIntensity,scintillationScore,compactness,shannonEntropy). Weights are always treated as zero or higher.
How the score is built
-
Normalize the metrics
Everything is turned into a ratio vs the reference. If a metric is better than the reference, we keep it above 1.0 but gently squash it so runaway values don’t dominate. -
Make contrast a single value
Contrast density and intensity are blended with a geometric mix (think “both must be good, one can’t carry the other”). If both contrast sliders are equal, this behaves like the square root of (density × intensity). If one slider is higher, that side is favored—but a weak partner still drags the contrast term down. -
Set the overall weights
The two contrast sliders also decide how important contrast is overall: their average becomes the contrast weight. Brightness, scintillation, compactness, and contrast weights are then scaled so they always sum to 1 (extras are included the same way). -
Take the weighted sum
Final score = brightness part + contrast part + scintillation part + compactness part. Higher numbers mean closer to, or better than, the reference cut.
Notes
- The contest verifier, optimizer, and UI all share this exact formula, so what you see is what gets verified.
Cloud Sync & Accounts
ProFacet stores every edit in your browser first for speed and data privacy. Cloud sync builds on that foundation: when you sign in, the studio mirrors those designs to your account so you can pick up the same work on another machine. This chapter explains what stays local, what changes after authentication, and how to keep multiple browsers aligned.
Local storage and manual backups
- The studio auto-saves every design into your browser's private cache, keeping edits fast and under your control unless you choose to sync.
- Browsers may automatically purge site data when disk space runs low. Treat IndexedDB and local storage as a cache, not your only archive.
- Open Design -> Backup exports the entire library (active and deleted designs) as a JSON file. Restoring that file walks you through conflicts: you can overwrite locals with the backup or keep whichever version has the newest change.
- Deleted entries stay two weeks in the dialog's Deleted filter until you purge them or restore them, so accidental removals are easy to undo.
- Clearing site data empties the cache. Export a backup first if you plan to reset the browser.
- Always keep multiple backups (cloud sync, JSON export, external drive) so you can recover if either the browser or the device has a bad day.
Signing in unlocks cloud sync
Click the Sign In badge in the editor header to open the account menu. Depending on the deployment, cloud features may be disabled entirely - in that case the badge simply reads Offline and behaves like a label.
When cloud sync is available, authenticating adds:
- Automatic backups - the latest copy of every design is stored alongside your account.
- Cross-device libraries - any browser that signs in with the same account receives the same portfolio. Remote designs win when they have a newer modification timestamp; otherwise your local edits stay in place.
- Preset sync - user-created optimizer presets are uploaded alongside designs, so the Optimizer panel shows the same list everywhere.
- Account controls - the menu offers Account Overview, Sync Now, Resend verification email (when needed), Delete Account, and Sign Out. Signed-out users get a one-click option to authenticate.
How syncing behaves
- Initial merge - the first sign-in pulls any remote snapshot, combines it with your local designs (newer changes win), saves the result back to the browser, and, if nothing is open, loads the most recently edited piece.
- Deferred uploads - after you edit a design, the badge notes that changes are waiting. About 45 seconds after you stop typing, the studio pushes the updated library so quick tweaks batch together.
- Interval syncs - while you are signed in, the studio also refreshes your cloud copy roughly every 10 minutes in the background.
- Manual Sync Now - choose Sync Now from the profile menu to force an immediate round-trip before you close a laptop or switch machines. The menu closes once the request completes.
- Optimizer presets - the presets you create travel with your account, so the Optimizer panel shows the same list wherever you sign in.
If the network is unavailable, sync retries later and your edits remain in IndexedDB. The spinner disappears once the controller falls back to idle.
Troubleshooting
- Designs are missing after signing in - open the profile menu and run Sync Now once to trigger a merge. If the designs still do not appear, check the browser console for
[cloud-sync]errors. - Contest submission says email unverified - use the menu's Resend verification email action and complete the link in your inbox, then try again.
- Preparing to wipe a device - export a JSON backup first (
Open Design -> Backup). You can restore that file on the fresh install before or after you sign in.
Shared Links & Scratch Buffers
The editor can encode the full FSL buffer inside a URL query parameter. Without this feature you have to select the entire .fsl file, copy it into a chat or e‑mail, have the receiver open ProFacet, create a blank design, paste, and hope nothing was truncated. With shared links you hand someone a single URL that opens the design directly. This page explains how the feature works, what you should expect to see when you open one of these links, and how to promote (or dismiss) the scratch buffer.
Creating a Share Link
- Make sure the editor contains the design you want to send. The link always captures the current buffer, not the last saved design.
- Click Share in the upper toolbar. This renders a Share Link panel that contains a read‑only URL and copies the same URL to your clipboard.
- Send the link however you prefer (chat, e‑mail, ticket, etc.). Anyone who opens the URL will see the exact FSL you shared—no clipboard gymnastics required.
Behind the scenes the app serializes the buffer plus a few metadata fields (name, timestamp) into the ?fsl= search parameter. No server round‑trip is required and nothing leaves the browser until you share the link yourself.
Opening a Shared Link
When a user opens a link that contains ?fsl=…, the workspace switches to a scratch buffer:
- The editor is populated with the shared source but is marked read‑only. Attempting to type, run the optimizer, or open files will highlight a warning banner.
- Toolbar actions that would mutate the buffer (
New,Open,Process, optimizer start/apply buttons, interactive slicer edits, keyboard shortcuts, etc.) are temporarily disabled. - A yellow banner explains that you are viewing a read‑only shared design, and Copy to Library and Cancel buttons become visible next to Share.
This safeguard prevents accidental edits that could overwrite your own work before you have consciously adopted the shared design.
Copying to Your Library
Use Copy to Library whenever you want to keep working on the shared design:
- Click the button in the toolbar. The UI shows a short “Copying…” state while it persists the design locally.
- On success the app exits scratch mode, removes the
?fsl=parameter, loads the newly saved design record, and re‑enables all controls (Open,New, Process, optimizer actions, interactive slicer, etc.). - The saved design inherits the metadata captured in the shared buffer. You can rename it, continue editing, or delete it like any other local design. The Copy/Cancel buttons hide because you are now editing your own version.
If the save fails (for example, IndexedDB is disabled) the workspace stays in scratch mode and an error message explains that the copy did not complete. You can attempt the copy again after resolving the problem.
Canceling a Shared Session
Not ready to adopt the design? Hit Cancel instead. The workspace drops the ?fsl= parameter, clears the scratch buffer from the editor, reloads your normal workspace (remembered design, latest file, etc.), and removes the scratch banner without saving anything from the shared link.
Tips & Limitations
- Shared links are entirely client‑side. Anyone with the link can read the raw FSL in the URL, so avoid posting sensitive work to public channels.
- Very large buffers will also result in long URLs. Most browsers handle this well, but if you frequently share complex designs you may prefer to zip the
.fslfile instead. - After you copy the design into your library you can re‑share it at any time—the Share button always reflects the current buffer, even if the design originated from another shared link.
This workflow removes the tedious “copy, alt‑tab, new, paste” exchange and still gives you an explicit “copy or cancel” decision before modifying the shared design.
FSL Tour
The Faceting Specification Language (FSL) is the handful of keywords you type inside the studio to describe a cut, the toolbox you fall back on when the interactive slicer runs out of road. Most designs come together entirely in that panel, but intricate meetpoint choreography or exotic girdles go faster when you edit the FSL source directly. Once you get comfortable, you may find yourself jumping straight into the editor—experienced cutters often do because it is the quickest way to iterate.
Work through the chapters in order or jump to the one you need:
- Core Concepts explains how the studio reads an
.fslfile, what “up” and “down” mean, and how state carries from line to line. - Statements shows the building blocks—
set,let, facet commands, macros—each with short examples you can paste directly into the studio. - Expressions covers numbers, point helpers, and the handy math functions you will reach for when lining up meet points.
- Macros & Reuse teaches you how to use this advanced feature to package repeated steps, especially for complex gem outlines.
- Facets and Tiers connects statement syntax to what actually happens to the stone.
The snippets are arranged so you can copy them into a new file, hit Process, and see the result immediately.
Core Concepts
Before we dive into individual commands, it helps to understand how the studio reads an .fsl file. Think of the interpreter as a very patient cutter: it reads a line, applies the change, then moves to the next line without skipping ahead or rearranging anything.
How the studio reads your file
- The file is a simple list of statements. Each line happens in the order you write it.
- Blank lines and comments are ignored, so feel free to add notes with
//whenever it helps future-you.
Here is a tiny program you can paste into the studio to see the order in action:
set name "Order Matters"
P 0 @ 45 : cp x 4
G 0 @ 90 : size
C 0 @ 28 : mp(G, P) +girdle // Depends on G and P already existing
T 0 @ 0 : 0.2
The crown (C) tier uses a meet point created by the P and G, so the line with the crown tier must come after P and G.
What the studio remembers for you
While it reads the file, the studio tracks a handful of details in the background:
- Stone metadata –
set name,set ri, and friends. - Machine setup –
set gear 96 cwsticks until you change it, so every later facet uses the same gear and rotation. - Variables –
letstatements store numbers or points that you can reuse later. - Last side and symmetry – if you cut a pavilion facet (
down) and the next line starts with a tier name, the interpreter keeps usingdownunless you say otherwise. Same story for symmetry; once you typex8, the next cut command will also use that symmetry unless you change the modifier.
If anything goes wrong—maybe a meet point cannot be found—the interpreter stops right there and the diagnostics panel shows the line and message.
Crown versus pavilion
downfacets belong to the pavilion and tilt toward negative Z.upfacets belong to the crown and tilt toward positive Z.- Tier labels are just names.
P1,C1,Break, andStarall work.
Symmetry in plain language
Add xN after a facet command to copy it around the stone every 360 / N degrees. Use xxN when you want mirrored N-fold symmetry (each pair becomes a left/right mirror). The count must be a positive whole number:
P3 12 @ 38.2 : 0.16 x 8 // eight pavilion mains
G1 0 @ 90 : size x 16 // sixteen girdle facets (90°)
There is no need to specify the symmetry for the next tier if there is no change in symmetry.
If you are cutting something asymmetric, you can skip symmetry entirely by spelling out every index in braces: P1 {3, 27, 44} @ 90 : size hits those three teeth in order and stops. Because you already provided the full list, you do not add an x 4 (or any other symmetry modifier) on that line. The first number in that brace list still serves as the base index for helpers such as mp()/gp().
Numbers, points, and edges
Most expressions boil down to one of three value types:
- Number – plain angles, depths, or calculations like
deg2rad(32). - Point – created with helpers such as
mp(P1),gp(G1),pt(0, 0, 1), orfred(...). These mark locations in space, whether pulled from existing facets or stated outright. - Edge – less common but handy for advanced macros, created with
edge(tier:index, tier:index).
You can store numbers or points in variables. Edge helpers always produce two points, so capture them with a destructuring assignment instead of storing the edge itself:
let pt1 = mp(P1)
let safety = 0.05
let first, second = edge(P1:0, P1:12)
C2 up 0 @ 29.5 : pt1 + girdle x16
Variables live until the end of the file (or until a macro finishes, if you are inside one).
With these basics in mind, continue to Statements to see every command you can type.
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.
| Command | Value syntax | What it controls |
|---|---|---|
set name "Brilliant Oval" | Quoted string | Updates the project title used in the viewer, analyzer panels, and printable exports. |
set ri 1.760 | Plain 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 #ffd166 | Hex literal (#fff, #ffaa33), named color (red, cyan, ...). | Sets the simulated material color in raytraces. |
set absorption 0.6 | Number from 0.0–1.0 | Controls how strongly the gem colour tints the interactive WebGL preview. |
set gear 96 cw | Positive integer followed by optional cw or ccw | Sets the index gear tooth count and dop rotation direction for every later facet. |
set girdle 4.5 | Number representing a percentage of the stone’s average width | Adjusts how thick the girdle is when interpreting +/- girdle offsets. |
set cube 3.0 | Number interpreted as the cube’s edge length | Rebuilds 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
Ctier lives on the crown, aPtier 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 wordsizefor 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
xxNwhen 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:
- Angle + depth —
P1 0 @ 41.8 : 0.18 - Angle + point —
C1 0 @ 32 : mp(G1). Aim the facet at a meet point or helper. - Point pair —
C2 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
Pdefault to the pavilion. - Names that start with
Cdefault 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.
Expressions
Note: Most designs never touch custom expressions. Skip this page unless you are building CAM outline macros or a highly specialised cut that needs custom math.
Expressions are the pieces that fit inside statements: numbers, point helpers, comparisons, and function calls.
Numbers, booleans, and strings
- Numbers accept decimals and scientific notation (
42,0.18,1.5e-2). - Booleans are just
trueandfalse. - Strings live inside double quotes and support escapes like
\"and\n. You will mostly see them inset nameor macro bodies.
Optimizable numbers use square brackets—[41.8, 38, 44]—to record a starting value and optional minimum/maximum bounds.
Arrays and indexing
Braces ({ and }) create an array literal that captures a list of numeric expressions evaluated right away. Example: let tiers = { 41.8, 32.5, [28.0] }. Arrays are zero-based, so tiers[0] returns the first entry, tiers[1] the next, and so on. Every access is bounds-checked—tiers[3] would halt the interpreter if the array only stored three items—so you get fast feedback when a loop walks too far.
Handy functions
Call functions by name with parentheses. The most common helpers are:
sin,cos,tan,asin,acos,atan,atan2deg2rad,rad2degsqrt,abs,log,log10,floor,round
Geometry Functions
These functions return geometric properties of the stone as it currently exists. They are useful for optimization targets or conditional logic.
crownHeightPercentage(): Returns the crown height as a percentage of width.pavilionDepthPercentage(): Returns the pavilion depth as a percentage of width.girdleThicknessPercentage(): Returns the average girdle thickness as a percentage of width.lengthWidthRatio(): Returns the length-to-width ratio of the stone.facetCount(): Returns the total number of facets cut so far.
Conditionals
Use if <expression> { ... } else { ... } to branch inside macros or loops. Any non-zero, finite value counts as true. The else block is optional.
Point helpers
Point helpers return locations in space and slot neatly into facet commands or variables. Store them with let point = mp(G1) and reuse as often as you like. The edge(...) helper is the lone exception: it returns two points, so you must destructure it (let left, right = edge(P1:0, P1:12)) instead of assigning the whole edge to a single identifier. When you need deeper explanations or syntax variations, head to the dedicated Point Helper Reference for cp, gp, gpl, mp, ep, fred, prz, pt, edge, and the at attachment clause.
Everything in motion
Paste this snippet into the studio to see several expression types working together:
set name "Expression Tour"
set gear 96
P1 0 @ 41.8 : cp x8
P3 1 @ (43 - 1.2) : cp // a parenthesized expression is needed here.
G1 0 @ 90 : size x16
C1 0 @ deg2rad(rad2deg(32.0) + 0.2) : mp(G1,P1)+ girdle
C2 0 @ 28.0 : ep(edge(C1:0, C1:6), 0.5)
let shoulder = mp(P1, P3, G1) at (12, 39)
show shoulder green
Point Helper Reference
Point helpers turn the geometry you have already cut into reusable coordinates. They power angle + point cuts, point-pair slices, macros, and optimizer targets. Most helpers resolve to a 3D point once the referenced tiers exist; pt(x, y, z) is the outlier that simply returns the literal coordinate you supply.
Literal point (pt)
pt(x, y, z) returns an explicit point without relying on any cut geometry. Coordinates share the same frame as other helpers: origin at the stone center, +Z toward the crown.
Note:
ptshould be used sparingly, as it is absolute and not sensitive to the geometry of the cut. One good use is in macros: it is used in the$Oval()system macro.
set name "Literal Anchor"
let anchor = pt(0, 0, 1.1)
show anchor orange
P1 0 @ 42 : anchor
Center point (cp)
cp sits on the stone's centre axis. It always returns (0, 0, 1) when the active side is the crown (up) and (0, 0, -1) when the active side is the pavilion (down).
set name "Center Anchors"
// Mark the culet to check alignment
show cp blue
P1 0 @ 41 : cp x8
Girdle points (gp)
gp scans the vertical girdle facets and returns the vertex that sits “in front” of the requested index/angle. Inside a cut command it borrows the current index/angle automatically; elsewhere you must supply at(index, angle) (angle optional) so the helper knows where to search.
set name "Girdle Pickup"
set gear 96
P1 0 @ 45 : cp x8
G1 0 @ 90 : size x16
C1 6 @ 34 : MP(P1,G1) + girdle x16
show gp at (0) cyan
show gp at (24) orange
For uneven girdle lines, there is some extra logic that decides if a point can be a true girdle point. If this fails for some reason, the user can use the mp() function, explained next.
Meet points (mp)
mp searches for the intersection of tiers you name. Pass one or more tiers to include, prefix a tier with ! to exclude it, and ProFacet finds the point that is the first to satisfy the filters. Think of an initial cutting plane far from the stone, that moves towards the center until a valid point is found.
mp respects the current index and angle context, when used inside a cut command. When used outside of a cut command, you can add at (explained later) to jump to a specific tooth and perform the meetpoint search at that tooth.
Matching tiers vs. explicit indices
Think of mp as a facet filter: every positive tier you list must participate in the actual meet, while negated tiers must be absent. There are often several equivalent ways to describe the same vertex.
// A few common patterns
mp(C1) // single-tier meet
mp(C1, G1) // multiple tiers
mp(C1, C1) // requires C1 to be part of the meet at least twice
mp(C1, G1, !C2) // exclude an unwanted tier
mp(C1:12, C2:16) // insist on specific cut indices
mp(C1) at (3, 45.0) // explicit tooth/angle override
Symmetry means those filters can match multiple physical points. That’s usually fine inside a facet command because the base index and angle narrow the choice. Outside of a cut—show mp(C1) or let helper = mp(G1)—add an at(index, angle) clause to keep things deterministic:
Relying on explicit indices like mp(C1:12, C2:16) ties the design to a particular gear/symmetry combination, so prefer tier filters without indices when you can.
Examples
set name "Meetpoint Examples"
set gear 96
P1 3 @ 41.0 : cp xx 8
G1 3 @ 90.0 : size
P2 0 @ 39.8 : gp
C1 3 @ 50.0 : mp(P1, P2) +girdle
C2 0 @ 38.0 : gp
C3 6 @ 26.0 : mp(C1)
T 0 @ 0 : mp(C2) x1
show mp(C1) green
show mp(C2) blue
show mp(C1, G1) purple
show mp(C1, C1, !C2) orange
show mp(P1, P2, P2) red
Edge references (edge)
edge returns the shared edge between two previously cut facets. You pass tier/index pairs (edge(C1:0, C1:6)), and the helper returns both endpoints so other helpers can work with them.
C1 0 @ 32 : 0.08 x6
let _, edge_pt = edge(C1:0, C1:16)
show edge_pt
Use edge only after both referenced facets exist; otherwise the interpreter raises an error telling you which tier/index is missing.
Edge interpolation (ep)
ep walks along an edge or between two arbitrary points. Supply either edge(...) plus a factor between 0.0 and 1.0, or two point helpers and the factor. Zero sticks to the first point, one sticks to the second, and any other value interpolates in between.
set name "Edge Interpolation"
C1 0 @ 32 : 0.08 x16
G1 0 @ 90: size
let start_p, end_p = edge(C1:0, C1:6)
let mid_edge = ep(start_p, end_p, 0.5)
show start_p red
show end_p blue
show mid_edge purple
ep(mp(...), mp(...), 0.25) is equally valid when you want a point partway between two meetpoints.
Projected crossover (fred)
In GemCad, holding down the Shift key limits the movement of the selected point so that it aligns vertically or horizontally with the previous point. 'fred' is a generalization of this idea. It solves more complex intersections—ideal when we need a reference that depends on multiple moving parts. Conceptually it projects two line segments onto the XY plane, finds their intersection, then lifts that XY intersection back onto one of the original segments. The helper was named in honor of Fred Van Sant—one of the most inventive gem designers the lapidary world has ever seen—whose many masterpieces lean on these projections in one way or another.
fred(p1, p2, p3, p4)intersects the XY projections ofp1–p2withp3–p4and returns the point that lives along thep3–p4segment.fred(p1, p2, edge(F1, F2))is shorthand that uses the shared edge betweenF1andF2for the second segment.
This helper is crucial for elaborate parametrized patterns such as Honeycomb FVS or Round Trichecker-1, where you have to drop points onto the exact crossover of two projected paths.
Projected reference (prz)
prz drops an existing point onto either a reference line or an existing facet. It mirrors the projection math used by fred, but instead of intersecting two lines it projects the target straight to the chosen guide and returns the corresponding 3D point.
-
prz(p_target, line_start, line_end)works with any combination of point helpers or variables that resolve to points and projects in the XY plane (so the projected point can fall outside the original segment if your geometry demands it). -
prz(p_target, facet_ref)uses a facet reference such asP1:12and projects onto the live plane of that facet. The facet must already exist in the current geometry—the interpreter will raise a clear error if it has been cut away or is degenerate. When you build tiers withconcat(...)you can reuse that same helper inside this reference (prz(mp(C1), concat(C, i) : 0)), so loops that name tiers dynamically remain easy to target later.When projecting onto a facet the interpreter keeps the target’s original XY coordinates and only recomputes Z as long as the facet’s normal isn’t nearly vertical. For very steep facets we fallback to the perpendicular planar projection so the result still lies on the surface.
Index and Angle override (at)
at is a trailing clause—not a separate function—that pins a helper to a specific index and optional angle: mp(P1) at (12, 39.4). The first number is the index (integer), and the second is the angle override. If you omit the angle (mp(P1) at (12)), the helper keeps the current angle context.
set name "Attachments"
set gear 96
P1 0 @ 41.5 : 0.18 x8
let tooth_12 = mp(P1) at (12, 41.5)
show tooth_12 green
C1 0 @ 32.2 : tooth_12 + girdle x8
Fred and prz Example
We are going to create a simple 3x3 checkerboard, practically identical to "Checkpoint (Tabled)" designed by Tom Herbst.
So the question is how to cut C2. We will do this by drawing lines, find out the intersection point and project this onto a facet. We will select four points, creating two (imaginary) lines:
...
show gp at (84) blue
show gp at (12) blue
show gp at (4) orange
show gp at (44) orange
In this case all four points are on the girdle, so we can easily select them using the at() clause, where we specify a close-enough index.
It should be clear that our desired meetpoint, when looked at from above, is where the line between the blue dots intersects with the line between the orange dots. This can be achieved by:
...
let f1 = fred(gp at (84),
gp at (12),
gp at (4),
gp at (44))
show f1 red
Now, since all blue and orange points are on the same height, the red one will be on that height as well. We need to project that red dot onto the facet above. For this we can use prz().
set ri 1.54
set gear 96
set name "Fred Demo"
P1 0 @ [41.2434] : cp x 12
G1 0 @ 90.00 : size
C1 8 @ [46.196] : mp(P1) +girdle xx 4
show gp at (84) blue
show gp at (12) blue
show gp at (4) orange
show gp at (44) orange
let f1 = fred(gp at(12), gp at (84),
gp at (4), gp at(44))
let f2 = prz(f1, C1:8)
show f2 green
Note how the green point is on facet C1 and exactly on the lines between the blue and orange dots (when looked at from above). We can now cut C2 and C3.
...
C2 0 :gp : f2
C3 12 :gp : f2
For the remaining facets we will need to compute one additional point, similar to the green one.
set ri 1.54
set gear 96
set name "Fred Demo"
P1 0 @ [41.2434] : cp x 12
G1 0 @ 90.00 : 1.0480
C1 8 @ [46.196] : mp(P1) +girdle xx 4
show gp at (84) blue
show gp at (12) blue
show gp at (4) orange
show gp at (44) orange
let f1 = fred(gp at(12), gp at (84),
gp at (4), gp at(44))
let f2 = prz(f1, C1:8)
show f2 green
C2 0 :gp : f2
C3 12 :gp : f2
show gp at (20) purple
show gp at (76) purple
let f3 = fred(gp at(20), gp at (76), gp at (4), gp at(44))
let f4 = prz(f3, C3:12)
show f4 magenta
C4 0 : f2 : f4
T 0 @ 0 :f4
Facets and Tiers
Facet commands translate the numbers you type into actual cuts. When you enter a line such as P1 0 @ 41.8 : 0.18 x8, you first describe one reference facet (P1 0 @ 41.8 : 0.18) and then immediately tell the studio to rotate it eight times (x8). Together, that single line defines the whole tier. This chapter spells out how the reference facet is evaluated and how symmetry turns it into a complete ring of facets.
Describe the reference facet
Every facet statement starts by naming the tier (P1, PF2, Break), optionally choosing a side (up for crown, down for pavilion), and giving an index. The index is the gear tooth where the reference facet begins, so it must be a whole number. If you omit up/down, the studio keeps the previous choice; crown tiers default to up, everything else keeps whatever you last typed.
Prefer the single index form for normal cutting. When you need to force a handful of specific teeth (for example, an asymmetric outline), you can pass an explicit index set with braces {4, 7, 11} instead of one number. In that mode the interpreter ignores symmetry completely and cuts exactly the indices you list, in order, so you leave off any xN or xxN modifier. The first element of the set is still treated as the “base” index for mp()/gp() and other meetpoint/girdle lookups. These explicit index sets will appear surrounded by curly braces (e.g., {4, 7, 11}) in the printable instructions to distinguish them from standard symmetrical tiers.
Cut tiers before you reference them. Point helpers and edge lookups only work after the relevant facets exist, so keep the cut order and the reference order aligned.
Aim the reference facet
Once the tier, side, and base index are set, you provide one of three specifiers:
- Angle + depth – supply both values and the studio cuts to that exact combination. Use the
sizekeyword for 90° cuts (girdle and table facets) when you want the depth handled automatically. - Angle + point – pick an angle and let a point helper decide the depth.
- Point pair – specify two points and the studio slices a plane that touches both, which is ideal for matching opposing facets.
The key idea: the tier/side/index plus specifier only define a single, precise facet at the stated index. The tier becomes complete the moment you append symmetry (x8, xx6, etc.), because those modifiers rotate or mirror that one facet without re-aiming it.
Symmetry builds the tier
After the specifier, symmetry modifiers (xN, xxN) tell the studio how to copy the reference facet around the stone:
xNrotates the reference facet every360 / Ndegrees. The first rotation uses the index you typed; subsequent facets are pure rotations of that plane.xxNmirrors each rotated facet, creating alternating clockwise/counter-clockwise mates for designs that need paired faces.
Important: The studio never “hunts” for meet points when it replicates a tier. It evaluates the reference facet once—using your tier, side, index, and aiming instructions—and then performs exact rotations (and optional mirrors) to fill out the tier. No meetpoints are searched; the reference facet is simply rotated from the base index you specified. No extra math, no iterative refinement, no automatic adjustments.
The result is a full facet tier: the first facet establishes the geometry, symmetry copies it, and modifiers (below) tweak presentation.
Modifiers that follow the specifier
| Modifier | What it does |
|---|---|
xN / xxN | Apply symmetry. x8 rotates the facet every 360/8 degrees. xx8 produces mirrored pairs. |
"instruction" | Replace the auto-generated printable description. |
frosted | Mark the facets in the printable diagrams. And affects the raytracer (still experimental) |
Modifiers can appear in any order after the main specifier.
Advanced
This chapter collects deep dives for cutters who want to stretch ProFacet beyond the basics. Each article focuses on a specialized workflow or design pattern so you can mix and match the right techniques for a particular stone.
Pavilion Angles from RI
Want to compute pavilion angles straight from the active refractive index? Head over to RI-Driven Pavilion Angles for a complete walkthrough and sample code that keeps your pavilion tracking the material's critical angle automatically.
Macros & Reuse
Macros are an advanced feature for packaging a sequence of cuts so you can replay it with a single command. While you can use them for simple repetition like a pavilion break, they are most powerful when defining complex gem outlines using the Centerpoint-Angle Method (CAM).
Anatomy of a macro
define Name(parameter = default, ...) {
# Optional docstring lines that explain the intent.
STATEMENTS...
}
- Name – any identifier. The studio treats names case-insensitively.
- Parameters – always named (
key = valuewhen you call the macro). Defaults are optional. - Body – regular FSL statements. Use
${parameter}to inject values.
Inside the macro you can run set, let, assert, facet commands—anything you would normally write by hand. Once the macro ends, any temporary variables disappear.
Calling a macro
Invoke a macro with $Name(key = value, ...). Every argument is explicit, which keeps macros readable and avoids guessing which number corresponds to what.
$CrownRing(tier = "C1", angle = 32.5)
If you skip a parameter, the default from the definition (or inline placeholder) is used instead. Macros are also the best place to guard your design with assert statements—for example, to stop the interpreter if a caller passes an angle outside the safe range.
Full example
The snippet below defines a macro that cuts two crown rings and checks the parameters before it runs. Paste it into the studio and tweak the angles to see how the macro behaves.
set name "Macro Tour"
set gear 96
set girdle 3.5
P1 0 @ 41.8 : 0.18 x8
P2 6 @ 39.4 : mp(P1) x8
G1 0 @ 90 : size x16
let crown_target = mp(G1)
define CrownRing(tier, angle = 32.0, target = crown_target) {
# Validate the inputs before cutting
assert ${angle} > 0 "Crown angle must be positive"
assert ${angle} < 35 "Keep crown angles below 35 degrees"
${tier} up 0 @ ${angle} : ${target} + girdle x16
}
$CrownRing(tier = "C1", angle = 32.4)
$CrownRing(tier = "C2", angle = 28.6)
Use macros sparingly—just enough to remove repetition without hiding what the stone is doing. For everyday tips on statements and expressions, revisit the earlier chapters; for advanced point tricks, head to Facets and Tiers.
CAM system macros
Looking for the built-in CAM helpers such as $Rect, $SquareTr, $Shield, or $TriCushTr? They now live in the System Macros appendix, complete with parameter tables and usage examples. Browse that chapter whenever you need a refresher on the available macros or want to drop one into a new design.
User Macros
In addition to defining macros within a single design file, you can also create User Macros that persist across all your projects. This is perfect for defining your own utility functions, constants, or setup code that you find yourself writing repeatedly.
To access this feature:
- Open the workspace menu (hamburger icon).
- Select User Macros.
- Enter your FSL code in the dialog and click Save.
This code will be automatically prepended to every script you run in the studio. For example, if you define let magic_angle = 42 in your user macros, you can use magic_angle in any design without redefining it.
Note: If an error occurs within your user macro code during execution, the error message will indicate that it happened inside the macro. Line numbers for errors in your main script are automatically adjusted to account for the prepended macro lines.
Gemstone Design Styles in ProFacet
This guide outlines three primary design styles used in ProFacet. Each style emphasizes different cutting constraints and offers unique advantages depending on whether you want reproducibility, fluid exploration, or full geometric freedom.
Style I: Angle + Depth
Style I leans on angle plus depth cutting. It is more 'static' than other methods, but it excels when you want to recreate existing designs that are documented with explicit depths or perform a fast floating facet check.
set name "Hearts and Arrows I"
set gear 96
P1 3 @ 41.0 : cp xx 8
G1 3 @ 90.0 : size
P2 0 @ 39.8 : .753
C1 3 @ 50.0 : .700
C2 0 @ 38.0 : .547
C3 6 @ 26.0 : .4383
T 0 @ 0 : .207
Style II: Angle + Point
Style II combines angle plus point cutting, making it far more flexible and fluid than Style I. The primary ProFacet UI emits cuts in this style, and it is a great fit for porting existing diagrams while keeping the option to experiment.
set name "Hearts and Arrows II"
set ri 2.46
set gear 96
P1 3 @ 41.0 : cp xx 8
G1 3 @ 90.0 : size
P2 0 @ 39.8 : gp
C1 3 @ 50.0 : mp(P1, P2) +girdle
C2 0 @ 38.0 : gp
C3 6 @ 26.0 : mp(C1)
T 0 @ 0 : mp(C2)
Style III: Point + Point
Style III— the fully fluid model—relies on point plus point cutting. Only one pavilion angle and one crown angle are specified; every other facet is derived from intersections of calculated points. It is indispensable for parametrized designs such as checkerboards where point-to-point control is required.
set name "Hearts and Arrows III"
set ri 2.46
set gear 96
P1 3 @ 41.0 : cp xx 8
G1 3 @ 90.0 : size
P2 0 : ep(edge(P1:3, P1:9), 0.76): gp
C1 3 @ 50.0 : mp(P1, P2) +girdle
C2 0 : ep(edge(C1:3, C1:9), 0.13) : gp
C3 6 : ep(mp(C2), mp(C2,G1),0.6) : mp(C1)
T 0 @ 0 : mp(C2)
The Optimizer
The optimizer can be used with all three styles. Style III is especially well-suited for optimization sessions because its point-to-point architecture can preserve the overal shape, something that is difficult—or sometimes impossible—with Styles I and II.
Mixing Styles
Nothing prevents you from mixing these approaches inside a single project.
CAM Outlines
The Centerpoint–Angle Method (CAM) is a powerful, geometric approach to faceting design that simplifies the creation of complex gemstone cuts by focusing on the relationships between facets rather than just their absolute positions. Instead of relying solely on trial-and-error meetpoint sequences, CAM allows you to define a "centerpoint" (a specific vertex where multiple facets meet) and then calculate the precise angle required for a new tier of facets to meet perfectly at that point. By mathematically linking the index (rotational position) and angle of a facet to a target intersection, CAM ensures exact symmetry and perfect meetpoints, making it an essential logic for designing precision cuts efficiently.
ProFacet exposes CAM-friendly helpers as macros so you can sketch a girdle in a single line and refine the angles later. The following outlines are ready today:
- Round — a fast circular preform via
$Round. - Oval — an elliptical outline via
$Oval. - Rectangle — the straight rectangle via
$Rect. - Truncated rectangle — available through
$RectTr. - Truncated square — available through
$SquareTr. - Double-truncated rectangle — available through
$RectDblTr. - Truncated triangle — available through
$TriTr. - Shield — mirrored triangular shield via
$Shield. - Cushioned truncated square — available through
$SquareCushTr. - Cushioned truncated triangle — available through
$TriCushTr. - Cushioned truncated pentagon — available through
$PentCushTr. - TriCurved — curved trilliant via
$TriCurved.
Round macro
$Round blocks in a simple circular preform when you need a neutral starting outline. Set the pavilion angle for the lower tier and pick a sym count that evenly divides your lap gear—higher numbers create finer girdle segmentation.
This round macro is included for 'completeness', round shapes don't really need CAM. :)
set name "CAM Round"
set gear 96
$Round(angle = 43, sym = 8)
Oval macro
$Oval walks an elliptical girdle by tracing one quadrant at a time. Use lwr for the length-to-width ratio, angle to control how deeply the ellipse drops, and segmentsPerQuad to set how smooth each quadrant is. Stick with gears divisible by four so the quarter marks line up.
set name "CAM Oval"
set gear 96
$Oval(angle = 42, lwr = 1.35, segmentsPerQuad = 6)
Rectangle
set name "CAM Rectangle"
set gear 96
$Rect(lwr = 1.6, angle = 45)
Truncated rectangle macro
Use $RectTr for a truncated rectangle: pick a length-to-width ratio, the pavilion angle for the long face, and an optional truncation ratio for the corners.
set name "CAM Truncated Rectangle"
set gear 96
$RectTr(lwr = 1.6, angle = 45, truncation = 0.30, offset = 5)
The macro generates the shallow preform (tiers PF1–PF3) and a finished girdle loop (G1–G3). Tweak truncation toward 0.0 for squarer corners or up toward 1.0 for aggressive chamfers. offset is optional and rotates the corner facets if you want the corners aligned to a different index.
Truncated square macro
$SquareTr mirrors the same control scheme on 4-fold symmetry. It cuts four identical long facets, then trims each corner using the truncation ratio.
set name "CAM Truncated Square"
set gear 96
$SquareTr(angle = 45, truncation = 0.35)
Because the macro expects a gear divisible by four it runs cleanly on 64-, 80-, 96-, or 120-tooth index gears.
Cushioned truncated square macro
$SquareCushTr builds a softened square outline by offsetting the four primary girdle facets and trimming the corners with a controllable ratio. Use cushion to steer the amount of cushioning, set the pavilion angle for those primary facets, and dial truncation between 0.0 (sharp corners) and about 1.0.
set name "CAM Cushioned Square"
set gear 96
$SquareCushTr(angle = 45, cushion = 2, truncation = 0.40)
The macro mirrors the pavilion cuts using xx4 symmetry so the cushion stays balanced even if you rotate the starting index. Lower offset keeps the flats on the 0 index; bump it higher to align the flats with any pre-existing tier. Use the girdle percentage or follow-up tiers to stretch the outline without rewriting the macro call.
Double-truncated rectangle macro
set name "CAM Double Trunc Rect"
set gear 96
$RectDblTr(lwr = 1.6, angle = 45, truncation1 = 0.60, truncation2 = 0.50)
Both truncation settings must stay non-negative, and lwr must be greater than zero—violations now fail fast during interpretation instead of collapsing the girdle geometry.
Truncated triangle macro
$TriTr blocks in a three-fold outline with controllable corner trims. Pass the pavilion angle for the primary girdle facets and adjust truncation between 0.0 (sharp points) and roughly 1.5 (heavy chamfers). The macro assumes a standard 0.7 girdle radius and uses mp(...) so the corners stay aligned.
set name "CAM Truncated Triangle"
set gear 96
$TriTr(angle = 44, truncation = 0.35)
Shield macro
$Shield lays in a mirrored threefold outline that reads like a shield or kite while still honoring CAM leveling. It fixes the base at index 6 and expects a gear divisible by six so each mirrored pair lands cleanly.
set name "CAM Shield"
set gear 96
$Shield(angle = 44)
Dial the pavilion angle to stretch or tighten the lobes—lower values soften the top shoulders, while steeper angles pull the outline toward a pointed kite.
Cushioned truncated triangle macro
$TriCushTr mirrors the same workflow but lets you offset the long side by tweaking the optional cushion index. Use it when you need a slightly rounded triangle that still lands on three repeatable meetpoints. The macro shares the same truncation and angle controls as $TriTr, so you can swap between the two without rewriting your design.
set name "CAM Cushioned Triangle"
set gear 96
$TriCushTr(angle = 44, truncation = 0.30, cushion = 2)
Cushioned truncated pentagon macro
set name "CAM Cushioned Pentagon"
set gear 80
$PentCushTr( angle=33, truncation=0.3, offset=1)
TriCurved macro
$TriCurved creates a Trilliant cut curved by amounts c1 and c2.
set name "CAM TriCurved"
set gear 96
$TriCurved(angle=45, c1=2, c2=4)
RI-Driven Pavilion Angles
The pavilion's performance depends on how closely you aim each facet at the material's critical angle. Now that FSL expressions can read the gemstone's refractive index through the built-in RI constant, you can script those angles directly instead of retyping tables from reference books.
Reading the active RI
RI always mirrors the most recent set ri command (including optimizable expressions and overrides), and it is case-insensitive (RI, ri, Ri, etc.). Because it behaves like a read-only variable, you can combine it with any of the math helpers in expressions, macro parameters, or optimizer targets.
Example: pavilion cut at the computed critical angle
The snippet below calculates the material's critical angle, adds a one-degree safety margin, and then cuts a pavilion that tracks that calculation. Any change to set ri automatically recomputes the angles—perfect for trying new materials or letting the optimizer sweep the refractive index.
set name "Critical Pavilion"
set gear 96
set ri 1.76
let critical_angle = rad2deg(asin(1 / RI))
let pavilion_angle = critical_angle + 1.0 // 1° safety margin
P1 2 @ pavilion_angle : cp x8
Try changing the RI to quartz (set ri 1.544) and re-run the design—the pavilion reorients itself instantly based on the new physics. You can extend the same approach to crowns, macro defaults, or optimizer objectives by referencing RI anywhere a numeric expression is allowed.
Advanced Optimization
This chapter describes advanced FSL functions designed for optimizing gemstone designs, particularly for ensuring geometric integrity and achieving specific proportions.
Note: Use these functions in combination with the
assertstatement to enforce specific geometric properties of the gemstone during the optimization process. Best to make sure that the geometry is valid before running the optimization process.
Geometric Check Functions
These functions allow you to assert specific geometric properties of the gemstone during the optimization process. They are particularly useful for:
- Preventing Girdle Corruption: Ensuring the girdle remains even and vertical.
- Shape Constraints: Enforcing specific ratios like table size or depth.
isGirdleOK()
Checks if the girdle is "even". An even girdle means:
- All girdle facets (vertical facets) have the same height (within a small tolerance).
- All girdle facets are centered at the same vertical position (Z).
Usage:
assert isGirdleOK() "Girdle issue!"
Use this assertion to reject any optimization steps that result in an uneven or twisted girdle.
tablePercentage()
Calculates the ratio of the table width to the stone's total width (diameter), expressed as a percentage.
- Table Width: The width (along X-axis) of the largest facet with a normal pointing straight up (0, 0, 1).
- Stone Width: The total width of the stone along the X-axis.
Usage:
// Ensure table is between 50% and 60%
assert tablePercentage() >= 50 "Table too small!"
assert tablePercentage() <= 60 "Table too big!"
depthPercentage()
Calculates the ratio of the stone's total depth (height) to its width, expressed as a percentage.
- Total Depth: The vertical distance between the lowest and highest points of the stone.
- Stone Width: The total width of the stone along the X-axis.
Usage:
// Target a specific depth percentage
assert abs(depthPercentage() - 60) < 1 "Depth too small!"
crownHeightPercentage()
Calculates the height of the crown as a percentage of the total stone height.
- Crown Height: The vertical distance from the top of the girdle to the highest point of the stone (usually the table).
- Total Height: The vertical distance between the lowest and highest points of the stone.
Usage:
// Ensure crown height is around 15% of total height
assert abs(crownHeightPercentage() - 15) < 2 "Crown height issue!"
facetCount()
Returns the total number of facets in the gemstone.
Usage:
// Ensure the design has exactly 57 facets
assert facetCount() == 57 "Facet count issue!"
pavilionDepthPercentage()
Calculates the height of the pavilion as a percentage of the total stone height.
- Pavilion Height: The vertical distance from the bottom of the girdle to the lowest point of the stone (culet).
- Total Height: The vertical distance between the lowest and highest points of the stone.
Usage:
// Ensure pavilion depth is around 43%
assert abs(pavilionDepthPercentage() - 43) < 2 "Pavilion depth issue!"
girdleThicknessPercentage()
Calculates the average girdle thickness as a percentage of the stone's width.
- Girdle Thickness: Average vertical thickness of facets whose normals are vertical (girdle facets).
- Stone Width: The total width of the stone along the X-axis.
Usage:
// Keep girdle between 3% and 5% of width
assert girdleThicknessPercentage() >= 3 "Girdle too thin!"
assert girdleThicknessPercentage() <= 5 "Girdle too thick!"
lengthWidthRatio()
Calculates the ratio of the stone's larger dimension to its smaller dimension (Length/Width or Width/Length).
- Result: Always greater than or equal to 1.0.
Usage:
// Ensure the stone is perfectly square/round (ratio 1.0)
assert abs(lengthWidthRatio() - 1.0) < 0.01
Examples
This chapter gathers the “recipe cards” we reach for most often. Each example pairs a bit of history with a ready-to-run FSL block and an interactive studio embed so you can orbit the stone and start experimenting immediately. Every bracketed literal ([41.0], [35.1184], etc.) in these pages is individually optimisable: plug the spec into the studio, fire up the optimizer, and nudge the angles that matter for your material.
What to expect on each page:
- Narrative context – When and why the cut became popular, plus our notes on how the geometry translates into brilliance vs. profile.
- Complete FSL – Minimal code that you can copy verbatim, then evolve by tweaking literals or inserting your own ideas.
- Interactive Viewer – A live view of the stone that mirrors the FSL block so you can inspect crown, pavilion, and girdle decisions.
- Optimization tips – Suggestions on which tiers to float when you want more brightness, contrast, or scintillation.
Use these examples as launch points: duplicate one of the pages, change the FSL values, and refresh the embed to reflect your own measurements. If you are new to FSL, start with the Brilliant or Step cut pages; if you want to see the optimizer in action, jump straight to the Portuguese and Princess entries where every tier angle is meant to be pushed.
Examples
- Brilliant Cut
- Briolette
- Mixed Cut
- Checkerboard
- Princess Cut (Optimized)
- Portuguese Cut
- Honeycomb
- Pixel Cut
- Step Cut
- ProFacet Team Designs
Brilliant Cut
The classic round brilliant maximizes sparkle by balancing crown height, pavilion depth, and table ratio. Use this template as a familiar starting point.
Sources
- https://www.gia.edu/round-brilliant-cut-diamond
- https://en.wikipedia.org/wiki/Round_brilliant
- https://en.wikipedia.org/wiki/Tolkowsky
FSL Spec
set name "Example:: Round Brilliant"
set ri 2.46
set gear 96
P1 3 @ 41.0 : cp xx 8
G1 3 @ 90.0 : size
P2 0 @ 39.8 : gp
C1 3 @ 50.0 : mp(P1, P2) +girdle
C2 0 @ 38.0 : gp
C3 6 @ 26.0 : mp(C1)
T 0 @ 0 : mp(C2) x1
Crown View
Briolette
Briolettes are all-flash drops: a continuous string of kite facets wraps a teardrop outline so the stone sparkles no matter how it spins on a chain.
Originally cut as drilled pendants in Mughal India and revived during the Victorian era, briolettes thrive when the facet run is tight and uninterrupted—imagine a smooth polished bead, but rendered in facets instead of a single glossy surface.
FSL Spec
set name "Briolette"
set ri 2.15
set gear 96
set cube 5 // We need a larger stone
G 3 @ 90 : 0.5 x 16
P1 3 @ 83 : 0.4
P2 0 @ 80 : mp(G)
P3 0 @ 66 : mp(P1)
P4 3 @ 62 : mp(P1)
P5 3 @ 58 : mp(P3)
P6 0 @ 55 : mp(P3)
P7 0 @ 40 : mp(P5)
P8 3 @ 38 : mp(P5)
P9 3 @ 18 : mp(P7)
C1 0 @ 87 : mp(P2)
C2 0 @ 80 : mp(G)
C3 3 @ 77 : mp(G)
C4 3 @ 72 : mp(C2)
C5 0 @ 70 : mp(C2)
C6 0 @ 65 : mp(C4)
C7 3 @ 63 : mp(C4)
C8 3 @ 53 : mp(C6)
C9 0 @ 51 : mp(C6) x 8
Interactive View
Step Cut
Step cuts emphasize broad flashes over scintillation. Long, parallel facets reward precise meetpoint control and reveal clarity, making them ideal for emerald or asscher style outlines.
Because not all cuts are meetpoint based, the design of emerald type cuts isn't straighforward. In the code below we create helper points at fixed distances, and cut through these. The angles can be changed, without changing the proportions of the facets, allowing to optimize this cut while keeping all proportions intact.
Design Highlights
- Rectangular outline with clipped corners for durable setting points.
- Tiered crown steps that keep the table expansive while adding subtle height.
- Pavilion tiers tuned to keep light return without creating a deep window.
FSL Spec
set name "Emerald Cut"
set ri 1.54
set gear 96
set color #00a300
// Pavilion angles
let ang1 = [53.0306]
let ang2 = [33]
let ang3 = [22.7648]
// Crown angles
let angc1 = [45]
let angc2 = [27.5353]
let angc3 = [27.5]
$RectTr(lwr = 1.5, angle = 40, truncation = 0.4)
P1 0 @ ang1 : cp x 2
let p7 = ep(edge(P1:0, G2:0), 0.5)
let p8 = ep(edge(P1:48, G2:48), 0.5)
let p9 = ep(p7, p8, 1 / 6)
let px = prz(p9, p7, cp)
P2 0 @ ang2 : px
let ncp = ep(edge(P2:0, P2:48), 0.5)
let midp = ep(ncp, px, 0.5)
P3 0 @ ang3 : midp
P4 12 @ ang1 : mp(G2, P1) xx 2
P5 12 @ ang2 : mp(P4, P1)
P6 12 @ ang3 : mp(P5, P2)
P7 24 @ ang1 : gp
P8 24 : mp(P2, P5) : mp(P4, P7)
P9 24 @ ang3 : mp(P5)
C1 0 @ angc1 : mp(P1) + girdle
let cmp1 = ep(edge(C1:0, G2:0), 0.5)
let cmp2 = ep(edge(C1:48, G2:48), 0.5)
let cmp3 = ep(cmp1, cmp2, 1 / 10)
let cmp5 = ep(edge(C1:0, C1:48), 0.5)
let pp = prz(cmp3, cmp5, cmp1)
C2 0 @ angc2 : pp
let cmp6 = ep(edge(C2:0, C2:48), 0.5)
let cmp7 = ep(cmp1, cmp2, 2 / 10)
let pp2 = prz(cmp7, cmp6, pp)
C3 0 @ angc3 : pp2
let cmp10 = ep(edge(C3:0, C3:48), 0.5)
let cmp11 = ep(cmp1, cmp2, 2.5 / 10)
let pp3 = prz(cmp11, pp2, cmp10)
T 0 @ 0 : pp3
C4 12 @ angc1 : mp(G2, G3)
C5 12 @ angc2 : mp(C1, C4)
C6 12 @ angc3 : mp(C2, C5)
C7 24 @ angc1 : mp(G1, G3)
C8 24 @ angc2 : mp(C4, C7)
C9 24 @ angc3 : mp(C5, C8)
Crown View
Pavilion View
Mixed Cut
Mixed cuts pair a brilliant crown with a step pavilion (or vice versa) creating hybrid light behavior suited for off-standard shapes. This concept favors custom designs where you want lively face-up play but tight control over depth.
Design Highlights
- Brilliant-style halo of crown facets to energize the table.
- Pavilion steps that manage depth for easier setting tolerances.
FSL Spec
set name "FVS-12 Karen-2"
set ri 1.76
set gear 64
$RectTr(angle=34,lwr=[1.5])
P1 0 @ 61.50 : cp xx 2
P2 8 @ 41: mp(G2)
P3 16 @ 46.00 : mp(G3)
P4 4 @ 42.73 : gp
P5 14 :mp(P2) :gp
P6 1 @ 43.41 : ep(mp(P4,P4,P4), mp(P2),0.5)
P7 0 : mp(P1) : mp(P4)
let ang1 = [36.64]
let ang2 = [30.64]
let ang3 = [28.98]
C1 8 @ ang1 : mp(P2) + girdle
C2 8 @ ang2 : ep(mp(C1),mp(G3),0.9)
C3 8 @ ang3 : ep(mp(C2),mp(C1),0.9)
C4 0 @ ang1 : mp(G3)
C5 0 @ ang2 : mp(C1)
C6 0 @ ang3 : mp(C2)
C7 16 @ ang1 : mp(G3)
C8 16 @ ang2 : mp(C1)
C9 16 @ ang3 : mp(C2)
C10 0 @ 0.00 : mp(C3)
Crown View
Pavilion View
Portuguese Cut
The Portuguese cut earned its reputation in the early 20th century when cutters in Lisbon popularised a multi-tiered wheel of rhomboid facets for quartz and topaz. With five pavilion tiers and a similarly dense crown, it trades the tidy profile of a brilliant for relentless sparkle—light breaks so many times that even modest material looks electrified.
Traditional instructions were guarded, but the version below mirrors the ratios published by Vargas in Faceting for Amateurs. The pavilion angle starts at 62° and cascades down in 5° steps; the crown mirrors the same rhythm. In ProFacet every bracketed literal (for example [62], [57], [46], etc.) is individually optimisable, so after loading the spec try nudging the upper tiers or letting the optimizer push them. Expect a tangible performance boost—brighter average intensity and richer scintillation—at the cost of a slightly less refined look.
FSL Spec
set name "Portuguese Cut"
set ri 1.54
set gear 96
set cube 3
G1 0 @ 90 : size x 16
P1 0 @ [62] : cp
P2 3 @ [57] : gp
P3 0 @ [52] : mp(P1)
P4 3 @ [47] : mp(P2)
P5 0 @ [42] : mp(P3)
C1 0 @ [46] : mp(P1) +girdle
C2 3 @ [41] : gp
C3 0 @ [36] : mp(C1)
C4 3 @ [31] : mp(C2)
C5 0 @ [26] : mp(C3)
C6 0 @ 0.00 : mp(C4) x1
Interactive View
Note: Be aware that with extreme adjustments the stone begins to lose the Portuguese cut’s characteristic look.
Princess Cut (Optimized)
Square brilliants trimmed on a 45° girdle have been marketed as “princess cuts” since the 1960s, but the modern profile—step pavilion, four chevrons on the crown—didn’t settle down until Israel “Izzy” Itzkowitz refined it in the early 80s. Since then cutters have leaned on it for fiery melee that packs more weight than rounds. The version below starts from a textbook princess recipe and pushes the angles with ProFacet’s optimizer, which shrinks the table while lifting contrast intensity.
Like every FSL block in this book, the bracketed literals ([56.1315], [35.1184], [30.7955], [0.7381]) are individually optimisable. Run the optimizer with different weights or tweak the angles manually—pavilion and crown tiers are independent, so you can chase more brightness or shrink the table further.
FSL Spec
set name "Princess Cut Optimized"
set ri 1.54
set gear 96
G1 0 @ 90 : size xx 4
P1 0 @ [56.1315] : cp
let p1 = ep(edge(P1:0, G1:0), 0.5)
let p2 = ep(edge(P1:48, G1:48), 0.5)
P2 1 :gp : prz(ep(p1, p2, 1 / 8), P1:0)
P3 2 :gp : prz(ep(p1, p2, 2 / 8), P2:1)
P4 3 :gp : prz(ep(p1, p2, 3 / 8), P3:2)
C1 0 @ [35.1184] : mp(P1) + girdle
C2 1 @ [30.7955] : gp
C3 2 : gp : ep(edge(C2:1, C2:95), [0.7381])
C4 0 @ 0 : mp(C2)
Interactive View
Checkerboard
Checkerboard crowns stretch facets into squares across the table, yielding bold sparkle even on shallow stones. This layout is popular for colored gems that rely on saturation rather than brilliance.
Design Highlights
- Square outline with evenly spaced crown facets to create the grid effect.
- Flatter crown to keep the table broad while still offering relief cuts.
- Pavilion angles tuned shallow to keep depth low for weight retention.
FSL Spec
Note: the design below is adapted from the "GemCad Advanced Tutorial". This is not an easy one, but the idea is to create points in a 4x4 grid, project one after another on the facet above and cut a new tier. The angles are optimized for quartz. There is an easier 3x3 checkboard in the Fred and prz Example
set name "GemCad Advanced Tutorial"
set ri 1.54
set gear 64
G1 3 @ 90.00 : 0.9500 xx 4
C1 1 @ [54.983] : cp
let midp1 = ep(edge(C1:1, G1:61), 0.5)
let midp2 = ep(edge(C1:31, G1:35), 0.5)
G2 1 @ 90.00 : midp1
let pu1 = fred(midp1, midp2, edge(C1:1, C1:15))
C2 3 : midp1 : pu1
let fr2 = fred(gp at(6), gp at(60), edge(C2:3, C1:1))
show fr2
C3 8 : gp at(6) : fr2
let pur1 = mp(C1, C2, C3) at(30)
show pur1 purple
let pur2 = mp(C1, C2, C3) at(3)
show pur2 purple
let pur3 = prz(ep(pur2, pur1, 0.25), C3:8)
show pur3 purple
C4 3 : pur3 : pur2
let or1 = mp(C3) at(56)
show or1 orange
let or2 = fred(pur3, or1, edge(C4:3, C4:61))
show or2 orange
C5 8 : pur3 : or2
P1 1 @ [45.3667] : mp(C1) - girdle
P2 3 @ [44.193] : gp
P3 2 @ [44.596] : gp
Honeycomb
The honeycomb motif as in FVS-222 is an advanced design that step by step creates (helper) points by interpolating and the use of the fred function. It is a "Projected crossover" used in a way to get a perfect honeycomb pattern. Note that the actual cuttings angles are computed and adapt when the starting position changes due to e.g. optimization. GemCad allows designing (by using Shift-click) checkerboards but not more "free form" patterns like this one.
FSL Spec
set name "FVS-222 Honeycomb"
set ri 1.54
set gear 96
G1 5 @ 90 :size xx6
P1 5 @ [43.02] : cp
G2 0 @ 90 : ep(edge(G1:5, P1:5), 0.25)
P2 0 @ 41.3 : mp(G1,G2,P1)
P3 8 @ 40.9 : mp(G1,P1)
C1 5 @ 35 : mp(P3) + girdle
C2 0 @ 28.6 : mp(G2,G1,C1) at (4,45)
let p1 = mp(G2,G1,C2) at(4,45)
let p2 = mp(G2,G1,C2) at(10,45)
let p3 = fred(p1,p2, edge(C1:11, C1:5))
show p1 #6d4370
show p2 purple
show p3 #1eff00
C3 8 @ 25.04 : p3
let p4 = mp(G2,G1,C2) at (46,45)
show p4 red
let p_white = fred(p1, p4, edge(C2:0, C3:8))
show p_white white
let orange1 = mp(C1,C1,C3) at (88,45)
let orange2 =mp(C1,C1,C3) at (24,45)
show orange1 #9c3540
show orange2 #2b2417
let center = mp(C1,C1,C3) at (0,45)
show center #b6a486
let blue1 = mp(C3) at (0,5)
let green1 = fred(orange1, orange2, center, blue1)
C4 0 :green1 :p_white
T 0 @ 0 : ep(edge(C4:0,C4:16), 0.5)
Crown View
Pavilion View
Pixel Cut
The “Pixel Cut” pushes the Cross-Bar geometry into a more modern, high-contrast profile. It leans on the optimizer to chase contrast intensity while keeping the pavilion layout compact. Start from the pavilion mains, build a simple girdle reference, then let the meetpoint ladder walk with evenly spaced projections to sculpt the interior reflections.
For the crown, this variation adds a pair of edge-plane helpers that feed a stepped trio of facets (“pixels”) radiating inwards. Arrays keep the optimized angles organized, so you can dial in each band without hunting through the whole code block. Drop the snippet into the studio, tweak the [value] entries, and re-run the optimizer any time you want to re-balance brightness versus contrast.
In the design below, all angles can be adjusted (manually or by the optimizer), while keeping the same pixel layout. Magic.
set name "FVS-39 Cross-Bar based. Optimized for contrast intensity"
set ri 1.54
set gear 64
set cube 5
P1 16 @ 70.00 : 0.8 x 2
G1 0 @ 90 : .8
let p1, p2 = edge(P1:16, P1:48)
let ang1 = {[49.5101], [48.054], [44.1912],[43.5928]}
P2 0 @ ang1[0] : 0.18
P3 0 @ ang1[1] : prz(ep(p1, p2, 1 / 8), P2:0)
P4 0 @ ang1[2] : prz(ep(p1, p2, 2 / 8), P3:0)
P5 0 @ ang1[3] : prz(ep(p1, p2, 3 / 8), P4:0)
P6 8 @ 65.53 : mp(P3) x 4
G2 8 @ 90.00 : mp(P2)
G3 16 @ 90.00 : mp(P6) x 2
C1 0 @ 70.00 : mp(P2) + girdle xx 2
C2 8 @ 49.8 : gp x 4
C3 16 @ 36.00 : gp x 2
let p6 = ep(edge(C3:48, G3:16), 0.5)
let p7 = ep(edge(C3:16, G3:48), 0.5)
let ang2 = {[20.4718],[14.52],0}
C4 16 @ ang2[0] : mp(C2)
C5 16 @ ang2[1] : prz(ep(p7, p6, 2/ 7), C4 :16)
C6 16 @ ang2[2] : prz(ep(p7, p6, 3/ 7), C5 :16)
Gallery
Work in progress. These images are generated with our path tracer, which is not released yet and is still being worked on. The path tracer is a physics-based renderer that uses Monte Carlo sampling to approximate the full light-transport solution: many random rays per pixel, many bounces, and progressive accumulation until the image converges. The stones are shown in a simple light box with even top lighting and optional spot light. No head shadows or special effects, just aiming for a realistic image.
Image quality varies. The renderer is under active development, so older images may look less good than newer ones, e.g. due to simple caustics.
![]() Sakura 96 by Marco Voltolini | ![]() Beginner Trillion by Michiko Huynh |
![]() Rubicello- Marco Voltolini | ![]() Void Reaver by Arya Akhavan |
![]() Standard Round Brilliant Cut with fire (dispersion) | ![]() Step Cut |
![]() Princess Cut on brushed aluminum | ![]() Step Cut |
![]() Crisantemo by Marco Voltolini | ![]() Abyssal Maw - Arya Akhavan |
![]() Voltolini-Tristano | ![]() Akhavan-FallenStar |
![]() FVS-336 Trillium | ![]() 2014 Maximize Entry 6 Marco Voltolini |
![]() Laborie - One Rupee | ![]() Bug Barion - Marco Voltolini |
![]() SRB with spot light | ![]() Frost Star Hex - Andrew Brown |
![]() FVS-162 Modified Orion - by Fred W. Van Sant | ![]() Portuguese Cut |
Gallery 2
Image quality varies. The renderer is under active development, so older images may look less good than newer ones, e.g. due to simple caustics.
![]() Blinder - Tom Herbst | ![]() Hanami - Marco Voltolini |
![]() Akhavan – Heavens-Piercing Drill | ![]() FVS-90 on copper |
![]() Star Trek Trillion - Jim Perkins | ![]() Fiorello 80 - Marco Voltolini |
![]() Tribal- Marco Voltolini | ![]() FVS-145 |
![]() FVS-80 | ![]() Signature#4 - Jeff Graham |
![]() Fancy Hexagonal Brilliant, by Jim Perkins |
Next: ProFacet Designs → ← Previous: Gallery
ProFacet Designs
Designs by ProFacet's designer
![]() ProFacet-1: pushing the limits of contrast | ![]() ProFacet-2 |
![]() ProFacet-3 | ![]() ProFacet-4 |
![]() ProFacet-5 | ![]() ProFacet-6 |
![]() ProFacet-7: Mesmerizing | ![]() ProFacet-8 |
![]() ProFacet-9 | ![]() ProFacet-10 |
![]() ProFacet-11 | ![]() ProFacet-12 |
![]() ProFacet-13: Claude enters the chat | ![]() ProFacet-14 |
![]() ProFacet-16 | ![]() ProFacet-17 |
![]() ProFacet-18 | ![]() ProFacet-20 |
![]() ProFacet-21 | ![]() ProFacet-23 |
![]() ProFacet-24 | ![]() ProFacet-25 |
![]() ProFacet-26 |
FAQ
Q: Can I import .gem, .asc, .gcs, or .fsl files?
A: Yes—there’s an experimental importer in the workspace menu (the three-line “hamburger” next to Process) that accepts those formats. It tries to reverse-engineer the topology (which tiers connect, how symmetry repeats, which meetpoints matter) from mostly geometric data. That reconstruction is heuristic: it works surprisingly well on many files, but it’s not perfect, and the importer will spell out what it could or couldn’t infer in the generated FSL.
Q: Why can’t I manually inspect light rays and adjust windowing?
A: Modeling every ray and every facet angle by hand is practically impossible—slight changes ripple through the entire stone, and no one can cover those consequences better than the machine. We lean on our GPU-powered computation because it outperforms manual tweaking, letting you spend energy on the actual design decisions instead of endlessly chasing individual rays.
Q: Why does ProFacet lean on WebGPU for its heavy compute passes instead of sticking to CPU or WebGL?
A: Optimization sweeps, lighting analyses, and the raytraced preview all boil down to parallel math: tracing millions of rays, sampling BRDFs (models that describe how light reflects off a material), and updating facet energy buffers every frame. WebGPU unlocks dedicated compute pipelines, storage buffers, and subgroup ops, so we can keep those workloads on the GPU without round-tripping through JavaScript. Trying to express that in WebGL would mean abusing fragment shaders and multiple render targets, which is slower, harder to debug, and far less predictable across drivers.
Q: How do I know whether my browser (or GPU driver) supports WebGPU today?
A: Visit caniuse.com/?search=webgpu and scroll down to the support matrix. Green boxes indicate native support; yellow entries usually require enabling an experimental flag in chrome://flags, edge://flags, or the equivalent in Safari Technology Preview. Red entries mean WebGPU is still unavailable on that platform, so plan on updating your browser or OS before relying on it for production work.
Q: I have P1 42.00 08-12-16-32-36-40-56-60-64-80-84-88, but the Symmetry Assistant says it isn’t valid. What’s wrong?
A: That string actually represents two symmetry loops interleaved together, so the assistant can’t guess a single pattern from it. Split the data by alternating indices: P1 42.00° 8, 16, 32, 40, 56, 64, 80, 88 and P2 42.00° 12, 36, 60, 84. Paste each loop separately and the Assistant will happily detect the symmetry for both tiers.
Q: Is this using a proper geometric modeling CAD kernel?
A: Yes. ProFacet runs on a Swiss Made 🇨🇭 precision kernel purpose-built for CAD workflows. It uses arbitrary-precision floating point math, so no drift creeps into the model—even under deep zooms or long optimization passes—keeping every point exactly where it belongs.
Q: Can I export the rendered mesh as a Wavefront .obj file?
A: Yes. Process the design first so the viewer has a current mesh, then click the hamburger button next to the Process chip and choose Export OBJ. ProFacet generates a watertight mesh straight from the WASM buffers, names the file after your design (or profacet-model.obj if unnamed), and downloads it immediately so you can inspect or 3D-print the model in other tools.
Q: How do I contact support or give feedback?
A: You can email us directly at jon@profacet.com for any questions, bug reports, or feature requests.
Q: Is there a way to auto format FSL source inside the editor?
A: Focus the FSL editor and press Ctrl+Shift+F (or Cmd+Shift+F on macOS) to run the formatter so spacing and delimiters are normalized automatically.
Privacy, Ownership, and Simulation Disclaimer
Cookies and tracking
- We do not use third-party cookies, analytics pixels, or advertising trackers.
- Session cookies stay first-party (scoped to
profacet.com) and exist only to keep you signed in when you opt into account features.
Design ownership
- Your designs remain yours. We do not mine, resell, or reuse them.
- Designs are only shared outside your account when you submit them to an official contest.
Hosting and infrastructure
- ProFacet is hosted on Firebase (Google Cloud). Think of it as renting a secure storage unit: we control the keys and decide what lives there.
- Security-sensitive services—authentication, encryption at rest, and access controls—run on Google Cloud’s managed stack, so they inherit the same hardened protections Google applies to its own infrastructure.
- Firebase serves the app over our own domain, so it does not drop third-party cookies or inject ads—everything you load still counts as first-party traffic.
- When you enable cloud sync, the files you pick are stored in Firebase solely so you can reach them from another device. Firebase acts as our subprocessor and cannot repurpose that data.
- Plain-language version for non-technical folks: we use Google’s servers the way you might use a bank vault. They keep the lights on, but they do not get to open the box, copy your designs, or sell your activity.
Payments and billing
- All subscriptions and checkout flows run through Stripe, a PCI Level 1 compliant payment processor trusted by millions of businesses.
- Stripe handles your payment info directly; ProFacet never stores card data. We only receive subscription status and minimal metadata needed to operate your account.
- Stripe encrypts and vaults cards, monitors fraud, and provides a customer portal so you can update or cancel billing anytime.
- If you have billing questions or want your payment data removed from Stripe, contact us and we’ll help route the request.
Storage model
- Projects live in your browser or desktop client first. They stay local unless and until you enable cloud sync.
- Cloud sync mirrors exactly the files you pick to your account so you can retrieve them on another machine; disable it and no further uploads occur.
Simulation disclaimer
- The renderer, analyzer, and optimizer are sophisticated simulations, but they are still simulations.
- Real stones will deviate because of polishing, material tolerances, lighting, and refractive index variance, so treat virtual results as guidance rather than a guarantee of optical performance.
System macros source
These CAM-related macros are compiled directly into the interpreter. The listing below is the literal define ... { ... } block the runtime parses.
define Round(angle=43, sym=8) {
// Simple round preform with adjustable symmetry.
// Parameters:
// angle: Pavilion angle for the round preform.
// sym: Symmetry count for the girdle loop.
assert ${sym} > 2 "Symmetry must be greater than 2."
assert GEAR % ${sym} == 0 "Gear index should be a multiple of the symmetry count."
G1 0 @ 90 : size x ${sym}
P1 0 @ ${angle} : cp
}
define Oval(angle=45, lwr=1.2, segmentsPerQuad=5) {
assert ${lwr} >= 1 "Length-to-width ratio must be >= 1."
assert ${segmentsPerQuad} >= 2 "segmentsPerQuad must be at least 2."
assert GEAR % 4 == 0 "Gear index should be a multiple of 4."
let width = 1
let length = ${lwr}
let rx = length / 2
let ry = width / 2
let quarter = GEAR / 4
let steps = ${segmentsPerQuad}
let stepSize = quarter / steps
let prevIdx = -1
let prevC = 0
let prevS = 0
let prevD = 0
let h = cos(${angle} * 3.14 / 90)
for k in 0..=steps {
let idx = round(k * stepSize) % GEAR
let ang = (idx / GEAR) * 2 * 3.14
let c = cos(ang)
let s = sin(ang)
let d = sqrt(rx * rx * c * c + ry * ry * s * s)
if prevIdx != -1 {
let det = prevC * s - prevS * c
let x = (prevD * s - d * prevS) / det
let y = (d * prevC - prevD * c) / det
concat(O,k) prevIdx : pt(x, y, -h) : cp xx 2 "Cut to center point"
if k == steps {
concat(O,k+1) (GEAR / 4) : pt(x, y, -h) : cp xx 2 "Cut to center point"
}
}
prevIdx = idx
prevC = c
prevS = s
prevD = d
}
G1 0 @ 90 : size "Cut to stone length"
for k in 2..=steps {
let idx = round((k - 1) * stepSize) % GEAR
concat(G,k) idx @ 90 : mp(concat(G,k-1), concat(O,k-1)) "Level girdle"
}
concat(G,steps+1) (GEAR / 4) @ 90 : mp(concat(G,steps), concat(O,steps)) "Level girdle"
}
define Rect(lwr=1.5, angle=45) {
// Creates a rectangular girdle shape
assert ${lwr} >= 1.0 "Length-to-width ratio must be >= 1."
assert GEAR % 4 == 0 "Gear index should be a multiple of 4."
let beta = rad2deg(atan(tan(deg2rad(${angle})) / ${lwr}))
let corner_index = GEAR / 4
PF1 corner_index @ beta : cp x 2
PF2 0 @ ${angle} : cp
G1 corner_index @ 90 : 0.5
G2 0 @ 90 : mp(G1, PF1, PF2)
}
define RectTr(lwr=1.4, angle=45, truncation=0.3, offset=0) {
// Rectangular girdle with truncated corners.
assert ${lwr} >= 1.0 "Length-to-width ratio must be >= 1."
assert ${truncation} > 0 "Truncation must be > 0."
assert ${truncation} <= 1 "Truncation should stay within 0-1."
assert GEAR % 4 == 0 "Gear index should be a multiple of 4."
let beta = rad2deg(atan(tan(deg2rad(${angle})) / ${lwr}))
let corner_index = GEAR / 4
PF1 corner_index @beta : cp xx 2
PF2 0 @ ${angle} : cp
G1 corner_index @ 90 : 0.5
G2 0 @ 90 : mp(G1, PF1, PF2)
PF3 (GEAR / 8 + ${offset})
: cp
: ep(edge(PF1:corner_index, G1:corner_index), ${truncation} / 2.0)
G3 (floor(GEAR / 8) + ${offset}) @ 90 : mp(G2, PF3, PF2)
}
define RectDblTr(lwr, angle, first_truncation=0.3, second_truncation=0.2, offset=0) {
// Double-truncated rectangle for octagons.
assert ${lwr} > 0 "Length-to-width ratio must be positive."
assert GEAR % 4 == 0 "Gear index should be a multiple of 4."
assert ${first_truncation} >= 0 "First truncation must be non-negative."
assert ${first_truncation} <= 1 "First truncation should stay within 0-1."
assert ${second_truncation} >= 0 "Second truncation must be non-negative."
assert ${second_truncation} <= 2 "Second truncation should stay within 0-2."
let corner_index = GEAR / 4
let split_index = floor(GEAR / 8) + ${offset}
let outer_ratio = ${first_truncation} / 2.0
let inner_ratio = ${second_truncation} / 2.0
PF1 (corner_index + ${offset}) @ rad2deg(atan(tan(deg2rad(${angle})) / ${lwr})) : cp x 2
PF2 ${offset} @ ${angle} : cp x 2
G1 (corner_index + ${offset}) @ 90 : size x 2
G2 ${offset} @ 90 : mp(G1, PF1, PF2) x 2
PF3 (GEAR / 8 + ${offset})
: cp
: ep(edge(PF1:(corner_index + ${offset}), G1:(corner_index + ${offset})), outer_ratio) xx2
G3 (floor(GEAR / 8) + ${offset}) @ 90 : mp(G2, PF3, PF2)
PF4 (floor(3 * GEAR / 16) + ${offset})
: cp
: ep(edge(PF3:split_index, G3:split_index), inner_ratio) xx2
PF5 (floor(GEAR / 16) + ${offset})
: cp
: ep(edge(PF3:split_index, G3:split_index), 1.0 - inner_ratio) xx2
G4 (floor(GEAR / 16) + ${offset}) @ 90 : mp(G3, PF5) xx2
G5 (floor(3 * GEAR / 16) + ${offset}) @ 90 : mp(G3, PF4) xx2
}
define SquareTr(angle, truncation=0.3, offset=0) {
assert ${truncation} >= 0 "Truncation must be non-negative."
assert ${truncation} <= 2 "Truncation should stay within 0-2."
assert GEAR % 4 == 0 "Gear index should be a multiple of 4."
let side_index = ${offset}
let corner_index = side_index + GEAR / 8
PF1 side_index @ ${angle} : cp x 4
G1 side_index @ 90 : size x 4
PF2 corner_index
: cp
: ep(edge(PF1:side_index, G1:side_index), 1.0 - ${truncation} / 2.0) x 4
G2 corner_index @ 90 : mp(G1, PF2) x 4
}
define SquareCushTr(cushion=1, angle, truncation=0.4) {
assert ${cushion} >= 1 "Index offset must be 1 or more."
assert GEAR % 4 == 0 "Gear index should be a multiple of 4."
let side_index = ${cushion}
let corner_index = GEAR / 8
G1 side_index @ 90 : size xx4
PF1 side_index @ ${angle} : cp xx4
PF2 corner_index
: cp
: ep(edge(PF1:side_index, G1:side_index), 1 - ${truncation} / 2.0) x4
G2 corner_index @ 90 : mp(G1,PF2,PF1)
}
define PentCushTr(offset=1, angle, truncation=0.3) {
assert ${offset} >= 1 "Index offset must be at least 1."
assert ${truncation} >= 0 "Truncation must be non-negative."
assert ${truncation} <= 1 "Truncation should stay within 0-1."
assert GEAR % 10 == 0 "Gear index should be a multiple of 10."
let side_index = ${offset}
let corner_index = GEAR / 10
G1 side_index @ 90 : size xx5
PF1 side_index @ ${angle} : cp xx5
PF2 corner_index
: cp
: ep(edge(PF1:side_index, G1:side_index), 1.0 - ${truncation}) xx5
G2 corner_index @ 90 : mp(G1, PF2) xx5
}
define Shield(angle=45) {
// Mirrored shield outline with threefold symmetry.
// Parameters:
// angle: Pavilion angle for the shield facets.
assert GEAR % 6 == 0 "Gear index should be a multiple of 6."
P1 6 @ ${angle} : cp xx 3
G1 6 @ 90 : size xx 3
}
define TriTr(truncation=0.25, angle) {
assert ${truncation} >= 0 "Truncation must be non-negative."
assert ${truncation} < 2 "Truncation must stay below 2."
assert GEAR % 6 == 0 "Gear index should be a multiple of 6."
PF1 0 @ ${angle} : cp x 3
G1 0 @ 90 : 0.7 x 3
PF2 GEAR / 6
: cp
: ep(edge(PF1:0, G1:0), 1 - ${truncation} / 2) x 3
G2 GEAR / 6 @ 90 : mp(G1, PF1, PF2)
}
define TriCushTr(truncation=0.25, angle, cushion=1) {
assert ${truncation} >= 0 "Truncation must be non-negative."
assert ${truncation} < 2 "Truncation must stay below 2."
assert ${cushion} >= 0 "Cushion offset must be non-negative."
assert GEAR % 6 == 0 "Gear index should be a multiple of 6."
PF1 ${cushion} @ ${angle} : cp xx3
G1 ${cushion} @ 90 : 0.7 xx3
PF2 GEAR / 6
: cp
: ep(edge(PF1:${cushion}, G1:${cushion}), 1 - ${truncation} ) x 3
G2 GEAR / 6 @ 90 : mp(G1, PF1, PF2)
}
define TriCurved(angle=45, c1=2,c2=4) {
// Trilliant Cut curved by amount c1 and c2.
set cube 3.4
let i1 = ${c1}
let i2 = ${c2}
assert i1 < i2 "c1 < c2"
assert GEAR % i1 == 0 "c1 must be divisible by gear"
assert GEAR % i2 == 0 "c2 must be divisible by gear"
PF1 i1 @${angle}: cp xx3
G1 i1 @90 : size
PF2 i2 : cp : ep(edge(PF1:i1,G1:i1),0.5) "Cut to centerpoint"
G2 i2 @90 : mp(PF1,PF2,G1)
}





















































