Cord/docs/cordial-reference.md

352 lines
8.6 KiB
Markdown

# Cordial Language Reference
Cordial is the primary source language for the Cord geometry system.
It compiles to a trigonometric intermediate representation (TrigGraph)
which can be evaluated as an f64 reference, compiled to WGSL shaders
for GPU raymarching, or lowered to pure CORDIC shift-and-add arithmetic.
File extension: `.crd`
---
## Variables
```
let r = 5
let height = 2 * pi
let s: Obj = sphere(r)
```
- `let` introduces a new variable.
- `: Obj` type annotation marks a variable as a renderable 3D object.
- Variables can be reassigned: `r = 10` (no `let` on reassignment).
---
## Constants
| Name | Value |
|-----------|---------------|
| `pi`, `PI`| 3.14159... |
| `e`, `E` | 2.71828... |
| `x` | Input X coord |
| `y` | Input Y coord |
| `z` | Input Z coord |
| `reg` | NaN register |
`x`, `y`, `z` are the spatial coordinates — use them to build
mathematical expressions and SDF fields directly.
---
## Arithmetic
| Syntax | Operation |
|---------|----------------|
| `a + b` | Addition |
| `a - b` | Subtraction |
| `a * b` | Multiplication |
| `a / b` | Division |
| `a ^ b` | Power (²,³ optimized) |
| `-a` | Negation |
Precedence: unary > power > multiplicative > additive.
Parentheses for grouping: `(a + b) * c`.
---
## Comments
```
// line comment
/= also a line comment
/* block comment */
/* nested /* block */ comments */
```
---
## Trig Functions
| Function | Aliases | Description |
|----------|---------|-------------|
| `sin(x)` | | Sine |
| `cos(x)` | | Cosine |
| `tan(x)` | | Tangent |
| `asin(x)` | `arcsin` | Inverse sine |
| `acos(x)` | `arccos`, `arcos` | Inverse cosine |
| `atan(x)` | `arctan` | Inverse tangent |
| `sinh(x)` | | Hyperbolic sine |
| `cosh(x)` | | Hyperbolic cosine |
| `tanh(x)` | | Hyperbolic tangent |
| `asinh(x)` | `arcsinh` | Inverse hyperbolic sine |
| `acosh(x)` | `arccosh`, `arcosh` | Inverse hyperbolic cosine |
| `atanh(x)` | `arctanh` | Inverse hyperbolic tangent |
---
## Math Functions
| Function | Aliases | Description |
|----------|---------|-------------|
| `sqrt(x)` | | Square root |
| `exp(x)` | | e^x |
| `ln(x)` | `log` | Natural logarithm |
| `abs(x)` | | Absolute value |
| `hypot(a, b)` | | √(a² + b²) |
| `atan2(y, x)` | | Two-argument arctangent |
| `min(a, b)` | | Minimum |
| `max(a, b)` | | Maximum |
| `length(a, b)` | `mag` | Magnitude (2D or 3D) |
| `mix(a, b, t)` | `lerp` | Linear interpolation |
| `clip(x, lo, hi)` | `clamp` | Clamp to range |
| `smoothstep(e0, e1, x)` | | Hermite interpolation |
| `quantize(x, step)` | | Snap to grid |
---
## SDF Primitives
These construct signed distance fields centered at the origin.
All dimensions are half-extents (centered geometry).
| Function | Arguments | Description |
|----------|-----------|-------------|
| `sphere(r)` | radius | Sphere |
| `box(hx, hy, hz)` | half-extents | Axis-aligned box |
| `cylinder(r, h)` | radius, half-height | Z-axis cylinder |
| `ngon(n, side)` | sides (≥3), side length | Regular polygon prism |
| `N-gon(side)` | side length | Shorthand: `6-gon(2)` = hexagonal prism |
---
## Transforms
Transforms take an SDF as the first argument and modify its coordinate space.
| Function | Aliases | Arguments | Description |
|----------|---------|-----------|-------------|
| `translate(sdf, tx, ty, tz)` | `mov`, `move` | SDF + offsets | Translate |
| `rotate_x(sdf, angle)` | `rx` | SDF + radians | Rotate around X |
| `rotate_y(sdf, angle)` | `ry` | SDF + radians | Rotate around Y |
| `rotate_z(sdf, angle)` | `rz` | SDF + radians | Rotate around Z |
| `scale(sdf, factor)` | | SDF + uniform scale | Uniform scale |
| `mirror_x(sdf)` | `mx` | SDF | Mirror across YZ plane |
| `mirror_y(sdf)` | `my` | SDF | Mirror across XZ plane |
| `mirror_z(sdf)` | `mz` | SDF | Mirror across XY plane |
---
## CSG Boolean Operations
| Function | Aliases | Arguments | Description |
|----------|---------|-----------|-------------|
| `union(a, b)` | | two SDFs | Union (min) |
| `intersect(a, b)` | | two SDFs | Intersection (max) |
| `diff(a, b)` | `subtract` | two SDFs | Difference (max(a, -b)) |
---
## Waveform Functions
| Function | Arguments | Description |
|----------|-----------|-------------|
| `saw(x)` | input | Sawtooth wave |
| `tri(x)` | input | Triangle wave |
| `square(x)` | input | Square wave |
---
## DSP / Signal Functions
| Function | Arguments | Description |
|----------|-----------|-------------|
| `am(signal, carrier, depth)` | | Amplitude modulation |
| `fm(signal, carrier, index)` | | Frequency modulation |
| `lpf(signal, cutoff)` | | Low-pass filter approximation |
| `hpf(signal, cutoff)` | | High-pass filter approximation |
| `bpf(signal, lo, hi)` | | Band-pass filter approximation |
| `dft(signal, n)` | | Discrete Fourier approximation |
| `hilbert(x)` | `envelope` | Analytic signal envelope |
| `phase(x)` | | Instantaneous phase |
---
## User-Defined Functions
```
f(a, b) = a^2 + b^2
let result = f(3, 4)
```
Define with `name(params) = body`. Body extends to the next
newline or semicolon. Functions are expanded inline at each call site.
---
## Schematics (`sch`)
Schematics are parameterized multi-statement blocks — like functions but
with full block bodies containing `let` bindings, intermediate variables,
and arbitrary nesting. The last expression is the return value.
```
sch Bracket(w, h, t) {
let plate: Obj = box(w, h, t)
let rib: Obj = box(t, h/2, t)
union(plate, translate(rib, w/2, 0, 0))
}
let b = Bracket(10, 5, 0.5)
cast(b)
```
Schematics can call other schematics and user-defined functions.
They can contain any number of statements.
```
sch Peg(r, h) {
let shaft = cylinder(r, h)
let head = translate(sphere(r * 1.5), 0, 0, h)
union(shaft, head)
}
sch PegRow(n, spacing) {
map(i, 0..n) { translate(Peg(0.5, 3), i * spacing, 0, 0) }
}
```
---
## Iteration (`map`)
`map` evaluates a body for each integer in a range and unions all
results. Iteration is unrolled at parse time — the TrigGraph is a DAG
with no runtime loops.
```
map(variable, start..end) { body }
```
- `variable` — bound to each integer in `[start, end)`
- `start..end` — exclusive range; bounds must resolve to constants
- `body` — any expression or block; can reference the iteration variable
- All iterations are unioned (min)
- Max 1024 iterations
### Examples
```
// Row of 5 spheres along X
let row = map(i, 0..5) { translate(sphere(1), i * 3, 0, 0) }
// Ring of 8 spheres
let ring = map(i, 0..8) {
rotate_z(translate(sphere(0.5), 5, 0, 0), i * pi/4)
}
// Grid using nested maps inside a schematic
sch Grid(nx, ny, spacing) {
map(i, 0..nx) {
map(j, 0..ny) {
translate(sphere(0.4), i * spacing, j * spacing, 0)
}
}
}
let g = Grid(4, 6, 2)
cast()
```
Since `map` is an expression, it works anywhere: inside `let` bindings,
as arguments to functions, inside schematic bodies, or nested in other maps.
---
## Rendering with `cast()`
Nothing renders without an explicit `cast()` call.
| Syntax | Effect |
|--------|--------|
| `cast()` | Render all defined variables |
| `cast(name)` | Render a specific variable |
| `name.cast()` | Dot syntax (Obj-typed variables only) |
Multiple `cast()` calls accumulate. The GUI provides a Render button
that inserts `cast()` automatically when new variables exist since
the last cast.
### Example
```
let a: Obj = sphere(3)
let b: Obj = box(4, 2, 1)
let c: Obj = translate(a, 5, 0, 0)
cast()
```
This renders `a`, `b`, and `c` as a union. Without `cast()`, nothing appears.
---
## Plotting with `plot()`
| Syntax | Effect |
|--------|--------|
| `plot()` | Plot all bare expressions |
| `plot(expr)` | Plot a specific expression |
The GUI provides a Plot button that inserts `plot()` when new
expressions exist since the last plot.
### Example
```
sin(x) * exp(-x^2)
plot()
```
---
## Complete Example
```
// Bolt head: hexagonal prism with a sphere on top
let head: Obj = 6-gon(3)
let dome: Obj = translate(sphere(3.2), 0, 0, 1.5)
let cap: Obj = intersect(head, dome)
// Shaft
let shaft: Obj = cylinder(1.2, 8)
let bolt: Obj = union(translate(cap, 0, 0, 8), shaft)
// Cross hole
let slot: Obj = box(0.4, 3, 10)
let slot2: Obj = rotate_z(slot, pi/2)
let cross: Obj = union(slot, slot2)
let final: Obj = diff(bolt, cross)
cast(final)
```
### Schematics + Iteration
```
// Reusable peg schematic
sch Peg(r, h) {
let shaft = cylinder(r, h)
let head = translate(sphere(r * 1.5), 0, 0, h)
union(shaft, head)
}
// Base plate with a ring of pegs
let plate: Obj = box(20, 20, 1)
let pegs: Obj = map(i, 0..8) {
rotate_z(translate(Peg(0.5, 3), 8, 0, 1), i * pi/4)
}
let assembly: Obj = union(plate, pegs)
cast(assembly)
```