$fn=64; // --- Parameters --- num_miniatures = 10; base_diameter = 25.5; // Fits standard 25mm miniature bases with slight clearance slot_depth = 2.0; // Depth of the recess holding the base base_thickness = 2.0; // Thickness of the strip underneath the miniatures spacing = 3.0; // Space between each miniature slot margin = 4.0; // Outer border margin corner_radius = 3.0; // Rounding of the strip's outer corners // --- Derived Dimensions --- strip_width = base_diameter + (margin * 2); strip_length = (base_diameter * num_miniatures) + (spacing * (num_miniatures - 1)) + (margin * 2); strip_height = base_thickness + slot_depth; // --- Main Module --- module miniature_stand_strip() { difference() { // Main solid strip body with rounded corners hull() { translate([corner_radius, corner_radius, 0]) cylinder(h=strip_height, r=corner_radius); translate([strip_length - corner_radius, corner_radius, 0]) cylinder(h=strip_height, r=corner_radius); translate([strip_length - corner_radius, strip_width - corner_radius, 0]) cylinder(h=strip_height, r=corner_radius); translate([corner_radius, strip_width - corner_radius, 0]) cylinder(h=strip_height, r=corner_radius); } // Cutouts for the miniature bases for (i = [0 : num_miniatures - 1]) { x_pos = margin + (base_diameter / 2) + i * (base_diameter + spacing); y_pos = strip_width / 2; // Translate to position and elevate by base_thickness // Height is extended slightly to prevent Z-fighting rendering issues translate([x_pos, y_pos, base_thickness]) cylinder(h=slot_depth + 1.0, d=base_diameter); } } } // Render and center the model on the origin translate([-strip_length / 2, -strip_width / 2, 0]) miniature_stand_strip();