Skip to content

Instantly share code, notes, and snippets.

@sowbug
Created May 27, 2025 16:59
Show Gist options
  • Save sowbug/22fd84a627a956d4855ad8b134f821e0 to your computer and use it in GitHub Desktop.
Save sowbug/22fd84a627a956d4855ad8b134f821e0 to your computer and use it in GitHub Desktop.
OpenSCAD file for 37-vial upright tray that fits in Igloo Legend 6-can cooler
// https://github.com/revarbat/BOSL/wiki/masks.scad#fillet
include <BOSL/constants.scad>
use <BOSL/masks.scad>
///////////////////////////////////////
// Configurable parameters
// 3mL vial
// https://www.amazon.com/dp/B0CP3XN582
actual_vial_diameter = 16.2;
actual_vial_height = 38.7;
// Add a little to make sure the vials fit
vial_width = actual_vial_diameter + 0.3;
vial_height = actual_vial_height + 1.3;
// Needed to offset cylinder projections
half_vial_width = vial_width / 2;
// Space between side-by-side vials
vial_spacing_x = 1.5;
// Space between bottom of one vial and top of the next
vial_spacing_y = 0.8;
// Igloo Legend 6-Can Cooler, Red, 5 Qt
// https://www.amazon.com/dp/B00BC3ISDA
//
// These are the interior dimensions of the usable space
// Full size tray
max_container_width = 204; // Container width
max_container_depth = 152; // Container depth
max_container_height = 149; // Container height
// Faster to print a smaller tray to test the design
// Uncomment these to enable
// max_container_width = 80; // Container width
// max_container_depth = 50; // Container depth
// max_container_height = 40; // Container height
// Margin outside the tray but inside the container
container_margin = 0.5;
two_container_margins = container_margin * 2;
// Margin inside the walls of the tray
tray_margin = 0.5;
two_tray_margins = tray_margin * 2;
wall_thickness = 1.25; // Thickness of the exterior tray walls
two_walls = wall_thickness * 2;
// End configurable parameters
///////////////////////////////////////
// Calculate the maximum tray dimensions so we can calculate what fits
max_tray_width = max_container_width - two_container_margins;
max_tray_depth = max_container_depth - two_container_margins;
max_tray_height = vial_width + wall_thickness;
// How many vials can we fit? We add the vial spacing to the numerator as well
// to compensate for the fact that there are only N-1 spaces between N vials.
vials_x = floor((max_tray_width - two_walls + vial_spacing_x) / (vial_width + vial_spacing_x));
vials_y = floor((max_tray_depth - two_walls + vial_spacing_y) / (vial_height + vial_spacing_y));
// We also want to fit some sideways vials in the excess space above the top row.
// We assume there's only one row.
vials_side_x = floor((max_tray_width - two_walls - two_tray_margins + vial_spacing_y) /
(vial_height + vial_spacing_x));
vials_side_y = 1;
// Now calculate the actual tray dimensions. HACK: we allow the sideways row to
// take up extra depth, but not extra width.
side_vials_depth =
vials_side_y // number of rows
* vial_width // the width/diameter of a vial
+ (vials_side_y - 1) // the number of spaces between rows (we know this is zero)
* vial_spacing_y // the space between rows of vials
+ vial_spacing_y; // the space between the upright and sideways rows
tray_width = ceil(vials_x * vial_width + (vials_x - 1) * vial_spacing_x + two_tray_margins + two_walls);
tray_depth = ceil(vials_y * vial_height + (vials_y - 1) * vial_spacing_y + two_tray_margins + side_vials_depth + two_walls);
tray_height = ceil(wall_thickness + vial_width);
side_vials_width =
vials_side_x // number of rows
* vial_height // the height of a vial
+ (vials_side_x - 1) // the number of spaces between rows (we know this is zero)
* vial_spacing_y; // the space between rows of vials
side_vials_centering_offset = (tray_width - side_vials_width) / 2;
vials_per_tray = vials_x * vials_y + vials_side_x * vials_side_y;
trays_per_container = floor(max_container_height / tray_height);
echo("Container dimensions WxDxH", max_container_width, max_container_depth, max_container_height);
echo("Tray dimensions WxDxH", tray_width, tray_depth, tray_height);
echo("Max vials/tray", vials_per_tray);
echo("Max trays/container", trays_per_container);
echo("Max vials/container", vials_per_tray * trays_per_container);
// Create the base of the tray
module tray_shape() {
// Think of grip_factor as how much of the circle you want to hug the vial.
// 0.5 is a perfect half-circle, so vials will stay in place if the tray is
// laying flat, but they'll fall out if you want the tray on its side (so
// that the vials are upright). 0.6 leaves 60% of the circle, which after
// some experimentation seemed to provide just enough of a friction fit
// without requiring too much force to remove.
grip_factor = 0.6;
internal_width = tray_width - wall_thickness * 2;
internal_depth = tray_depth - wall_thickness * 2;
internal_height = tray_height - wall_thickness - tray_height * 0.6;
difference() {
cube([tray_width, tray_depth, tray_height]);
translate([wall_thickness, wall_thickness, tray_height - internal_height]) {
cube([internal_width, internal_depth, internal_height]);
}
}
}
// Create upright vials
module upright_vials(y) {
for (x = [0:vials_x - 1]) {
translate([ x * (vial_width + vial_spacing_x),
y * (vial_height + vial_spacing_y),
0]) {
translate([half_vial_width, 0, 0]) { // Center the vials
rotate([0, 90, 90]) { // Rotate the vials to be upright
cylinder(h = vial_height, d = vial_width);
}
}
}
}
}
// Create sideways vials
module sideways_vials(y) {
for (x = [0:vials_side_x - 1]) {
translate([ x * (vial_height + vial_spacing_y) + tray_margin,
half_vial_width + vial_spacing_x + y * (vial_width + vial_spacing_x),
0]) {
rotate([0, 90, 0]) {
cylinder(h = vial_height, d = vial_width);
}
}
}
}
module grip_handle() {
handle_width = 8;
handle_depth = 2;
handle_height = 1;
hole_inset = 0.05; // What percentage of the handle is the hole
translate([tray_width / 2 , tray_depth + (handle_depth / 2) * 0.2, handle_height / 2]) {
fillet(fillet=0.25, size = [handle_width, handle_depth, handle_height], $fn=24) {
difference() {
cube([handle_width, handle_depth, handle_height], center=true);
cube([handle_width * hole_inset, handle_depth * hole_inset, handle_height], center=true);
}
}
}
}
module tray() {
fillet(fillet=2, size = [tray_width, tray_depth, tray_height], $fn=24) {
translate([-tray_width / 2, -tray_depth / 2, -tray_height / 2]) {
difference() {
tray_shape();
// Regular vial orientation
translate([wall_thickness + tray_margin,
wall_thickness + tray_margin,
wall_thickness + half_vial_width]) {
for (y = [0:vials_y-1]) {
upright_vials(y);
}
}
// Side vial orientation
vial_y_space = vials_y * (vial_height) + (vials_y - 1) * vial_spacing_y;
translate([side_vials_centering_offset,
wall_thickness + vial_y_space + vial_spacing_y,
wall_thickness + half_vial_width]) {
for (y = [0:vials_side_y-1]) {
sideways_vials(y);
}
}
}
}
}
}
module handle() {
translate([-tray_width / 2, -tray_depth / 2, -tray_height / 2]) {
grip_handle();
}
}
tray();
// removed because it adds too much height
// handle();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment