Dodecahedron
// 3×3×3 hollow dodecahedron cube – solid fill at BOTH ends
// Bottom fill: Z = 0 to fill_height
// Top fill: Z = total_height - fill_height to total_height
// Plates: 0.4 mm thick, overlapping 0.4 mm
// ============================================================
phi = (1 + sqrt(5)) / 2;
vertices = [
[ 1, 1, 1], [ 1, 1, -1], [ 1, -1, 1], [ 1, -1, -1],
[-1, 1, 1], [-1, 1, -1], [-1, -1, 1], [-1, -1, -1],
[0, 1/phi, phi], [0, 1/phi, -phi], [0, -1/phi, phi], [0, -1/phi, -phi],
[ 1/phi, phi, 0], [ 1/phi, -phi, 0], [-1/phi, phi, 0], [-1/phi, -phi, 0],
[ phi, 0, 1/phi], [ phi, 0, -1/phi], [-phi, 0, 1/phi], [-phi, 0, -1/phi]
];
faces = [
[0, 8, 4, 14, 12], [0, 8, 10, 2, 16], [0, 16, 17, 1, 12],
[7, 11, 3, 13, 15], [7, 19, 5, 9, 11], [7, 15, 6, 18, 19],
[1, 9, 5, 14, 12], [6, 15, 13, 2, 10], [1, 17, 3, 11, 9],
[6, 10, 8, 4, 18], [2, 13, 3, 17, 16], [5, 19, 18, 4, 14]
];
function face_center(idx) =
let(p = [for (i = faces[idx]) vertices[i]])
(p[0] + p[1] + p[2] + p[3] + p[4]) / 5;
function inradius(radius) = radius * norm(face_center(0)) / sqrt(3);
function bbox_half(radius) = (radius / sqrt(3)) * max(abs(phi), abs(1/phi), 1);
module dodecahedron(radius = 1) {
scale(radius / sqrt(3))
polyhedron(points = vertices, faces = faces);
}
module hollow_dodecahedron(radius = 20, wall = 0.8) {
difference() {
dodecahedron(radius);
scale((radius - wall) / radius)
dodecahedron(radius);
}
}
module dodecahedron_cube_hollow_thin_filled_top_and_bottom(
radius = 20,
rows = 3,
cols = 3,
layers = 3,
wall = 0.8,
plate_thickness = 0.4,
plate_overlap = 0.4,
fill_height = 5 // height of the solid fill at each end
) {
s = 2 * inradius(radius);
h = bbox_half(radius);
dx = (cols - 1) * s + 2 * h;
dy = (rows - 1) * s + 2 * h;
total_height = (layers - 1) * s + 2 * h;
echo(str("Plate footprint: ", dx, " mm x ", dy, " mm"));
echo(str("Total height: ", total_height + plate_overlap * 2, " mm"));
echo(str("Fill height (bottom & top): ", fill_height, " mm"));
translate([-dx/2, -dy/2, -total_height/2])
union() {
// --- Bottom plate (thin) ---
translate([0, 0, -plate_overlap])
cube([dx, dy, plate_thickness]);
// --- Solid fill block at bottom (Z = 0 to fill_height) ---
translate([0, 0, 0])
cube([dx, dy, fill_height]);
// --- Top plate (thin) ---
translate([0, 0, total_height - plate_thickness + plate_overlap])
cube([dx, dy, plate_thickness]);
// --- Solid fill block at top (Z = total_height - fill_height to total_height) ---
translate([0, 0, total_height - fill_height])
cube([dx, dy, fill_height]);
// --- Hollow dodecahedra (embedded into the fill blocks) ---
for (layer = [0:layers-1])
for (row = [0:rows-1])
for (col = [0:cols-1])
translate([h + col * s, h + row * s, h + layer * s])
hollow_dodecahedron(radius, wall);
}
}
// Generate the model
dodecahedron_cube_hollow_thin_filled_top_and_bottom(radius = 10, fill_height = 3);
// ============================================================
// Generate the model - change radius 5 23.4 × 23.4 mm 27.9 mm
// 10 46.8 × 46.8 mm 55.7 mm
// 15 70.2 × 70.2 mm 83.5 mm
// 20 93.6 × 93.6 mm 111.3 mm
// ============================================================
Straight after Batman.
// ============================================================
// 3×3×3 Kelvin lattice (truncated octahedra) – hollow, connected,
// solid fill at BOTH ends, optional skin plates.
// ============================================================
/* [Main Parameters] */
size = 25; // distance between opposite square faces of one cell
rows = 3; // number of cells along Y
cols = 3; // number of cells along X
layers = 3; // number of cells along Z
wall = 0.8; // shell thickness (≥ 0.8 mm for strength)
fill_height = 3.0; // height of solid fill blocks at top & bottom
plate_thickness = 0.4; // thickness of outer skin plates (0 = no plates)
plate_overlap = 0.4; // how much the plates extend beyond the lattice
// ---------- vertex & face data (one unit truncated octahedron) ----------
to_raw = [
[ 0, 1, 2], [ 0, 2, 1], [ 0, 2, -1], [ 0, 1, -2],
[ 0, -1, -2], [ 0, -2, -1], [ 0, -2, 1], [ 0, -1, 2],
[ 1, 0, 2], [ 2, 0, 1], [ 2, 0, -1], [ 1, 0, -2],
[-1, 0, -2], [-2, 0, -1], [-2, 0, 1], [-1, 0, 2],
[ 1, 2, 0], [ 2, 1, 0], [ 2, -1, 0], [ 1, -2, 0],
[-1, -2, 0], [-2, -1, 0], [-2, 1, 0], [-1, 2, 0]
];
// all 14 faces with correct outward orientation
to_faces = [
// hexagonal faces (8)
[0,8,9,17,16,1], // (+x,+y,+z)
[2,16,17,10,11,3], // (+x,+y,-z)
[8,7,6,19,18,9], // (+x,-y,+z)
[10,18,19,5,4,11], // (+x,-y,-z)
[0,1,23,22,14,15], // (-x,+y,+z)
[2,3,12,13,22,23], // (-x,+y,-z)
[7,15,14,21,20,6], // (-x,-y,+z)
[5,20,21,13,12,4], // (-x,-y,-z)
// square faces (6)
[9,18,10,17], // +x
[22,13,21,14], // -x
[1,16,2,23], // +y
[20,5,19,6], // -y
[15,7,8,0], // +z
[3,11,4,12] // -z
];
// ---------- module: one hollow truncated octahedron ----------
module hollow_kelvin_cell(size, wall) {
s_outer = size / 4; // raw coords span 4 → size
s_inner = (size - 2*wall) / 4; // inner shell offset by wall
outer_pts = [for (v = to_raw) v * s_outer];
inner_pts = [for (v = to_raw) v * s_inner];
difference() {
polyhedron(points = outer_pts, faces = to_faces);
polyhedron(points = inner_pts, faces = to_faces);
}
}
// ---------- overall lattice module ----------
module kelvin_lattice_sandwich(
size,
rows, cols, layers,
wall,
fill_height,
plate_thickness,
plate_overlap
) {
dx = cols * size;
dy = rows * size;
total_height = layers * size;
echo(str("Plate footprint: ", dx, " mm × ", dy, " mm"));
echo(str("Total lattice height: ", total_height, " mm"));
echo(str("Fill height (top & bottom): ", fill_height, " mm"));
union() {
// --- hollow cells array (centers at half‑cell intervals) ---
for (x = [0:cols-1], y = [0:rows-1], z = [0:layers-1])
translate([x*size + size/2, y*size + size/2, z*size + size/2])
hollow_kelvin_cell(size, wall);
// --- bottom solid fill block ---
translate([0, 0, 0])
cube([dx, dy, fill_height]);
// --- top solid fill block ---
translate([0, 0, total_height - fill_height])
cube([dx, dy, fill_height]);
// --- optional thin outer plates ---
if (plate_thickness > 0) {
// bottom plate
translate([0, 0, -plate_overlap])
cube([dx, dy, plate_thickness]);
// top plate
translate([0, 0, total_height - plate_thickness + plate_overlap])
cube([dx, dy, plate_thickness]);
}
}
}
// ---------- generate model ----------
kelvin_lattice_sandwich(
size = size,
rows = rows,
cols = cols,
layers = layers,
wall = wall,
fill_height = fill_height,
plate_thickness = plate_thickness,
plate_overlap = plate_overlap
);
// 3×3×3 Kelvin lattice (truncated octahedra) – Klemmbrick variants
//
// This script builds on the hollow Kelvin lattice sandwich and
// adds compatible male/female connectors to turn it into a
// stackable building block.
//
// Two variants are provided:
// kelvin_lattice_tile() – flat top, female sockets on bottom
// kelvin_lattice_brick() – male studs on top, female sockets on bottom
//
// The stud grid automatically follows the cell arrangement:
// stud spacing = size, stud count = cols × rows
// ============================================================
/* [Main Parameters] */
size = 25; // distance between opposite square faces of one cell
rows = 3; // number of cells along Y
cols = 3; // number of cells along X
layers = 3; // number of cells along Z
wall = 0.8; // shell thickness (≥ 0.8 mm for strength)
fill_height = 3.0; // height of solid fill blocks at top & bottom
plate_thickness = 0.4; // thickness of outer skin plates (0 = no plates)
plate_overlap = 0.4; // how much the plates extend beyond the lattice
/* [Klemmbrick Connectors] */
stud_spacing = 25; // grid spacing of the connectors (default = size)
stud_diameter = 10; // diameter of male studs
stud_height = 3.75; // height of male studs (default = size*0.15)
female_clearance = 0.2; // extra diameter for female sockets
// ---------- vertex & face data (one unit truncated octahedron) ----------
to_raw = [
[ 0, 1, 2], [ 0, 2, 1], [ 0, 2, -1], [ 0, 1, -2],
[ 0, -1, -2], [ 0, -2, -1], [ 0, -2, 1], [ 0, -1, 2],
[ 1, 0, 2], [ 2, 0, 1], [ 2, 0, -1], [ 1, 0, -2],
[-1, 0, -2], [-2, 0, -1], [-2, 0, 1], [-1, 0, 2],
[ 1, 2, 0], [ 2, 1, 0], [ 2, -1, 0], [ 1, -2, 0],
[-1, -2, 0], [-2, -1, 0], [-2, 1, 0], [-1, 2, 0]
];
to_faces = [
// hexagonal faces (8)
[0,8,9,17,16,1], // (+x,+y,+z)
[2,16,17,10,11,3], // (+x,+y,-z)
[8,7,6,19,18,9], // (+x,-y,+z)
[10,18,19,5,4,11], // (+x,-y,-z)
[0,1,23,22,14,15], // (-x,+y,+z)
[2,3,12,13,22,23], // (-x,+y,-z)
[7,15,14,21,20,6], // (-x,-y,+z)
[5,20,21,13,12,4], // (-x,-y,-z)
// square faces (6)
[9,18,10,17], // +x
[22,13,21,14], // -x
[1,16,2,23], // +y
[20,5,19,6], // -y
[15,7,8,0], // +z
[3,11,4,12] // -z
];
// ---------- module: one hollow truncated octahedron ----------
module hollow_kelvin_cell(size, wall) {
s_outer = size / 4;
s_inner = (size - 2*wall) / 4;
outer_pts = [for (v = to_raw) v * s_outer];
inner_pts = [for (v = to_raw) v * s_inner];
difference() {
polyhedron(points = outer_pts, faces = to_faces);
polyhedron(points = inner_pts, faces = to_faces);
}
}
// ---------- core lattice sandwich (internal) ----------
module kelvin_lattice_sandwich(
size,
rows, cols, layers,
wall,
fill_height,
plate_thickness,
plate_overlap
) {
dx = cols * size;
dy = rows * size;
total_height = layers * size;
union() {
// hollow cell array
for (x = [0:cols-1], y = [0:rows-1], z = [0:layers-1])
translate([x*size + size/2, y*size + size/2, z*size + size/2])
hollow_kelvin_cell(size, wall);
// bottom solid fill
translate([0, 0, 0])
cube([dx, dy, fill_height]);
// top solid fill
translate([0, 0, total_height - fill_height])
cube([dx, dy, fill_height]);
// optional thin outer plates
if (plate_thickness > 0) {
// bottom plate
translate([0, 0, -plate_overlap])
cube([dx, dy, plate_thickness]);
// top plate
translate([0, 0, total_height - plate_thickness + plate_overlap])
cube([dx, dy, plate_thickness]);
}
}
}
// ---------- Klemmbrick helper: female sockets on the bottom ----------
module add_female_sockets(
dx, dy,
cols, rows,
size,
fill_height,
plate_thickness,
plate_overlap,
stud_diameter,
female_clearance,
stud_height
) {
// deepest point of the model underside
bottom_z = (plate_thickness > 0) ? -plate_overlap : 0;
// hole must go from bottom surface up through the solid fill
hole_height = fill_height - bottom_z;
// female hole radius
female_r = (stud_diameter + female_clearance) / 2;
// subtract a cylinder at each cell center
for (x = [0:cols-1], y = [0:rows-1]) {
cx = x * size + size/2;
cy = y * size + size/2;
translate([cx, cy, bottom_z])
cylinder(r = female_r, h = hole_height, $fn = 32);
}
}
// ---------- Klemmbrick helper: male studs on the top ----------
module add_male_studs(
dx, dy,
cols, rows,
size,
total_height,
plate_thickness,
plate_overlap,
stud_diameter,
stud_height
) {
// highest surface of the model
top_z = (plate_thickness > 0)
? total_height + plate_overlap
: total_height;
stud_r = stud_diameter / 2;
for (x = [0:cols-1], y = [0:rows-1]) {
cx = x * size + size/2;
cy = y * size + size/2;
translate([cx, cy, top_z])
cylinder(r = stud_r, h = stud_height, $fn = 32);
}
}
// ---------- public module: flat tile with female connectors on bottom ----------
module kelvin_lattice_tile() {
dx = cols * size;
dy = rows * size;
total_height = layers * size;
difference() {
kelvin_lattice_sandwich(
size, rows, cols, layers,
wall, fill_height, plate_thickness, plate_overlap
);
add_female_sockets(
dx, dy, cols, rows, size,
fill_height, plate_thickness, plate_overlap,
stud_diameter, female_clearance, stud_height
);
}
}
// ---------- public module: brick with male studs on top and female on bottom ----------
module kelvin_lattice_brick() {
dx = cols * size;
dy = rows * size;
total_height = layers * size;
difference() {
union() {
kelvin_lattice_sandwich(
size, rows, cols, layers,
wall, fill_height, plate_thickness, plate_overlap
);
add_male_studs(
dx, dy, cols, rows, size,
total_height, plate_thickness, plate_overlap,
stud_diameter, stud_height
);
}
add_female_sockets(
dx, dy, cols, rows, size,
fill_height, plate_thickness, plate_overlap,
stud_diameter, female_clearance, stud_height
);
}
}
// ============================================================
// Build one of the variants below (comment/uncomment)
// ============================================================
// Tile (flat top, female bottom)
// kelvin_lattice_tile();
// Brick (male top, female bottom)
kelvin_lattice_brick();
// 3×3×3 Kelvin lattice – Klemmbrick with INDEPENDENT stud grid
//
// - Stud spacing FIXED at 8 mm (true Lego grid)
// - Stud diameter / height locked to original Lego values
// - Plate size (lattice) can be scaled independently
// ============================================================
/* [Main Parameters] */
size = 10; // cell size of the Kelvin lattice
rows = 30; // number of cells along Y
cols = 15; // number of cells along X
layers = 3; // number of cells along Z
wall = 0.8; // shell thickness
fill_height = 3.0; // solid fill height top & bottom
plate_thickness = 0.4; // outer skin thickness (0 = off)
plate_overlap = 0.4; // extra plate extension
/* [Lego Grid – DO NOT change for standard compatibility] */
stud_spacing = 8; // fixed 8 mm grid (original Lego pitch)
stud_diameter = 4.8; // standard stud diameter
stud_height = 1.8; // standard stud height
female_clearance = 0.2; // extra hole width for a snug fit
// ---------- vertex & face data (unchanged) ----------
to_raw = [
[ 0, 1, 2], [ 0, 2, 1], [ 0, 2, -1], [ 0, 1, -2],
[ 0, -1, -2], [ 0, -2, -1], [ 0, -2, 1], [ 0, -1, 2],
[ 1, 0, 2], [ 2, 0, 1], [ 2, 0, -1], [ 1, 0, -2],
[-1, 0, -2], [-2, 0, -1], [-2, 0, 1], [-1, 0, 2],
[ 1, 2, 0], [ 2, 1, 0], [ 2, -1, 0], [ 1, -2, 0],
[-1, -2, 0], [-2, -1, 0], [-2, 1, 0], [-1, 2, 0]
];
to_faces = [
[0,8,9,17,16,1], [2,16,17,10,11,3], [8,7,6,19,18,9],
[10,18,19,5,4,11], [0,1,23,22,14,15], [2,3,12,13,22,23],
[7,15,14,21,20,6], [5,20,21,13,12,4],
[9,18,10,17], [22,13,21,14], [1,16,2,23],
[20,5,19,6], [15,7,8,0], [3,11,4,12]
];
// ---------- one hollow cell ----------
module hollow_kelvin_cell(size, wall) {
s_outer = size / 4;
s_inner = (size - 2*wall) / 4;
outer_pts = [for (v = to_raw) v * s_outer];
inner_pts = [for (v = to_raw) v * s_inner];
difference() {
polyhedron(points = outer_pts, faces = to_faces);
polyhedron(points = inner_pts, faces = to_faces);
}
}
// ---------- core lattice sandwich ----------
module kelvin_lattice_sandwich(size, rows, cols, layers, wall, fill_height, plate_thickness, plate_overlap) {
dx = cols * size;
dy = rows * size;
total_height = layers * size;
union() {
for (x = [0:cols-1], y = [0:rows-1], z = [0:layers-1])
translate([x*size + size/2, y*size + size/2, z*size + size/2])
hollow_kelvin_cell(size, wall);
translate([0,0,0]) cube([dx, dy, fill_height]);
translate([0,0,total_height - fill_height]) cube([dx, dy, fill_height]);
if (plate_thickness > 0) {
translate([0,0,-plate_overlap]) cube([dx, dy, plate_thickness]);
translate([0,0,total_height - plate_thickness + plate_overlap]) cube([dx, dy, plate_thickness]);
}
}
}
// ---------- stud grid helper (uses fixed 8 mm spacing) ----------
module generate_stud_grid(dx, dy, total_height, plate_thickness, plate_overlap, female = false) {
// how many studs can we fit across each axis?
studs_x = floor(dx / stud_spacing);
studs_y = floor(dy / stud_spacing);
// center the grid on the plate
offset_x = (dx - studs_x * stud_spacing) / 2 + stud_spacing / 2;
offset_y = (dy - studs_y * stud_spacing) / 2 + stud_spacing / 2;
// height for male studs (top) or female holes (bottom)
top_z = (plate_thickness > 0) ? total_height + plate_overlap : total_height;
bottom_z = (plate_thickness > 0) ? -plate_overlap : 0;
hole_height = fill_height - bottom_z;
female_r = (stud_diameter + female_clearance) / 2;
stud_r = stud_diameter / 2;
for (ix = [0:studs_x-1], iy = [0:studs_y-1]) {
cx = offset_x + ix * stud_spacing;
cy = offset_y + iy * stud_spacing;
if (female) {
// subtract female socket
translate([cx, cy, bottom_z])
cylinder(r = female_r, h = hole_height, $fn = 32);
} else {
// add male stud
translate([cx, cy, top_z])
cylinder(r = stud_r, h = stud_height, $fn = 32);
}
}
}
// ---------- flat tile (top without studs, bottom with sockets) ----------
module kelvin_lattice_tile() {
dx = cols * size;
dy = rows * size;
total_height = layers * size;
difference() {
kelvin_lattice_sandwich(size, rows, cols, layers, wall, fill_height, plate_thickness, plate_overlap);
generate_stud_grid(dx, dy, total_height, plate_thickness, plate_overlap, female = true);
}
}
// ---------- brick (studs on top, sockets on bottom) ----------
module kelvin_lattice_brick() {
dx = cols * size;
dy = rows * size;
total_height = layers * size;
difference() {
union() {
kelvin_lattice_sandwich(size, rows, cols, layers, wall, fill_height, plate_thickness, plate_overlap);
generate_stud_grid(dx, dy, total_height, plate_thickness, plate_overlap, female = false);
}
generate_stud_grid(dx, dy, total_height, plate_thickness, plate_overlap, female = true);
}
}
// ============================================================
// Choose one:
// ============================================================
// kelvin_lattice_tile();
kelvin_lattice_brick();




