Forráskód Böngészése

Adding OpensCAD files

Nicole Portas 1 hónapja
szülő
commit
fd81129c3a
5 módosított fájl, 698 hozzáadás és 0 törlés
  1. 171 0
      LED Enclosure.scad
  2. 105 0
      LED Frame.scad
  3. 146 0
      LED Front grid.scad
  4. 163 0
      LED Grill support.scad
  5. 113 0
      LED_LENS.scad

+ 171 - 0
LED Enclosure.scad

@@ -0,0 +1,171 @@
+// -----------------------------------------------------------------------
+// Modular 6-Part Enclosure for 10mm Pitch LED Matrix WITH DOVETAILS
+// -----------------------------------------------------------------------
+// NATIVE LANDSCAPE LAYOUT (3 columns x 2 rows)
+// Select which tile to generate:
+// 1 = Top Left      |  2 = Top Mid      |  3 = Top Right
+// 4 = Bottom Left   |  5 = Bottom Mid   |  6 = Bottom Right
+part = 1; // [1:6]
+
+// --- Core Math ---
+cellsX = 16; 
+cellsY = 16; 
+pitch = 10;
+gridWidth = cellsX * pitch;
+gridHeight = cellsY * pitch;
+borderWidth = 10;
+
+// Internal depth is 51mm (clearing the 49mm box height)
+depth = 55; 
+wall = 4;
+
+// Logic for 3x2 Landscape Grid
+hasLeft   = (part == 1 || part == 4) ? 1 : 0;
+hasRight  = (part == 3 || part == 6) ? 1 : 0;
+hasTop    = (part == 1 || part == 2 || part == 3) ? 1 : 0;
+hasBottom = (part == 4 || part == 5 || part == 6) ? 1 : 0;
+
+leftOffset = hasLeft * borderWidth;
+bottomOffset = hasBottom * borderWidth;
+
+totalWidth = gridWidth + (hasLeft * borderWidth) + (hasRight * borderWidth);
+totalHeight = gridHeight + (hasTop * borderWidth) + (hasBottom * borderWidth);
+
+$fn = 24;
+
+// --- Dovetail Joint Geometry ---
+module dovetail(clearance=0) {
+    linear_extrude(wall)
+    polygon([
+        [-0.1, -6 - clearance],
+        [8, -10 - clearance],
+        [8, 10 + clearance],
+        [-0.1, 6 + clearance]
+    ]);
+}
+
+module posts() {
+    holeRad = 1.4; // Sized for M3 self-tapping screws
+    postRad = 4;
+    
+    // Left Border
+    if (hasLeft) {
+        for (i = [1 : 3]) {
+            translate([borderWidth / 2, bottomOffset + (i * gridHeight/4), 0])
+            difference() {
+                cylinder(h=depth, r=postRad);
+                translate([0, 0, wall]) cylinder(h=depth, r=holeRad);
+            }
+        }
+    }
+    // Right Border
+    if (hasRight) {
+        for (i = [1 : 3]) {
+            translate([totalWidth - (borderWidth / 2), bottomOffset + (i * gridHeight/4), 0])
+            difference() {
+                cylinder(h=depth, r=postRad);
+                translate([0, 0, wall]) cylinder(h=depth, r=holeRad);
+            }
+        }
+    }
+    // Top Border
+    if (hasTop) {
+        for (i = [1 : 3]) {
+            translate([leftOffset + (i * gridWidth/4), totalHeight - (borderWidth / 2), 0])
+            difference() {
+                cylinder(h=depth, r=postRad);
+                translate([0, 0, wall]) cylinder(h=depth, r=holeRad);
+            }
+        }
+    }
+    // Bottom Border
+    if (hasBottom) {
+        for (i = [1 : 3]) {
+            translate([leftOffset + (i * gridWidth/4), borderWidth / 2, 0])
+            difference() {
+                cylinder(h=depth, r=postRad);
+                translate([0, 0, wall]) cylinder(h=depth, r=holeRad);
+            }
+        }
+    }
+}
+
+module keyhole() {
+    cylinder(r=4, h=wall+2, center=true);
+    translate([0, 6, 0]) cylinder(r=2.2, h=wall+2, center=true);
+    translate([0, 3, 0]) cube([4.4, 6, wall+2], center=true);
+}
+
+difference() {
+    union() {
+        // Main block
+        cube([totalWidth, totalHeight, depth]);
+        
+        // Add Male Dovetail Tabs on internal edges
+        if (!hasRight) {
+            for(i=[1:3]) translate([totalWidth, totalHeight * 0.25 * i, 0]) dovetail(0);
+        }
+        if (!hasBottom) {
+            for(i=[1:3]) translate([totalWidth * 0.25 * i, 0, 0]) rotate([0, 0, -90]) dovetail(0);
+        }
+    }
+    
+    // Cavity cutout
+    cutX = hasLeft ? wall : -1;
+    cutY = hasBottom ? wall : -1;
+    cutW = totalWidth - (hasLeft ? wall : -1) - (hasRight ? wall : -1);
+    cutH = totalHeight - (hasBottom ? wall : -1) - (hasTop ? wall : -1);
+    
+    translate([cutX, cutY, wall])
+    cube([cutW, cutH, depth]);
+    
+    // Subtract Female Dovetail Cuts on internal edges
+    if (!hasLeft) {
+        for(i=[1:3]) translate([0, totalHeight * 0.25 * i, 0]) dovetail(0.3);
+    }
+    if (!hasTop) {
+        for(i=[1:3]) translate([totalWidth * 0.25 * i, totalHeight, 0]) rotate([0, 0, -90]) dovetail(0.3);
+    }
+    
+    // Power Jack (9mm hole) straight through the right wall
+    if (part == 6) {
+        // Y = 17.5 (slot start) + 12.5 (center of 25mm controller) = 30
+        // Z = 4 (floor) + 19 = 23
+        translate([totalWidth + 1, 30, 23])
+        rotate([0, -90, 0])
+        cylinder(h=wall + 20, r=4.5);
+    }
+    
+    // Wall mount keyholes on top parts 
+    if (hasTop && hasLeft) {
+        translate([totalWidth/2, totalHeight/2, wall/2])
+        keyhole();
+    }
+    if (hasTop && hasRight) {
+        translate([totalWidth/2, totalHeight/2, wall/2])
+        keyhole();
+    }
+}
+
+// Add mounting posts back into the cavity
+posts();
+
+// =================================================================
+// PERPENDICULAR CONTROLLER SLOT
+// Exclusively generated on Part 6. 
+// Mounts the 25x49 face dead flush against the internal right wall.
+// Controller extends 90mm inward along the X-axis.
+// =================================================================
+if (part == 6) {
+    // Left retaining wall (X-axis inner stop at 90mm length)
+    translate([73, 15.5, wall])
+    cube([2, 30, 20]);
+    
+    // Top retaining wall (Y-axis upper stop)
+    translate([73, 43.5, wall])
+    cube([93, 2, 20]);
+    
+    // Bottom retaining wall (Y-axis lower stop)
+    translate([73, 15.5, wall])
+    cube([93, 2, 20]);
+}

