/frame builder.scad
// Standard preamble stuff
$fn=$preview ? 20 : 64;

// All dimensions in mm
// width of the module boards
module_w = 38.1; // 1.5 inches
// height of the module boards
module_h = 46.99; // 1.85 inches
// Distance between the hole and the edge
hole_inset = 3.175;
// Extra space between module mounts
margin = 1;

// Breadboard width (not including tabs)
bb_w = 35.5;
// Breadboard height
bb_h = 47.5;
// Width of the breadboard tabs
bb_tab_w = 5;

// Outer retaining wall width
bb_wall = 2;

// Probably nothing configurable below here; skip to the bottom

// Width of the hole peg
hole_w = hole_inset * 2;
// Distance between holes on adjacent modules
inter_hole_distance = hole_w + margin;
// Distance between mounting holes, horizontal
hole_distance_x = module_w - hole_inset * 2;
// Distance between mounting holes, vertical
hole_distance_y = module_h - hole_inset * 2;
// Distance between mounting holes, diagonal
hole_distance_cross = sqrt(hole_distance_x^2 + hole_distance_y^2);
// Angle between diagonal holes
cross_angle = atan2(hole_distance_x, hole_distance_y);

bb_outer_w = bb_w + bb_wall * 2;
bb_outer_h = bb_h + bb_wall * 2;

module hole(h) {
    difference() {
        cylinder(h=h, r=hole_inset);
        translate([0, 0, -0.5])
        cylinder(h+1, 2.5/2, 2.5/2);
    }
    cylinder(h=3, r=hole_inset);
}

module vframe() {
    hole(7);
    translate([0, hole_distance_y/2, 1.5])
    cube([hole_w, hole_distance_y, 3], center=true);
    translate([0, hole_distance_y, 0])
    hole(7);
}

module frame() {
    vframe();
    
    translate([hole_distance_x, 0, 0])
    vframe();
    
    translate([hole_distance_x/2, 0, 1.5])
    cube([hole_distance_x, hole_w, 3], center=true);

    translate([hole_distance_x/2, hole_distance_y, 1.5])
    cube([hole_distance_x, hole_w, 3], center=true);
    
    translate([hole_distance_x/2, hole_distance_y/2, 1.5])
    rotate([0, 0, 90-cross_angle])
    cube([hole_distance_cross, hole_w, 3], center=true);
    
    translate([hole_distance_x/2, hole_distance_y/2, 1.5])
    rotate([0, 0, 90+cross_angle])
    cube([hole_distance_cross, hole_w, 3], center=true);
}

module module_row(n, skip=0, row=0) {
    for (i = [skip:n-1+skip]) {
        translate([i * (module_w + margin) + hole_inset, row * (module_h + margin) + hole_inset, 0])
        frame();
        
        if (i > skip) {
            translate([i * (module_w + margin) - hole_inset - margin, row * (module_h + margin), 0])
            cube([inter_hole_distance, module_h, 3]);
        }
    }
    if (row > 0) {
        translate([skip * (module_w + margin), row * (module_h + margin) - hole_inset - margin, 0])
        cube([n * (module_w + margin) - margin, inter_hole_distance, 3]);
    }
}

module breadboard() {
    difference() {
        cube([bb_outer_w, bb_outer_h, 5]);
        translate([bb_wall, bb_wall, 1])
        cube([bb_w, bb_h, 4.1]);
        
        translate([bb_w/2 - bb_tab_w/2 + bb_wall, bb_h + bb_wall - 0.1, 1])
        cube([bb_tab_w, bb_wall + 0.2, 4.1]);
        
        translate([-0.1, bb_h/2 - bb_tab_w/2 + bb_wall, 1])
        cube([bb_wall + 0.2, bb_tab_w+0.1, 4.1]);
    }
}

// Put a breadboard in the second row, first column
translate([0, module_h, 0])
breadboard();
// Patch left edge where frame and breadboard meet
translate([0, module_h - hole_inset, 0])
cube([bb_wall, 4, 5]);

// A row of modules in the first row
module_row(2);
// One module in the second row, starting in the second column
module_row(1, skip=1, row=1);