Cord/docs/scad-to-cordial.md

8.9 KiB
Raw Blame History

SCAD → Cordial Translation Reference

Every SCAD operation has a Cordial equivalent. Where the operation can be parallelized, the conditions and a Cordial example are shown.


Primitives

sphere

SCAD

sphere(r=5);
sphere(5);

Cordial

sphere(5.0)

Parallelism: n/a — leaf node; always evaluable at any point independently.


cube

SCAD

cube([10, 20, 30]);
cube(5);
cube([10, 20, 30], center=true);

Cordial

box3(5.0, 10.0, 15.0)   // half-extents, always centered
cube(5.0)                // equal half-extents

Note: SCAD cube() uses full sizes and defaults to corner-aligned. Cordial uses half-extents and is always centered. The lowerer handles the translation offset automatically when ingesting SCAD.


cylinder

SCAD

cylinder(h=10, r=3);
cylinder(h=10, r=3, center=true);

Cordial

cylinder(3.0, 10.0)   // always centered on Z

Transforms

translate

SCAD

translate([10, 0, 0]) sphere(1);

Cordial

sphere(1.0).translate(10.0, 0.0, 0.0)

Parallelism: a translation wraps its child — the child subtree is independently evaluable. Multiple translates on independent shapes are always parallel.


rotate

SCAD

rotate([45, 0, 0]) cube(5);
rotate([0, 90, 0]) cube(5);

Cordial

cube(5.0).rotate_x(45.0)
cube(5.0).rotate_y(90.0)

SCAD rotate([x,y,z]) decomposes into sequential X, Y, Z rotations. Cordial exposes each axis independently.


scale

SCAD

scale([2, 1, 1]) sphere(1);
scale(3) sphere(1);

Cordial

sphere(1.0).scale(2.0, 1.0, 1.0)
sphere(1.0).scale_uniform(3.0)

Boolean Operations

union

SCAD

union() {
    sphere(1);
    cube(2);
}

Cordial

sphere(1.0) | cube(2.0)

// or explicitly:
sphere(1.0).union(cube(2.0))

// or variadic:
Shape::union_all([sphere(1.0), cube(2.0), cylinder(1.0, 3.0)])

Parallelism: always parallelizable.

Condition: union children share no intermediate state. In an SDF, every child is a separate distance field — min(d_a, d_b) evaluates d_a and d_b independently.

// Parallel union — each branch evaluates on its own thread
par::branch()
    .add(sphere(1.0))
    .add(cube(2.0).translate(5.0, 0.0, 0.0))
    .add(cylinder(1.0, 3.0).rotate_x(90.0))
    .union()

difference

SCAD

difference() {
    cube(10, center=true);
    sphere(5);
}

Cordial

cube(10.0) - sphere(5.0)

// or explicitly:
cube(10.0).difference(sphere(5.0))

// or multiple subtractions:
cube(10.0).difference_all([sphere(5.0), cylinder(2.0, 20.0)])

Parallelism: parallelizable when operands are independent subtrees.

Condition: the base shape and the subtracted shapes share no intermediate nodes. The base evaluates independently from the subtracted shapes. The subtracted shapes themselves are a union of independent evaluations (each contributes to max(base, -sub)).

// The base and each subtracted shape are independent branches
par::branch()
    .add(cube(10.0))
    .add(sphere(5.0))
    .add(cylinder(2.0, 20.0))
    .intersection()  // difference = intersection with complement

intersection

SCAD

intersection() {
    sphere(5);
    cube(4, center=true);
}

Cordial

sphere(5.0) & cube(4.0)

Parallelism: parallelizable when operands are independent subtrees.

Same condition as difference — max(d_a, d_b) evaluates both sides independently.

par::branch()
    .add(sphere(5.0))
    .add(cube(4.0))
    .intersection()

Control Flow

for loop

SCAD

for (i = [0:5])
    translate([i*10, 0, 0]) sphere(1);

for (i = [0:2:10])
    translate([i, 0, 0]) sphere(1);

for (x = [1, 5, 10])
    translate([x, 0, 0]) cube(2);

Cordial (.crd source)

// Linear array — 6 spheres spaced 10 apart
map(i, 0..6) { translate(sphere(1), i * 10, 0, 0) }

// 8 bolts around a circle
map(i, 0..8) { rotate_z(translate(cylinder(0.5, 2), 5, 0, 0), i * pi/4) }

Cordial (Rust DSL)

pattern::linear_array(&sphere(1.0), 6, [10.0, 0.0, 0.0])

