Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 13: Math, Variables, Vectors, Planes & RI-Adaptive Optics

PART II · CHAPTER 13 Variables & Arrays · Math Namespace · RI-Driven Angles · 3D Vectors, Points & Planes

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 = (no let or local keyword 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 local variables, and wrap optimizable numbers in val(initial, delta) (or val(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] is 0, teeth.length() is 4)
  • 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] is 0, #teeth is 4)

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:

OperationFSL (Math.*)Lua (math.*)
Degrees ↔ RadiansMath.toRadians(deg), Math.toDegrees(rad)math.rad(deg), math.deg(rad)
Sine / Cosine / TangentMath.sin(rad), Math.cos(rad), Math.tan(rad)math.sin(rad), math.cos(rad), math.tan(rad)
Inverse TrigMath.asin(x), Math.acos(x), Math.atan(x), Math.atan2(y, x)math.asin(x), math.acos(x), math.atan(y, x)
Square Root & PowerMath.sqrt(x), Math.pow(base, exp)math.sqrt(x), base ^ exp
Absolute, Min, MaxMath.abs(x), Math.min(a, b), Math.max(a, b)math.abs(x), math.min(a, b), math.max(a, b)
RoundingMath.floor(x), Math.ceil(x), Math.round(x)math.floor(x), math.ceil(x), math.floor(x + 0.5)
Range GeneratorMath.range(start, end, step)for i = start, stop, step do ... end
ConstantsMath.PI (3.14159265...), Math.Emath.pi, math.exp(1)

3. RI-Adaptive Pavilion Design: Computing Angles from ri

Remember from Chapter 1 that a gemstone's critical angle is θc=arcsin(1/RI). Because ri is readable right inside your script, you can compute θc 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 & ConstructorProperties & OperationsFSL & 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 + L in Studio, or right below the playground in this book).
  • show(point_or_edge, "label", "#hexColor"): Renders a bright 3D marker at a Point or a glowing line along an edge() right inside the 3D Viewport!

Enforcing Geometric Guardrails with assert(condition, message)

  • assert(expr, "reason"): Verifies that expr is true. If expr is false:
    • 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.