+ 105 - 0
LED Frame.scad

@@ -0,0 +1,105 @@
+// -----------------------------------------------------------------------
+// Modular LED Matrix Outer Frame / Diffuser Clamp (Through-Holes)
+// -----------------------------------------------------------------------
+// NATIVE LANDSCAPE LAYOUT (3 columns x 2 rows)
+// Select which tile to generate:
+// 1 = Top Left      |  2 = Top Mid      |  3 = Top Right
+// 4 = Bottom Left   |  5 = Bottom Mid   |  6 = Bottom Right
+part = 1; // [1:6]
+
+// --- Matrix Parameters ---
+cellsX = 16;
+cellsY = 16;
+pitch = 10;
+borderWidth = 10;
+
+// --- Frame Settings ---
+thickness = 3; 
+overhang = 1; // 1mm bite inwards to clamp the diffuser without blocking LEDs
+overW = borderWidth + overhang; // Total width of the frame bars
+lap_len = 6; // Length of the hidden half-lap joint
+tol = 0.2; // 0.2mm tolerance for a friction fit without sanding
+
+// --- Logic ---
+hasLeft   = (part == 1 || part == 4) ? 1 : 0;
+hasRight  = (part == 3 || part == 6) ? 1 : 0;
+hasTop    = (part == 1 || part == 2 || part == 3) ? 1 : 0;
+hasBottom = (part == 4 || part == 5 || part == 6) ? 1 : 0;
+
+gridWidth = cellsX * pitch;
+gridHeight = cellsY * pitch;
+
+leftOffset = hasLeft * borderWidth;
+bottomOffset = hasBottom * borderWidth;
+
+totalWidth = gridWidth + (hasLeft * borderWidth) + (hasRight * borderWidth);
+totalHeight = gridHeight + (hasTop * borderWidth) + (hasBottom * borderWidth);
+
+$fn = 32;
+
+// --- Countersunk Hole Module ---
+module cs_hole(x, y) {
+    // M3 shaft clearance (3.2mm diameter)
+    translate([x, y, -0.1]) 
+        cylinder(h=thickness + 0.2, r=1.6);
+    // Countersink cone for 5mm head (using 5.2mm for flush fit)
+    // 1mm depth matches standard 90-degree profile for this head size
+    translate([x, y, thickness - 1]) 
+        cylinder(h=1.1, r1=1.6, r2=2.6);
+}
+
+difference() {
+    union() {
+        // Base Perimeter Bars
+        if(hasLeft)   translate([0, 0, 0]) cube([overW, totalHeight, thickness]);
+        if(hasRight)  translate([totalWidth - overW, 0, 0]) cube([overW, totalHeight, thickness]);
+        if(hasTop)    translate([0, totalHeight - overW, 0]) cube([totalWidth, overW, thickness]);
+        if(hasBottom) translate([0, 0, 0]) cube([totalWidth, overW, thickness]);
+
+        // Male Half-Lap Tabs (Hidden on the bottom half, Z = 0 to thickness/2)
+        // Extending Right (P1, P2) on Top Bar
+        if(hasTop && !hasRight) translate([totalWidth, totalHeight - overW, 0]) cube([lap_len, overW, thickness/2]);
+        // Extending Right (P4, P5) on Bottom Bar
+        if(hasBottom && !hasRight) translate([totalWidth, 0, 0]) cube([lap_len, overW, thickness/2]);
+        // Extending Down (P1) on Left Bar
+        if(hasLeft && !hasBottom) translate([0, -lap_len, 0]) cube([overW, lap_len, thickness/2]);
+        // Extending Down (P3) on Right Bar
+        if(hasRight && !hasBottom) translate([totalWidth - overW, -lap_len, 0]) cube([overW, lap_len, thickness/2]);
+    }
+    
+    // Female Half-Lap Slots (Cut from Z = -0.1 to thickness/2 + tol)
+    // Cut from Left (P2, P3) on Top Bar
+    if(hasTop && !hasLeft) translate([-0.1, totalHeight - overW - tol, -0.1]) cube([lap_len + 0.1, overW + 2*tol, thickness/2 + tol]);
+    // Cut from Left (P5, P6) on Bottom Bar
+    if(hasBottom && !hasLeft) translate([-0.1, -tol, -0.1]) cube([lap_len + 0.1, overW + 2*tol, thickness/2 + tol]);
+    // Cut from Top (P4) on Left Bar
+    if(hasLeft && !hasTop) translate([-tol, totalHeight - lap_len, -0.1]) cube([overW + 2*tol, lap_len + 0.1, thickness/2 + tol]);
+    // Cut from Top (P6) on Right Bar
+    if(hasRight && !hasTop) translate([totalWidth - overW - tol, totalHeight - lap_len, -0.1]) cube([overW + 2*tol, lap_len + 0.1, thickness/2 + tol]);
+
+    // Countersunk Mounting Holes
+    // Left Border
+    if (hasLeft) { 
+        for (i = [1 : 3]) { 
+            cs_hole(borderWidth / 2, bottomOffset + (i * gridHeight/4)); 
+        } 
+    } 
+    // Right Border
+    if (hasRight) { 
+        for (i = [1 : 3]) { 
+            cs_hole(totalWidth - (borderWidth / 2), bottomOffset + (i * gridHeight/4)); 
+        } 
+    } 
+    // Top Border
+    if (hasTop) { 
+        for (i = [1 : 3]) { 
+            cs_hole(leftOffset + (i * gridWidth/4), totalHeight - (borderWidth / 2)); 
+        } 
+    } 
+    // Bottom Border
+    if (hasBottom) { 
+        for (i = [1 : 3]) { 
+            cs_hole(leftOffset + (i * gridWidth/4), borderWidth / 2); 
+        } 
+    } 
+}

