// 24-Cell Orthographic Projection (Rhombic Dodecahedron Envelope)
// Projected vertex-first from 4D to 3D.

$fn = 24; 

// Scale by ~61% to match the volume of the perspective projection.
scale_factor = 80 * 0.61;
thickness = 1.5;   // Radius of the "sausages"

// --- 1. Generate Base 4D Vertices ---
v4 = [
    for (i = [0:15]) [
        (i % 2) > 0 ? 0.5 : -0.5,
        (floor(i / 2) % 2) > 0 ? 0.5 : -0.5,
        (floor(i / 4) % 2) > 0 ? 0.5 : -0.5,
        (floor(i / 8) % 2) > 0 ? 0.5 : -0.5
    ],
    [1,0,0,0], [-1,0,0,0], 
    [0,1,0,0], [0,-1,0,0], 
    [0,0,1,0], [0,0,-1,0], 
    [0,0,0,1], [0,0,0,-1]
];

// --- 2. Determine Edges ---
edges = [
    for (i = [0:23])
        for (j = [i+1:23])
            let (
                d2 = pow(v4[i][0] - v4[j][0], 2) + 
                     pow(v4[i][1] - v4[j][1], 2) + 
                     pow(v4[i][2] - v4[j][2], 2) + 
                     pow(v4[i][3] - v4[j][3], 2)
            )
            if (abs(d2 - 1.0) < 0.01) [i, j]
];

// --- 3. Orthographic Projection to 3D ---
v3 = [
    for (v = v4)
        [v[0], v[1], v[2]] * scale_factor
];

// --- 4. Render as "Sausages" ---
module sausage(p1, p2, r) {
    hull() {
        translate(p1) sphere(r);
        translate(p2) sphere(r);
    }
}

// --- 5. Orientation and Final Output ---
// The distance from the origin to a rhombic face center is exactly 1/sqrt(2)
z_offset = (1 / sqrt(2)) * scale_factor;

// Translate the shape up so that it perfectly rests on the Z=0 plane
translate([0, 0, z_offset + thickness]) {
    // Nested rotations force Z to evaluate before Y. 
    // This points the (0.5, 0.5, 0) face normal perfectly straight down.
    rotate([0, 90, 0]) {
        rotate([0, 0, -45]) {
            for (e = edges) {
                sausage(v3[e[0]], v3[e[1]], thickness);
            }
        }
    }
}