Chapter 13: Math, Variables, Vectors, Planes & RI-Adaptive Optics
Most simple designs only need numbers and point helpers (cp(), gp(), mp()). When you want to write a material-adaptive cut that recalculates its own Pavilion angles from ri, or construct a custom CAM outline using 3D geometry, ProFacet gives you a rich mathematical toolkit in both FSL and Lua.
1. Variables, Optimizable Numbers (+/- & val()) & Arrays
A. Defining Variables & Optimizable Ranges
- In FSL: Assign any value directly with
=(noletorlocalkeyword needed), and attach+/-to any number that the GPU Optimizer is allowed to tune:baseAngle = 41.5 +/- 1.5 stepDelta = 1.8 - In Lua: Use standard
localvariables, and wrap optimizable numbers inval(initial, delta)(orval(initial, "2%")/val(initial, min, max)):local baseAngle = val(41.5, 1.5) local stepDelta = 1.8
B. Arrays & Numeric Sequences (Math.range)
In FSL, square brackets create zero-indexed arrays, and Math.range(start, end, step) generates a sequence of numbers from start (inclusive) up to end (exclusive):
teeth = [0, 12, 24, 36](teeth[0]is0,teeth.length()is4)steps = Math.range(0, 8, 1)expands to[0, 1, 2, 3, 4, 5, 6, 7]
In Lua, curly braces create 1-indexed tables:
local teeth = {0, 12, 24, 36}(teeth[1]is0,#teethis4)
2. Trigonometry & the Math Namespace
Both FSL (Math.*) and Lua (math.*) provide a complete set of trigonometric and algebraic functions, along with built-in degree/radian converters:
| Operation | FSL (Math.*) | Lua (math.*) |
|---|---|---|
| Degrees ↔ Radians | Math.toRadians(deg), Math.toDegrees(rad) | math.rad(deg), math.deg(rad) |
| Sine / Cosine / Tangent | Math.sin(rad), Math.cos(rad), Math.tan(rad) | math.sin(rad), math.cos(rad), math.tan(rad) |
| Inverse Trig | Math.asin(x), Math.acos(x), Math.atan(x), Math.atan2(y, x) | math.asin(x), math.acos(x), math.atan(y, x) |
| Square Root & Power | Math.sqrt(x), Math.pow(base, exp) | math.sqrt(x), base ^ exp |
| Absolute, Min, Max | Math.abs(x), Math.min(a, b), Math.max(a, b) | math.abs(x), math.min(a, b), math.max(a, b) |
| Rounding | Math.floor(x), Math.ceil(x), Math.round(x) | math.floor(x), math.ceil(x), math.floor(x + 0.5) |
| Range Generator | Math.range(start, end, step) | for i = start, stop, step do ... end |
| Constants | Math.PI (3.14159265...), Math.E | math.pi, math.exp(1) |
3. RI-Adaptive Pavilion Design: Computing Angles from ri
Remember from Chapter 1 that a gemstone's critical angle is . Because ri is readable right inside your script, you can compute dynamically and set your Pavilion culet and break angles to automatically track the material!
Try changing 1.54 (Quartz) to 1.76 (Sapphire) or 2.42 (Diamond) in the live playground below and clicking Run—watch print() log the new angles and reshape the Pavilion cone automatically:
name = "RI-Adaptive Portuguese Cut"
ri = 1.54 // Try 1.54 (Quartz), 1.76 (Sapphire), or 2.42 (Diamond)!
gear = 96
cube = 3
color = "#38bdf8"
G1 0 @ 90.0 : size x16
// Compute critical angle in degrees and set culet 2.2° above critical!
crit = Math.toDegrees(Math.asin(1.0 / ri))
culet = Math.max(39.5, crit + 2.2)
print("RI:", ri, "-> Critical Angle:", Math.round(crit * 100) / 100, "-> Culet Angle:", Math.round(culet * 100) / 100)
// 3-tier Pavilion stepping down to the RI-adaptive culet
P1 0 @ (culet + 12.0) : cp() x16
P2 3 @ (culet + 6.0) : gp() x16
P3 0 @ culet : mp(P1) x16
// Crown tiers
C1 0 @ 44.0 : mp(P1) + girdle x16
C2 3 @ 39.0 : gp() x16
C3 0 @ 32.0 : mp(C1) x16
T 0 @ 0.0 : mp(C2) x1
-- lua script
name("RI-Adaptive Portuguese Cut")
local mat_ri = 1.54 -- Try 1.54, 1.76, or 2.42!
ri(mat_ri)
gear(96)
cube(3)
color("#38bdf8")
TierAD("G1", 0, 90.0, 1.0, "x16")
-- Compute critical angle in degrees
local crit = math.deg(math.asin(1.0 / mat_ri))
local culet = math.max(39.5, crit + 2.2)
print("RI:", mat_ri, "-> Critical Angle:", math.floor(crit * 100 + 0.5) / 100, "-> Culet Angle:", math.floor(culet * 100 + 0.5) / 100)
TierAP("P1", 0, culet + 12.0, cp(), "x16")
TierAP("P2", 3, culet + 6.0, gp(), "x16")
TierAP("P3", 0, culet, mp("P1"), "x16")
TierAP("C1", 0, 44.0, mp("P1"):add(girdle), "x16")
TierAP("C2", 3, 39.0, gp(), "x16")
TierAP("C3", 0, 32.0, mp("C1"), "x16")
TierAP("T", 0, 0.0, mp("C2"), "x1")
4. 3D Geometric Primitives: Point, Vector, and Plane
When writing custom CAM outlines or geometric constructions, you can work directly with 3D points, vectors, and planes in both FSL and Lua:
| Type & Constructor | Properties & Operations | FSL & Lua Examples |
|---|---|---|
Point(x, y, z) | .x, .y, .z, .distance(p2)• FSL: p + v -> Point, p1 - p2 -> Vector• Lua: p:add(v) -> Point, p1:sub(p2) -> Vector | FSL: shifted = gp() + Vector(0, 0, 0.03)Lua: local shifted = gp():add(Vector(0, 0, 0.03)) |
Vector(x, y, z) | .x, .y, .z, .length(), .normalize().dot(v2), .cross(v2)• FSL: v * s, v / s, -v• Lua: v:mul(s), v:div(s), v:unm() | FSL: n = Vector(0, 1, -1).normalize()Lua: local n = Vector(0, 1, -1):normalize() |
Plane(origin, normal)Plane(p1, p2, p3) | Constructs a 3D plane from an origin Point + normal Vector, or from 3 non-collinear Points..origin, .normal, .distance(pt), .project(pt), .intersect(rayOrigin, rayDir) | pl = Plane(Point(0, 0, 0), Vector(0, 0, 1))hit = pl.intersect(Point(0.5, 0, 1), Vector(0, 0, -1)) |
Visual Debugging with show() and print()
print(val1, val2, ...): Logs numbers, strings, points, or stone metrics to the Script Output console (Cmd/Ctrl + Lin Studio, or right below the playground in this book).show(point_or_edge, "label", "#hexColor"): Renders a bright 3D marker at aPointor a glowing line along anedge()right inside the 3D Viewport!
Enforcing Geometric Guardrails with assert(condition, message)
assert(expr, "reason"): Verifies thatexpristrue. Ifexprisfalse:- During normal editing, it stops and highlights the assertion message.
- During a GPU Optimization run, any candidate stone that violates an
assert()is automatically penalized and rejected!
Next, see how FSL’s functional programming features (=> lambdas, map, filter, reduce, and forEach) let you build multi-tier patterns and even the Platonic Solids in Chapter 14: Functional Programming in FSL — Lambdas & Pipelines.