+ 146 - 0
LED Front grid.scad

@@ -0,0 +1,146 @@
+// -----------------------------------------------------------------------
+// Modular 10mm Pitch LED Matrix Grid WITH BORDER-ONLY BLIND SNAPS
+// -----------------------------------------------------------------------
+// NATIVE LANDSCAPE LAYOUT (3 columns x 2 rows)
+// Select which tile to generate:
+// 1 = Top Left      |  2 = Top Mid      |  3 = Top Right
+// 4 = Bottom Left   |  5 = Bottom Mid   |  6 = Bottom Right
+part = 1; // [1:6]
+
+// --- Matrix Parameters ---
+cellsX = 16; 
+cellsY = 16; 
+pitch = 10; 
+meshSolid = 2; 
+meshSpace = pitch - meshSolid; // 8mm hole
+thickness = 5; 
+
+// --- Frame Parameters ---
+borderWidth = 10; 
+holeRad = 2; 
+
+// --- Logic (Do not touch) ---
+// FIXED: Mapped to 3x2 Landscape Grid
+hasLeft   = (part == 1 || part == 4) ? 1 : 0; 
+hasRight  = (part == 3 || part == 6) ? 1 : 0; 
+hasTop    = (part == 1 || part == 2 || part == 3) ? 1 : 0; 
+hasBottom = (part == 4 || part == 5 || part == 6) ? 1 : 0; 
+
+gridWidth = cellsX * pitch; 
+gridHeight = cellsY * pitch; 
+
+leftOffset = hasLeft * borderWidth; 
+bottomOffset = hasBottom * borderWidth; 
+
+totalWidth = gridWidth + (hasLeft * borderWidth) + (hasRight * borderWidth); 
+totalHeight = gridHeight + (hasTop * borderWidth) + (hasBottom * borderWidth); 
+
+$fn = 16; 
+
+// --- Border Insert Geometry (FDM friendly tapered hexagonal pegs) ---
+module tab_x() {
+    rotate([0, 90, 0]) rotate([0, 0, 30])
+    cylinder(h=5, r1=1.8, r2=1.3, $fn=6);
+}
+module hole_x() {
+    translate([-0.1, 0, 0])
+    rotate([0, 90, 0]) rotate([0, 0, 30])
+    cylinder(h=5.5, r1=2.0, r2=1.5, $fn=6);
+}
+module tab_y() {
+    rotate([-90, 0, 0]) rotate([0, 0, 30])
+    cylinder(h=5, r1=1.8, r2=1.3, $fn=6);
+}
+module hole_y() {
+    translate([0, -0.1, 0])
+    rotate([-90, 0, 0]) rotate([0, 0, 30])
+    cylinder(h=5.5, r1=2.0, r2=1.5, $fn=6);
+}
+
+difference() {
+    union() {
+        // Base solid plate
+        cube([totalWidth, totalHeight, thickness], center=false); 
+        
+        // Add Male pegs ONLY on the thick outer borders
+        if (!hasRight) {
+            if (hasTop) {
+                translate([totalWidth, totalHeight - borderWidth/2, thickness/2]) tab_x();
+            }
+            if (hasBottom) {
+                translate([totalWidth, borderWidth/2, thickness/2]) tab_x();
+            }
+        }
+        if (!hasTop) {
+            if (hasLeft) {
+                translate([borderWidth/2, totalHeight, thickness/2]) tab_y();
+            }
+            if (hasRight) {
+                translate([totalWidth - borderWidth/2, totalHeight, thickness/2]) tab_y();
+            }
+        }
+    }
+    
+    // Punch out the LED cells
+    for (x = [0 : cellsX - 1]) { 
+        for (y = [0 : cellsY - 1]) { 
+            translate([ 
+                leftOffset + (meshSolid/2) + (x * pitch), 
+                bottomOffset + (meshSolid/2) + (y * pitch), 
+                -1 
+            ]) 
+            cube([meshSpace, meshSpace, thickness + 2], center=false); 
+        } 
+    } 
+
+    // Cut Female holes ONLY on the thick outer borders
+    if (!hasLeft) {
+        if (hasTop) {
+            translate([0, totalHeight - borderWidth/2, thickness/2]) hole_x();
+        }
+        if (hasBottom) {
+            translate([0, borderWidth/2, thickness/2]) hole_x();
+        }
+    }
+    if (!hasBottom) {
+        if (hasLeft) {
+            translate([borderWidth/2, 0, thickness/2]) hole_y();
+        }
+        if (hasRight) {
+            translate([totalWidth - borderWidth/2, 0, thickness/2]) hole_y();
+        }
+    }
+
+    // --- Mounting Holes ---
+    // Left Border
+    if (hasLeft) { 
+        for (i = [1 : 3]) { 
+            translate([borderWidth / 2, bottomOffset + (i * gridHeight/4), -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+    
+    // Right Border
+    if (hasRight) { 
+        for (i = [1 : 3]) { 
+            translate([totalWidth - (borderWidth / 2), bottomOffset + (i * gridHeight/4), -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+
+    // Top Border
+    if (hasTop) { 
+        for (i = [1 : 3]) { 
+            translate([leftOffset + (i * gridWidth/4), totalHeight - (borderWidth / 2), -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+
+    // Bottom Border
+    if (hasBottom) { 
+        for (i = [1 : 3]) { 
+            translate([leftOffset + (i * gridWidth/4), borderWidth / 2, -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+}

+ 163 - 0
LED Grill support.scad

@@ -0,0 +1,163 @@
+// -----------------------------------------------------------------------
+// Modular LED Matrix X-Brace Backplate with Flush Snaps
+// -----------------------------------------------------------------------
+// NATIVE LANDSCAPE LAYOUT (3 columns x 2 rows)
+// Select which tile to generate:
+// 1 = Top Left      |  2 = Top Mid      |  3 = Top Right
+// 4 = Bottom Left   |  5 = Bottom Mid   |  6 = Bottom Right
+part = 1; // [1:6]
+
+// --- Matrix Parameters ---
+cellsX = 16;
+cellsY = 16;
+pitch = 10;
+
+// Flat 1.6mm Z-plane for fast FDM printing
+thickness = 1.6; 
+brace_width = 15;
+hub_diameter = 40; // Added central hub for structural integrity
+
+// --- Frame Parameters ---
+borderWidth = 10;
+holeRad = 2;
+inner_rim = 8; // Solid rim on interior seams for the snaps to anchor into
+
+// --- Logic ---
+hasLeft   = (part == 1 || part == 4) ? 1 : 0;
+hasRight  = (part == 3 || part == 6) ? 1 : 0;
+hasTop    = (part == 1 || part == 2 || part == 3) ? 1 : 0;
+hasBottom = (part == 4 || part == 5 || part == 6) ? 1 : 0;
+
+gridWidth = cellsX * pitch;
+gridHeight = cellsY * pitch;
+
+leftOffset = hasLeft * borderWidth;
+bottomOffset = hasBottom * borderWidth;
+
+totalWidth = gridWidth + (hasLeft * borderWidth) + (hasRight * borderWidth);
+totalHeight = gridHeight + (hasTop * borderWidth) + (hasBottom * borderWidth);
+
+left_rim   = hasLeft ? borderWidth : inner_rim;
+right_rim  = hasRight ? borderWidth : inner_rim;
+top_rim    = hasTop ? borderWidth : inner_rim;
+bottom_rim = hasBottom ? borderWidth : inner_rim;
+
+$fn = 32;
+
+// --- Flush Puzzle Snaps (XY Plane) ---
+module puzzle_tab_x() {
+    // Points out to the right
+    translate([0, -3, 0]) cube([3, 6, thickness]); 
+    translate([3, 0, 0]) cylinder(r=4, h=thickness); 
+}
+module puzzle_slot_x() {
+    // Cuts inwards from the left
+    translate([-0.1, -3.2, -1]) cube([3.2, 6.4, thickness+2]);
+    translate([3, 0, -1]) cylinder(r=4.2, h=thickness+2);
+}
+module puzzle_tab_y() {
+    // Points downwards
+    rotate([0, 0, -90]) puzzle_tab_x();
+}
+module puzzle_slot_y() {
+    // Cuts downwards from the top
+    rotate([0, 0, -90]) puzzle_slot_x();
+}
+
+difference() {
+    union() {
+        // Main solid frame with hollowed center
+        difference() {
+            cube([totalWidth, totalHeight, thickness], center=false);
+            
+            // Punch out the dead space
+            translate([left_rim, bottom_rim, -1])
+            cube([
+                totalWidth - left_rim - right_rim, 
+                totalHeight - top_rim - bottom_rim, 
+                thickness + 2
+            ]);
+        }
+        
+        // Internal X-Braces with Central Hub
+        intersection() {
+            cube([totalWidth, totalHeight, thickness]);
+            union() {
+                // Diagonal /
+                hull() {
+                    translate([left_rim, bottom_rim, 0]) 
+                        cylinder(d=brace_width, h=thickness);
+                    translate([totalWidth - right_rim, totalHeight - top_rim, 0]) 
+                        cylinder(d=brace_width, h=thickness);
+                }
+                // Diagonal \
+                hull() {
+                    translate([totalWidth - right_rim, bottom_rim, 0]) 
+                        cylinder(d=brace_width, h=thickness);
+                    translate([left_rim, totalHeight - top_rim, 0]) 
+                        cylinder(d=brace_width, h=thickness);
+                }
+                // Central Hub
+                translate([totalWidth/2, totalHeight/2, 0])
+                    cylinder(d=hub_diameter, h=thickness);
+            }
+        }
+        
+        // Add Male Puzzle Tabs on internal mating edges
+        if (!hasRight) {
+            translate([totalWidth, totalHeight/3, 0]) puzzle_tab_x();
+            translate([totalWidth, 2*totalHeight/3, 0]) puzzle_tab_x();
+        }
+        if (!hasBottom) {
+            translate([totalWidth/3, 0, 0]) puzzle_tab_y();
+            translate([2*totalWidth/3, 0, 0]) puzzle_tab_y();
+        }
+    }
+    
+    // Cut Female Puzzle Slots on internal mating edges
+    if (!hasLeft) {
+        translate([0, totalHeight/3, 0]) puzzle_slot_x();
+        translate([0, 2*totalHeight/3, 0]) puzzle_slot_x();
+    }
+    if (!hasTop) {
+        translate([totalWidth/3, totalHeight, 0]) puzzle_slot_y();
+        translate([2*totalWidth/3, totalHeight, 0]) puzzle_slot_y();
+    }
+
+    // --- Mounting Holes ---
+    // Left Border
+    if (hasLeft) { 
+        for (i = [1 : 3]) { 
+            translate([borderWidth / 2, bottomOffset + (i * gridHeight/4), -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+    
+    // Right Border
+    if (hasRight) { 
+        for (i = [1 : 3]) { 
+            translate([totalWidth - (borderWidth / 2), bottomOffset + (i * gridHeight/4), -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+
+    // Top Border
+    if (hasTop) { 
+        for (i = [1 : 3]) { 
+            translate([leftOffset + (i * gridWidth/4), totalHeight - (borderWidth / 2), -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+
+    // Bottom Border
+    if (hasBottom) { 
+        for (i = [1 : 3]) { 
+            translate([leftOffset + (i * gridWidth/4), borderWidth / 2, -1]) 
+            cylinder(h=thickness + 2, r=holeRad); 
+        } 
+    } 
+    
+    // Central wire pass-through hole in the middle of the X
+    translate([totalWidth/2, totalHeight/2, -1]) 
+    cylinder(d=25, h=thickness + 2);
+}

+ 113 - 0
LED_LENS.scad

@@ -0,0 +1,113 @@
+// -----------------------------------------------------------------------
+// Modular LED Matrix Prismatic Lens Array (Short Pegs / Manifold Fix)
+// -----------------------------------------------------------------------
+// NATIVE LANDSCAPE LAYOUT (3 columns x 2 rows)
+// Select which tile to generate:
+// 1 = Top Left      |  2 = Top Mid      |  3 = Top Right
+// 4 = Bottom Left   |  5 = Bottom Mid   |  6 = Bottom Right
+part = 1; // [1:6]
+
+// --- Matrix Parameters ---
+cellsX = 16;
+cellsY = 16;
+pitch = 10;
+meshSolid = 2;
+meshSpace = pitch - meshSolid; // 8mm hole space from the Front Grid
+borderWidth = 10;
+
+// --- Lens Array Parameters ---
+baseThickness = 0.4; // 2 layers at 0.2mm for an ultra-thin sandwich film
+clearance = 0.3; // Friction fit tolerance
+plugSize = meshSpace - clearance; // ~7.7mm peg
+plugHeight = 1.5; // SHORTENED: Snaps into hole but leaves an air gap above the LED
+prismSize = 1.5; // Size of the light-scattering pyramids on top
+prismHeight = 0.8; // Height of the pyramids
+
+// --- Logic ---
+hasLeft   = (part == 1 || part == 4) ? 1 : 0;
+hasRight  = (part == 3 || part == 6) ? 1 : 0;
+hasTop    = (part == 1 || part == 2 || part == 3) ? 1 : 0;
+hasBottom = (part == 4 || part == 5 || part == 6) ? 1 : 0;
+
+gridWidth = cellsX * pitch;
+gridHeight = cellsY * pitch;
+
+leftOffset = hasLeft * borderWidth;
+bottomOffset = hasBottom * borderWidth;
+
+totalWidth = gridWidth + (hasLeft * borderWidth) + (hasRight * borderWidth);
+totalHeight = gridHeight + (hasTop * borderWidth) + (hasBottom * borderWidth);
+
+holeRad = 2; // Clearance for M3 screws
+
+// --- Procedural Prismatic Refractor (Manifold Safe) ---
+module pyramid(size, h) {
+    // A 4-sided cylinder rotated 45 degrees makes a perfect, render-safe square pyramid
+    translate([size/2, size/2, 0])
+    rotate([0, 0, 45])
+    cylinder(h=h, r1=size/sqrt(2), r2=0, $fn=4);
+}
+
+module refractor_top(size, h, step) {
+    count = floor(size / step);
+    offset = (size - (count * step)) / 2;
+    translate([offset, offset, 0])
+    for(x=[0:count-1]) {
+        for(y=[0:count-1]) {
+            translate([x*step, y*step, 0])
+            pyramid(step, h);
+        }
+    }
+}
+
+// --- Main Build ---
+difference() {
+    union() {
+        // 1. The Ultra-Thin Sandwich Film Base
+        cube([totalWidth, totalHeight, baseThickness]);
+
+        // 2. The Extruded Diffuser Pegs
+        for (x = [0 : cellsX - 1]) {
+            for (y = [0 : cellsY - 1]) {
+                // Calculate position to exactly match the Front Grid holes
+                px = leftOffset + (meshSolid/2) + (x * pitch) + (clearance/2);
+                py = bottomOffset + (meshSolid/2) + (y * pitch) + (clearance/2);
+                
+                translate([px, py, baseThickness]) {
+                    // Solid Light Pipe
+                    cube([plugSize, plugSize, plugHeight]);
+                    
+                    // Prismatic Scatter Top
+                    translate([0, 0, plugHeight])
+                    refractor_top(plugSize, prismHeight, prismSize);
+                }
+            }
+        }
+    }
+
+    // 3. Punch alignment holes perfectly matched to the outer frame 
+    if (hasLeft) { 
+        for (i = [1 : 3]) { 
+            translate([borderWidth / 2, bottomOffset + (i * gridHeight/4), -0.1])
+            cylinder(h=baseThickness + plugHeight + prismHeight + 1, r=holeRad, $fn=32); 
+        } 
+    } 
+    if (hasRight) { 
+        for (i = [1 : 3]) { 
+            translate([totalWidth - (borderWidth / 2), bottomOffset + (i * gridHeight/4), -0.1])
+            cylinder(h=baseThickness + plugHeight + prismHeight + 1, r=holeRad, $fn=32); 
+        } 
+    } 
+    if (hasTop) { 
+        for (i = [1 : 3]) { 
+            translate([leftOffset + (i * gridWidth/4), totalHeight - (borderWidth / 2), -0.1])
+            cylinder(h=baseThickness + plugHeight + prismHeight + 1, r=holeRad, $fn=32); 
+        } 
+    } 
+    if (hasBottom) { 
+        for (i = [1 : 3]) { 
+            translate([leftOffset + (i * gridWidth/4), borderWidth / 2, -0.1])
+            cylinder(h=baseThickness + plugHeight + prismHeight + 1, r=holeRad, $fn=32); 
+        } 
+    } 
+}