Functional Icosahedron
FSL is a powerful functional language that allows for procedural generation of complex geometry. This example demonstrates how to construct a Platonic solid (Icosahedron) using vector mathematics and functional programming patterns (Map/Reduce), rather than hardcoding tier definitions.
The Concept
An Icosahedron has 20 faces. The face normals correspond to the vertices of a Dodecahedron. Instead of manually calculating angles and indices for each face, we:
- Define the 20 vectors of a Dodecahedron mathematically.
- Convert these vectors into Faceting Machine coordinates (Index, Angle, Side).
- Execute the cuts using a loop.
This approach is resolution-independent. Since the angles involve the Golden Ratio ($\phi$), they are irrational and do not align perfectly with standard gears (like 96). To demonstrate the precision of FSL, we use a very high gear setting (360,000) to approximate these angles nearly perfectly.
The Code
// Functional Icosahedron Generator
// Demonstrates: Vector Geometry, Map/Reduce, and Dynamic Tiers
// --- Configuration ---
// We use a high gear to minimize rounding errors for irrational angles
gear = 360000
width = 1.0 // Distance from center to face
// Golden Ratio
phi = (1 + Math.sqrt(5)) / 2
// --- Helper Functions ---
// Flatten an array of arrays
flatten = (arr) => arr.reduce((acc, val) => concat(acc, val), [])
// Map a function over an array and flatten the result
flatMap = (arr, fn) => flatten(arr.map(fn))
// Generate 3 cyclic permutations of a vector [x, y, z]
permute = (v) => [
[v[0], v[1], v[2]],
[v[1], v[2], v[0]],
[v[2], v[0], v[1]]
]
// Convert a Cartesian Vector to Machine Coordinates [Index, Angle, Side]
toMachine = (v) => {
x = v[0]
y = v[1]
z = v[2]
mag = Math.sqrt(x*x + y*y + z*z)
// Azimuth (Index)
// atan2 returns radians -PI to PI
az_rad = Math.atan2(y, x)
az_norm = az_rad < 0 ? az_rad + 2 * Math.PI : az_rad
// Map to gear index
idx_raw = (az_norm / (2 * Math.PI)) * gear
idx = Math.round(idx_raw) % gear
// Elevation (Angle)
// angle from XY plane (Equator)
lat_rad = Math.asin(z / mag)
lat_deg = lat_rad * 180 / Math.PI
// Convert to Faceting Angle (0 = Top/Pole, 90 = Girdle)
// We mirror bottom hemisphere angles to match 0-90 range
final_angle = 90 - Math.abs(lat_deg)
// Determine side. Note: Use semicolons to avoid array indexing ambiguity.
cutSide = cond(z >= 0, () => "up", () => "down");
[idx, final_angle, cutSide]
}
// --- Geometry Generation ---
pm = [-1, 1] // Plus/Minus variations
// Group 1: Cube vertices (+-1, +-1, +-1)
// Cartesian product of pm, pm, pm
g1 = flatMap(pm, x =>
flatMap(pm, y =>
pm.map(z => [x, y, z])
)
)
// Group 2: Cyclic permutations of (0, +-1/phi, +-phi)
invPhi = 1 / phi
g2_base = flatMap(pm, y =>
pm.map(z => [0, y * invPhi, z * phi])
)
// Apply permutations (x,y,z) -> (y,z,x) -> (z,x,y)
g2 = flatMap(g2_base, v => permute(v))
// Combine all 20 vectors
vectors = concat(g1, g2)
// Convert to machine commands
commands = vectors.map(toMachine)
// Separate by side
pavilion = commands.filter(c => c[2] == "down")
crown = commands.filter(c => c[2] == "up")
// --- Execution ---
print("Generating Icosahedron...")
print("Total Faces: ", commands.length)
// 1. Cut Pavilion (Side defaults to Down)
print("Cutting Pavilion (", pavilion.length, " faces)")
setSide("down")
pavilion.forEach(cmd => {
// cut(angle, index, tier_name, depth)
cut(cmd[1], cmd[0], "Pavilion", width)
})
// 2. Cut Crown (Switch to Up)
print("Cutting Crown (", crown.length, " faces)")
setSide("up")
crown.forEach(cmd => {
cut(cmd[1], cmd[0], "Crown", width)
})
print("Done!")
Explanation
- Vector Generation: We mathematically define the 20 normal vectors of an Icosahedron.
- 8 from the corners of a cube: $(\pm 1, \pm 1, \pm 1)$
- 12 from the "roof" permutations: $(0, \pm \frac{1}{\phi}, \pm \phi)$
- Coordinate Transformation: The
toMachinefunction handles the complex math of converting a 3D vector into theIndex(azimuth) andAngle(elevation) required by the faceting machine. - Side Switching: We use
setSide("up")andsetSide("down")to control the orientation of the machine before executing the cuts using the low-levelcut()function.
This script generates a highly precise Icosahedron by utilizing a high-resolution gear (360,000) to approximate the irrational angles inherent in the Golden Ratio geometry.