par::polar(&cylinder(0.5, 2.0).translate(5.0, 0.0, 0.0), 8)

Parallelism: always parallelizable when bounds are constant.

map unrolls at parse time into N independent branches joined by union. Each iteration is independent — no iteration reads state written by another. This is the fundamental serial-to-parallel transformation: what looks like a sequential loop is actually N independent geometric evaluations.


if / else

SCAD

if (use_sphere)
    sphere(5);
else
    cube(5, center=true);

x = 10;
if (x > 5) sphere(x);

Cordial — direct conditional geometry isn't needed because Rust has native if:

let shape = if use_sphere {
    sphere(5.0)
} else {
    cube(5.0)
};

Parallelism: constant conditions → dead code elimination.

Condition: if the condition evaluates to a constant at lowering time, only the taken branch produces geometry. The other branch is eliminated entirely — zero cost.

When the condition is variable (unknown at compile time), both branches are included as a union. This is conservative but correct — the SDF field is defined everywhere.


Ternary

SCAD

r = big ? 10 : 1;
sphere(r);

Cordial — native Rust:

let r = if big { 10.0 } else { 1.0 };
sphere(r)

Evaluated at lowering time when all inputs are constant.


Patterns (Cordial-only)

These have no direct SCAD equivalent — they're higher-level abstractions that compile to parallel-friendly structures.

linear_array

// 5 spheres along X, spaced 3 units apart
pattern::linear_array(&sphere(1.0), 5, [3.0, 0.0, 0.0])

Always parallel — each instance is independent.

polar_array

// 12 bolts around Z
pattern::polar_array(&cylinder(0.3, 2.0).translate(5.0, 0.0, 0.0), 12)

Always parallel — equivalent to N rotations of the same shape.

grid_array

// 4x6 grid of cylinders
pattern::grid_array(&cylinder(0.5, 1.0), 4, 6, 3.0, 3.0)

Always parallel — N×M independent instances.

mirror

pattern::mirror_x(&sphere(1.0).translate(3.0, 0.0, 0.0))
// Original at (3,0,0) + mirror at (-3,0,0)

Always parallel — 2 branches, one original, one reflected.


Parallel Composition (Cordial-only)

par::branch — explicit parallel grouping

// N independent shapes, explicitly grouped for parallel evaluation
let part = par::branch()
    .add(sphere(2.0))
    .add(cylinder(1.0, 5.0).translate(0.0, 0.0, 1.0))
    .add(cube(1.5).rotate_z(45.0).translate(3.0, 0.0, 0.0))
    .union();

Each .add() is an independent branch. The join operation (.union(), .intersection(), .smooth_union(k)) combines results after all branches complete.

par::map — multiplicative parallelism

// Same shape, N different transforms — all independent
par::map(&sphere(0.5), (0..20).map(|i| {
    let t = i as f64 / 20.0;
    let x = (t * std::f64::consts::TAU).cos() * 5.0;
    let y = (t * std::f64::consts::TAU).sin() * 5.0;
    let z = t * 10.0;
    move |s: Shape| s.translate(x, y, z)
}))

par::symmetric — mirror parallelism

par::symmetric_x(&part)     // 2 branches
par::symmetric_xyz(&part)   // 8 branches (all octants)

par::polar — rotational parallelism

par::polar(&fin, 6)   // 6 branches around Z

Parallelism Summary

Operation Parallelizable? Condition
Primitive (sphere, cube, etc.) Always Leaf node — independent by definition
Transform (translate, rotate, scale) Always Wraps child; child evaluates independently
Union Always min(a, b) — operands share no state
Difference Yes Operands are independent subtrees
Intersection Yes Operands are independent subtrees
For loop (constant bounds) Always Unrolls to N independent branches
For loop (variable bounds) No Cannot unroll at compile time
If/else (constant condition) n/a Dead code eliminated; only one branch exists
If/else (variable condition) Yes Both branches included as union
par::branch Always Explicit parallel grouping
par::map Always Same shape, N transforms
par::polar Always N rotations around axis
pattern::* Always Compile to union of independent instances

The threshold: an operation becomes parallelizable when it crosses into calculus — when accumulated structure becomes continuous and differentiable, every point in the field is independently evaluable. SDFs are inherently in this territory: the distance function is defined at every point in space, and evaluating it at point A tells you nothing about point B. Serial operations that build up an SDF tree are just describing the function — once described, evaluation is embarrassingly parallel.