removed some dead code

removed some dead code

eps = 0.01;

module qiagen_logo()
{
  translate([-10.5, -9, 0])
    translate([0,0,1+1])
      linear_extrude(1)
        scale(.42*[1,1])
          import("qiagen_logo.svg");
}

difference()
{
  cylinder(d=30, h=1+1, $fn=100);
  translate([0,0,-eps])
    linear_extrude(1+eps)
      text("#!", size=18, halign="center", valign="center");
}
qiagen_logo();

eps = 0.01;

module qiagen_logo()
{
  translate([-10.5, -9, 0])
    translate([0,0,1+1])
      linear_extrude(1)
//        mirror([1,0,0])
          scale(.42*[1,1])
            import("qiagen_logo.svg");
}

difference()
{
  cylinder(d=30, h=1+1, $fn=100);
  translate([0,0,-eps])
    linear_extrude(1+eps)
      text("#!", size=18, halign="center", valign="center");
}
#qiagen_logo();


Hardware vitamins for modelling the battery pack

// Battery packs
// battery_pack_linear - all batteries in a row
// battery_pack_double - 2 rows of batteries (can't cope with odd numbers!)

// Point of origin is the centre of the end of the end battery.

// AAs - http://en.wikipedia.org/wiki/AA_battery
// len includes the positive-end button
Battery_dia = 14.5;
Battery_len = 50.5;

module battery() {

  linear_extrude(height=Battery_len) {
    circle(r=Battery_dia/2);
  }
}

module battery_pack_linear(battery_sep, battery_count) {
  for(i=[0:battery_count-1]) {
    translate([i*(Battery_dia+battery_sep), 0, 0])
      battery();
  }
}

module battery_pack_double(battery_sep, battery_count) {
  for(i=[0:(battery_count/2)-1]) {
    translate([i*(Battery_dia+battery_sep), 0, 0])
      battery();
    translate([i*(Battery_dia+battery_sep), Battery_dia+battery_sep, 0])
      battery();
  }
}

*battery_pack_linear(2,4);
*battery_pack_double(2, 4);

error: pathspec 'hardware/vitamins/BatteryPack.scad' did not match any file(s) known to git


generation now prints multiple elements at a time

h=2;
count=3;

module extension()
{
  cube([25,10,h]);
  translate([25,5,0])
    cylinder(h=h, r=5, $fs=0.1);
}

for(x=[0:2])
  translate([0, x*13, 0])
    extension();


Head tuning

Head tuning

// Chilicorn
$fn=45;

head_big_radius = 10;
head_small_radius = 4.5;
head_spacing = 18;
head_angle = 50;

horn_length = 20;
horn_radius = 2.8;

neck_radius = 2;
neck_ratio = .2; // Flattens the interface to the top body
neck_top_x = 3;

body_x = 15;
body_z = 28;
body_radius = 15;
body_back_radius = 13;
body_length = 33;

leg_length = 28 + body_radius;
leg_radius = 4;

cute_leg_first_ratio = 0.65;
cute_leg_second_ratio = 0.25;
cute_leg_third_ratio = 0.11;

module head() {
    rotate([0, -head_angle, 0]) {
        hull () {
            sphere(r=head_big_radius);
            translate([-head_spacing, 0, 0])
                sphere(r=head_small_radius);
        }
    }
}

module horn() {
    rotate([0, -head_angle, 0]) {
        translate([0, 0, head_big_radius - .2])
        cylinder(h = horn_length, r1 = horn_radius, r2 = 0);
    }
}

module neck() {
    hull() {
        translate([neck_top_x, 0, 0]) rotate([0, 180, 0]) {
            cylinder(h = head_big_radius - .2, r1 = neck_radius, r2 = neck_radius);
        }
        translate([body_x, 0, -body_z])
            difference() {
                sphere(r = body_radius);
                translate([body_radius*neck_ratio, -body_radius, -body_radius])
                    cube([body_radius, 2*body_radius, 2*body_radius]);
            }
        }
}

module mane() {
    rotate([0, -neck_angle + 180, 0]) {
        cylinder(h = neck_length + head_big_radius - .2, r1 = neck_radius, r2 = neck_radius);
    }
}

module body() {
    translate([body_x, 0, -body_z])
        hull() {
            sphere(r = body_radius);
            translate([body_length, 0, 0])
                sphere(r = body_back_radius);
        }
}

module leg(lengthwards=0, sideways=0, theta=20) {
    translate([body_x + lengthwards, sideways, -body_z])
        rotate([180 + theta, 0, 0]) {
        cylinder(h = leg_length, r1 = leg_radius, r2 = leg_radius);
        }
}

module cute_leg(lengthwards=0, sideways=0, downwards=0, theta=20, rise_angle=70) {
    translate([body_x + lengthwards, sideways, -body_z - downwards])
        rotate([180 + theta, rise_angle, 0]) {
            cylinder(h = leg_length*cute_leg_first_ratio, r1 = leg_radius, r2 = leg_radius);
            translate([0, 0, leg_length*cute_leg_first_ratio])
                union() {
                    sphere(r = leg_radius);
                    rotate([0, 90, 0]) {
                        cylinder(h = leg_length*cute_leg_second_ratio, r1 = leg_radius, r2 = leg_radius);
                    }
                        translate([leg_length*cute_leg_second_ratio, 0, 0])
                            union() {
                                sphere(r = leg_radius);
                                rotate([0, 90 + rise_angle, 0]) {
                                cylinder(h = leg_length*cute_leg_third_ratio, r1 = leg_radius, r2 = leg_radius);
                                }
                            }
                }
    }
}

head();
horn();
neck();
body();
leg(theta=5, sideways=3);
cute_leg(theta=-5, sideways=-3, downwards=3);
leg(lengthwards = body_length, sideways=2, theta=15);
leg(lengthwards = body_length, sideways=-2, theta=-15);

// Chilicorn
$fn=45;

head_big_radius = 10;
head_small_radius = 5;
head_spacing = 18;
head_angle = 45;

horn_length = 18;
horn_radius = 3;

neck_radius = 2;
neck_ratio = .2; // Flattens the interface to the top body
neck_top_x = 3;

body_x = 15;
body_z = 28;
body_radius = 15;
body_back_radius = 13;
body_length = 33;

leg_length = 28 + body_radius;
leg_radius = 4;

cute_leg_first_ratio = 0.65;
cute_leg_second_ratio = 0.25;
cute_leg_third_ratio = 0.11;

module head() {
    rotate([0, -head_angle, 0]) {
        hull () {
            sphere(r=head_big_radius);
            translate([-head_spacing, 0, 0])
                sphere(r=head_small_radius);
        }
    }
}

module horn() {
    rotate([0, -head_angle, 0]) {
        translate([0, 0, head_big_radius - .2])
        cylinder(h = horn_length, r1 = horn_radius, r2 = 0);
    }
}

module neck() {
    hull() {
        translate([neck_top_x, 0, 0]) rotate([0, 180, 0]) {
            cylinder(h = head_big_radius - .2, r1 = neck_radius, r2 = neck_radius);
        }
        translate([body_x, 0, -body_z])
            difference() {
                sphere(r = body_radius);
                translate([body_radius*neck_ratio, -body_radius, -body_radius])
                    cube([body_radius, 2*body_radius, 2*body_radius]);
            }
        }
}

module mane() {
    rotate([0, -neck_angle + 180, 0]) {
        cylinder(h = neck_length + head_big_radius - .2, r1 = neck_radius, r2 = neck_radius);
    }
}

module body() {
    translate([body_x, 0, -body_z])
        hull() {
            sphere(r = body_radius);
            translate([body_length, 0, 0])
                sphere(r = body_back_radius);
        }
}

module leg(lengthwards=0, sideways=0, theta=20) {
    translate([body_x + lengthwards, sideways, -body_z])
        rotate([180 + theta, 0, 0]) {
        cylinder(h = leg_length, r1 = leg_radius, r2 = leg_radius);
        }
}

module cute_leg(lengthwards=0, sideways=0, downwards=0, theta=20, rise_angle=70) {
    translate([body_x + lengthwards, sideways, -body_z - downwards])
        rotate([180 + theta, rise_angle, 0]) {
            cylinder(h = leg_length*cute_leg_first_ratio, r1 = leg_radius, r2 = leg_radius);
            translate([0, 0, leg_length*cute_leg_first_ratio])
                union() {
                    sphere(r = leg_radius);
                    rotate([0, 90, 0]) {
                        cylinder(h = leg_length*cute_leg_second_ratio, r1 = leg_radius, r2 = leg_radius);
                    }
                        translate([leg_length*cute_leg_second_ratio, 0, 0])
                            union() {
                                sphere(r = leg_radius);
                                rotate([0, 90 + rise_angle, 0]) {
                                cylinder(h = leg_length*cute_leg_third_ratio, r1 = leg_radius, r2 = leg_radius);
                                }
                            }
                }
    }
}

head();
horn();
neck();
body();
leg(theta=5, sideways=3);
cute_leg(theta=-5, sideways=-3, downwards=3);
leg(lengthwards = body_length, sideways=2, theta=15);
leg(lengthwards = body_length, sideways=-2, theta=-15);


Refine MicroUSB dimensions and overhang

Refine MicroUSB dimensions and overhang

/*
  Vitamin: ArduinoPro
  Model of an Arduino Pro Mini/Micro

  Derived from: https://github.com/sparkfun/Pro_Micro/
           and: https://github.com/sparkfun/Arduino_Pro_Mini_328/

  Authors:
    Jamie Osborne (@jmeosbn)

  Local Frame:
    Point of origin is the centre of the bottom left pin (9)

  Parameters:
    type: can be "mini", or "micro";
          otherwise returns a blank board with header pin holes

  Returns:
    Model of an Arduino Pro Mini/Micro with header holes
*/

// ArduinoPro PCB Variables
ArduinoPro_PCB_Pitch  = 2.54;                       // spacing of the holes
ArduinoPro_PCB_Inset  = ArduinoPro_PCB_Pitch/2;     // inset from board edge
ArduinoPro_PCB_Width  =  0.7 * 25.4;                // width of the PCB     (along x)
ArduinoPro_PCB_Length =  1.3 * 25.4;                // length of the PCB    (along y)
ArduinoPro_PCB_Height = .063 * 25.4;                // thickness of the PCB (along z)
ArduinoPro_PCB_Colour = [26/255, 90/255, 160/255];  // colour of solder mask

// Show board clearance area for components
ArduinoPro_PCB_Clearance = 3;
ArduinoPro_PCB_Clearance_Show  = true;

// FIXME: export to external vitamin?
module Micro_USB_Clearance() {
  // dimensions for micro usb header
  // http://www.farnell.com/datasheets/1693470.pdf
  height = 2.8;
  width  = 7.5;
  depth  = 5.3;
  flange = 7.8;  // width of front flange

  // distance to overhang board edge
  offset  = 2.15 + 0.6 - 1.45;

  move_x  = ArduinoPro_PCB_Width / 2 - ArduinoPro_PCB_Inset;
  move_y  = ArduinoPro_PCB_Length - ArduinoPro_PCB_Inset + offset - depth/2;
  move_z  = ArduinoPro_PCB_Height;

  color(Grey80)
  translate([move_x, move_y, move_z])
    linear_extrude(height=height)
    square([flange, depth], center=true);
}

module ArduinoPro_PCB() {
  // Bare PCB, origin through location for bottom left hole
  translate([-ArduinoPro_PCB_Inset, -ArduinoPro_PCB_Inset, 0])
    square(size = [ArduinoPro_PCB_Width, ArduinoPro_PCB_Length]);
}

// TODO: Add throughhole/padding
// TODO: Option to show header pins?
// FIXME: export to external vitamin?
module ArduinoPro_Header_Hole(d = 1.25) {
  // Standard PCB hole
  circle(d = d);
}

module ArduinoPro_Headers() {
  // distance between the two header rows
  rowpitch  = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;
  // length for holes, leaving room for end header and insets
  rowlength = ArduinoPro_PCB_Length - ArduinoPro_PCB_Pitch - ArduinoPro_PCB_Inset*2;

  // Add headers to either side (along y)
  for (x = [0, rowpitch], y = [0 : ArduinoPro_PCB_Pitch : rowlength]) {
    translate([x, y, 0]) ArduinoPro_Header_Hole();
  }

}

module ArduinoPro_Serial_Header() {
  // y position for header
  header_y = ArduinoPro_PCB_Length - ArduinoPro_PCB_Inset*2;
  // width for holes, leaving room for insets
  rowwidth = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;

  // Add serial header along top of board (along x)
  for (x = [ArduinoPro_PCB_Inset : ArduinoPro_PCB_Pitch : rowwidth]) {
    translate([x, header_y, 0]) ArduinoPro_Header_Hole();
  }
}

module ArduinoPro(type = "mini") {
  union() {
    color(ArduinoPro_PCB_Colour)
    linear_extrude(height=ArduinoPro_PCB_Height)
    difference() {
      // Common Features for Pro Mini/Micro
      ArduinoPro_PCB();
      ArduinoPro_Headers();

      // Features for Pro Mini
      if (type == "mini") {
        // Pro Mini Serial Header
        ArduinoPro_Serial_Header();
      }
    }

    // Features for Pro Micro
    if (type == "micro") {
      // Pro Micro USB port
      Micro_USB_Clearance();
    }

    // Indicate area for minimum board clearance
    if (ArduinoPro_PCB_Clearance_Show)
      color(ArduinoPro_PCB_Colour, 0.1)
      translate([-ArduinoPro_PCB_Inset, -ArduinoPro_PCB_Inset, ArduinoPro_PCB_Height])
      linear_extrude(height=ArduinoPro_PCB_Clearance)
      square(size = [ArduinoPro_PCB_Width, ArduinoPro_PCB_Length]);
  }
}

// Example Usage
*ArduinoPro("mini");
*ArduinoPro("micro");

/*
  Vitamin: ArduinoPro
  Model of an Arduino Pro Mini/Micro

  Derived from: https://github.com/sparkfun/Pro_Micro/
           and: https://github.com/sparkfun/Arduino_Pro_Mini_328/

  Authors:
    Jamie Osborne (@jmeosbn)

  Local Frame:
    Point of origin is the centre of the bottom left pin (9)

  Parameters:
    type: can be "mini", or "micro";
          otherwise returns a blank board with header pin holes

  Returns:
    Model of an Arduino Pro Mini/Micro with header holes
*/

// ArduinoPro PCB Variables
ArduinoPro_PCB_Pitch  = 2.54;                       // spacing of the holes
ArduinoPro_PCB_Inset  = ArduinoPro_PCB_Pitch/2;     // inset from board edge
ArduinoPro_PCB_Width  =  0.7 * 25.4;                // width of the PCB     (along x)
ArduinoPro_PCB_Length =  1.3 * 25.4;                // length of the PCB    (along y)
ArduinoPro_PCB_Height = .063 * 25.4;                // thickness of the PCB (along z)
ArduinoPro_PCB_Colour = [26/255, 90/255, 160/255];  // colour of solder mask

// Show board clearance area for components
ArduinoPro_PCB_Clearance = 3;
ArduinoPro_PCB_Clearance_Show  = true;

// FIXME: export to external vitamin?
module Micro_USB_Clearance() {
  // Rough dimensions for micro usb header
  height  = 2.7;
  area    = [7.7, 5.2];
  colour  = [220/255, 220/255, 220/255];
  move_x  = ArduinoPro_PCB_Width/2 - ArduinoPro_PCB_Inset;
  move_y  = ArduinoPro_PCB_Length - ArduinoPro_PCB_Pitch;
  move_z  = ArduinoPro_PCB_Height;

  color(colour)
    translate([move_x, move_y, move_z])
    linear_extrude(height=height)
    square(size = area, center = true);
}

module ArduinoPro_PCB() {
  // Bare PCB, origin through location for bottom left hole
  translate([-ArduinoPro_PCB_Inset, -ArduinoPro_PCB_Inset, 0])
    square(size = [ArduinoPro_PCB_Width, ArduinoPro_PCB_Length]);
}

// TODO: Add throughhole/padding
// TODO: Option to show header pins?
// FIXME: export to external vitamin?
module ArduinoPro_Header_Hole(d = 1.25) {
  // Standard PCB hole
  circle(d = d);
}

module ArduinoPro_Headers() {
  // distance between the two header rows
  rowpitch  = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;
  // length for holes, leaving room for end header and insets
  rowlength = ArduinoPro_PCB_Length - ArduinoPro_PCB_Pitch - ArduinoPro_PCB_Inset*2;

  // Add headers to either side (along y)
  for (x = [0, rowpitch], y = [0 : ArduinoPro_PCB_Pitch : rowlength]) {
    translate([x, y, 0]) ArduinoPro_Header_Hole();
  }

}

module ArduinoPro_Serial_Header() {
  // y position for header
  header_y = ArduinoPro_PCB_Length - ArduinoPro_PCB_Inset*2;
  // width for holes, leaving room for insets
  rowwidth = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;

  // Add serial header along top of board (along x)
  for (x = [ArduinoPro_PCB_Inset : ArduinoPro_PCB_Pitch : rowwidth]) {
    translate([x, header_y, 0]) ArduinoPro_Header_Hole();
  }
}

module ArduinoPro(type = "mini") {
  union() {
    color(ArduinoPro_PCB_Colour)
    linear_extrude(height=ArduinoPro_PCB_Height)
    difference() {
      // Common Features for Pro Mini/Micro
      ArduinoPro_PCB();
      ArduinoPro_Headers();

      // Features for Pro Mini
      if (type == "mini") {
        // Pro Mini Serial Header
        ArduinoPro_Serial_Header();
      }
    }

    // Features for Pro Micro
    if (type == "micro") {
      // Pro Micro USB port
      Micro_USB_Clearance();
    }

    // Indicate area for minimum board clearance
    if (ArduinoPro_PCB_Clearance_Show)
      color(ArduinoPro_PCB_Colour, 0.1)
      translate([-ArduinoPro_PCB_Inset, -ArduinoPro_PCB_Inset, ArduinoPro_PCB_Height])
      linear_extrude(height=ArduinoPro_PCB_Clearance)
      square(size = [ArduinoPro_PCB_Width, ArduinoPro_PCB_Length]);
  }
}

// Example Usage
*ArduinoPro("mini");
*ArduinoPro("micro");


rack: started

rack: started


wall mount prototype done

wall mount prototype done

use <joint.scad>

module _wallSurface()
{
  difference()
  {
    union()
    {
      // root element
      cube([150, 40, 2]);
      // lock mount
      translate([(150-20)/2, 40, 0])
        cube([20, 7, 2]);
    }
    // holes
    for(offset = [30, 90])
      translate([offset, 10, 0])
        cube([30, 20, 2]);
  }
}

module lock()
{
  translate([0, 3, 0])
    rotate([0, 0, -90])
      joint(4);
}

module wallMount()
{
  _wallSurface();
  for(offset = [10, 150-10-3])
    translate([offset, 0, 2])
      joint(2);
  translate([(150-20)/2 + 10-6.5/2, 40+7-3, 2])
    lock();
}

wallMount();

module wallMount()
{
  cube([2,3,4]);
}

wallMount();


update rudder dimensions

update rudder dimensions

/* Machine */
MACHINE_TOLERANCE = 0; //For the JAS laser at Maker Space
/* MACHINE_TOLERANCE = 0.1; //For most other lasers */

/* Material */
MATERIAL_THICKNESS = 3;
HALF_MATERIAL_THICKNESS = MATERIAL_THICKNESS / 2;

/* General */
CORNER = 1;
HALF_CORNER = CORNER / 2;
SPACER_TAB_WIDTH_FACTOR = 0.35;

/* Base plate */
BASE_DIMENSIONS = [180, 240];
BASE_FRONT_CORNER_RADIUS = BASE_DIMENSIONS[0] / 2;
BASE_REAR_CORNER_RADIUS = 30;

/* Lift fan duct mount */
LIFT_FAN_DUCT_DIMENSIONS = [71, 28]; // [diameter, length]
LIFT_FAN_DUCT_MOUNT_PADDING = 30;
LIFT_FAN_DUCT_POSITION = [0, (BASE_DIMENSIONS[1] / 2) - LIFT_FAN_DUCT_DIMENSIONS[0]];
LIFT_FAN_DUCT_MOUNT_SPACING = 18;
LIFT_FAN_DUCT_MOUNT_SEP_WIDTH = 14;

/* Thrust fan duct mount */
THRUST_FAN_DUCT_DIMENSIONS = [74.5, 48]; // [diameter, length]
THRUST_FAN_DUCT_HEIGHT = 60;
THRUST_FAN_DUCT_MOUNT_PADDING = 30;
THRUST_FAN_DUCT_MOUNT_POSITION = [0, -40];
THRUST_FAN_DUCT_MOUNT_SPACING = THRUST_FAN_DUCT_DIMENSIONS[1] - 8;
THRUST_FAN_DUCT_MOUNT_WIDTH = BASE_DIMENSIONS[0] * 0.8;
THRUST_FAN_DUCT_MOUNT_SEP_WIDTH = 14;
THRUST_FAN_DUCT_MOUNT_TAB_POSITIONS = [-THRUST_FAN_DUCT_MOUNT_WIDTH * 0.4, 0, THRUST_FAN_DUCT_MOUNT_WIDTH * 0.4];
THRUST_FAN_DUCT_MOUNT_TAB_WIDTH = 10;
THRUST_FAN_DUCT_MOUNT_BRACE_DIM = [25, THRUST_FAN_DUCT_HEIGHT * 0.95];
THRUST_FAN_DUCT_MOUNT_BRACE_SPACING = THRUST_FAN_DUCT_MOUNT_WIDTH * 0.7;
THRUST_FAN_DUCT_MOUNT_BRACE_TOP_TAB_WIDTH = 10;
THRUST_FAN_DUCT_MOUNT_BRACE_TOP_TAB_POS = THRUST_FAN_DUCT_MOUNT_BRACE_DIM[1] * 0.8;
THRUST_FAN_DUCT_MOUNT_BRACE_BOTTOM_TAB_WIDTH = THRUST_FAN_DUCT_MOUNT_BRACE_DIM[0] * 0.75;
THRUST_FAN_DUCT_MOUNT_BRACE_BOTTOM_TAB_POS = THRUST_FAN_DUCT_MOUNT_BRACE_DIM[0] * 0.5;

/* Rudder */
RUDDER_DIMENSIONS = [30, THRUST_FAN_DUCT_DIMENSIONS[0] * 0.9];
RUDDER_CORNER_RADIUS = 2;
RUDDER_PIVOT_LENGTH = 5;
RUDDER_SPACING = THRUST_FAN_DUCT_DIMENSIONS[0] * 0.475;
RUDDER_ARM_HOLE_POSITION = [RUDDER_DIMENSIONS[0] * 0.2, -RUDDER_DIMENSIONS[1] * 0.45];
RUDDER_ARM_HOLE_DIAM = 2;

/* Rudder mount */
RUDDER_MOUNT_DIMENSIONS = [THRUST_FAN_DUCT_DIMENSIONS[0] * 0.8, 10];
RUDDER_MOUNT_CORNER_RADIUS = 5;
RUDDER_MOUNT_OFFSET = (RUDDER_DIMENSIONS[1] / 2) + MATERIAL_THICKNESS;
RUDDER_MOUNT_TAB_WIDTH = 5;
RUDDER_MOUNT_TAB_OFFSET = (RUDDER_MOUNT_DIMENSIONS[0] - RUDDER_MOUNT_TAB_WIDTH) / 2;

/* Skirt clamp */
SKIRT_CLAMP_PADDING = 10;
SKIRT_CLAMP_HOLE_DIAM = 3;

/* Lower skirt mount */
LOWER_SKIRT_MOUNT_SCALE = 0.6;

/* Machine */
MACHINE_TOLERANCE = 0; //For the JAS laser at Maker Space
/* MACHINE_TOLERANCE = 0.1; //For most other lasers */

/* Material */
MATERIAL_THICKNESS = 3;
HALF_MATERIAL_THICKNESS = MATERIAL_THICKNESS / 2;

/* General */
CORNER = 1;
HALF_CORNER = CORNER / 2;
SPACER_TAB_WIDTH_FACTOR = 0.35;

/* Base plate */
BASE_DIMENSIONS = [180, 240];
BASE_FRONT_CORNER_RADIUS = BASE_DIMENSIONS[0] / 2;
BASE_REAR_CORNER_RADIUS = 30;

/* Lift fan duct mount */
LIFT_FAN_DUCT_DIMENSIONS = [71, 28]; // [diameter, length]
LIFT_FAN_DUCT_MOUNT_PADDING = 30;
LIFT_FAN_DUCT_POSITION = [0, (BASE_DIMENSIONS[1] / 2) - LIFT_FAN_DUCT_DIMENSIONS[0]];
LIFT_FAN_DUCT_MOUNT_SPACING = 18;
LIFT_FAN_DUCT_MOUNT_SEP_WIDTH = 14;

/* Thrust fan duct mount */
THRUST_FAN_DUCT_DIMENSIONS = [74.5, 48]; // [diameter, length]
THRUST_FAN_DUCT_HEIGHT = 60;
THRUST_FAN_DUCT_MOUNT_PADDING = 30;
THRUST_FAN_DUCT_MOUNT_POSITION = [0, -40];
THRUST_FAN_DUCT_MOUNT_SPACING = THRUST_FAN_DUCT_DIMENSIONS[1] - 8;
THRUST_FAN_DUCT_MOUNT_WIDTH = BASE_DIMENSIONS[0] * 0.8;
THRUST_FAN_DUCT_MOUNT_SEP_WIDTH = 14;
THRUST_FAN_DUCT_MOUNT_TAB_POSITIONS = [-THRUST_FAN_DUCT_MOUNT_WIDTH * 0.4, 0, THRUST_FAN_DUCT_MOUNT_WIDTH * 0.4];
THRUST_FAN_DUCT_MOUNT_TAB_WIDTH = 10;
THRUST_FAN_DUCT_MOUNT_BRACE_DIM = [25, THRUST_FAN_DUCT_HEIGHT * 0.95];
THRUST_FAN_DUCT_MOUNT_BRACE_SPACING = THRUST_FAN_DUCT_MOUNT_WIDTH * 0.7;
THRUST_FAN_DUCT_MOUNT_BRACE_TOP_TAB_WIDTH = 10;
THRUST_FAN_DUCT_MOUNT_BRACE_TOP_TAB_POS = THRUST_FAN_DUCT_MOUNT_BRACE_DIM[1] * 0.8;
THRUST_FAN_DUCT_MOUNT_BRACE_BOTTOM_TAB_WIDTH = THRUST_FAN_DUCT_MOUNT_BRACE_DIM[0] * 0.75;
THRUST_FAN_DUCT_MOUNT_BRACE_BOTTOM_TAB_POS = THRUST_FAN_DUCT_MOUNT_BRACE_DIM[0] * 0.5;

/* Rudder */
RUDDER_DIMENSIONS = [20, THRUST_FAN_DUCT_DIMENSIONS[0] * 0.9];
RUDDER_CORNER_RADIUS = 2;
RUDDER_PIVOT_LENGTH = 5;
RUDDER_SPACING = THRUST_FAN_DUCT_DIMENSIONS[0] * 0.475;
RUDDER_ARM_HOLE_POSITION = [RUDDER_DIMENSIONS[0] * 0.4, -RUDDER_DIMENSIONS[1] * 0.45];
RUDDER_ARM_HOLE_DIAM = 2;

/* Rudder mount */
RUDDER_MOUNT_DIMENSIONS = [THRUST_FAN_DUCT_DIMENSIONS[0] * 0.8, 10];
RUDDER_MOUNT_CORNER_RADIUS = 5;
RUDDER_MOUNT_OFFSET = (RUDDER_DIMENSIONS[1] / 2) + MATERIAL_THICKNESS;
RUDDER_MOUNT_TAB_WIDTH = 5;
RUDDER_MOUNT_TAB_OFFSET = (RUDDER_MOUNT_DIMENSIONS[0] - RUDDER_MOUNT_TAB_WIDTH) / 2;

/* Skirt clamp */
SKIRT_CLAMP_PADDING = 10;
SKIRT_CLAMP_HOLE_DIAM = 3;

/* Lower skirt mount */
LOWER_SKIRT_MOUNT_SCALE = 0.6;


Second try at case, using U-panels to make a box, coming along very well; test print ongoing..

/* Case parts for Zikzak rev3
 * www.zikzak.ca
 */
 use <_fanholes.scad>;
 use <_assert.scad>;

 // ref
// colours: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations#color

// TODO:
// - FIX: INVERT
// - VERIFY: HEIGHT positions to actually be right for jacks, relative to height padding
// - FIX: so it actually fits together; sloppyness ratio? or just use scaling?
// - add cart hole 'sides' as guides for the cart to go down nicely into edge connector
// - 2 fastener bits per side instead of 1 ... problem is jacks

// render target
// - frontbacktop
// - leftrightbottom
// - all
target = "frontbacktop";
//target = "leftrightbottom";
//target = "all";

// scale? Use this to print out tiny versions for inspection prior to printing real ones
scale_factor_x = 1;
scale_factor_y = 1;
scale_factor_z = 1;
//scale_factor_x = 0.4;
//scale_factor_y = 0.4;
//scale_factor_z = 0.4;

// invert for printing?
//rotate_y = 0;       // for modelling or printing bottom
rotate_y = 180;     // for printing top

// fan holes?
fan_holes = 1;      // render fan holes
//fan_holes = 0;    // skip fan holes
//holedia_top = 10;   // larger (faster rendering) fan holes on top
holedia_top = 7;    // nicer smaller fan holes on top

// facets
$fn = 10;

// board params
wall_thickness = 2.5;
jack_buffer = 2; 			// amount of extra cutout space around each side of a jack
tooth_width = wall_thickness;
tooth_depth = 20;
tooth_height = 20;

pcb_slush_factor = 3;
pcb_width = 150 + pcb_slush_factor;
pcb_depth = 100 + pcb_slush_factor;
pcb_height = 40 + pcb_slush_factor + (1*wall_thickness);

module outline_frame ( offset = 10 ) {
    // a thick edged rect frame
    // full surface - smaller surface
    // frame thickness is 'offset'

    difference() {

        // top or bottom full size surface
        cube ( [ pcb_width + (wall_thickness*4), pcb_depth + (2*wall_thickness), wall_thickness] );
        // top or bottom smaller surface
        translate ( [ offset, offset, 0 ] ) {
            cube ( [ (pcb_width + (wall_thickness*4)) - (2*offset), (pcb_depth + (2*wall_thickness)) - (2*offset), wall_thickness] );
        }

    } // diff

} // outline frame

module frontbacktop () {

    module top() {
        union() {
			// top
			color ( "MediumSeaGreen" ) {
				translate ( [ 0, 0, pcb_height + (1*wall_thickness) ] ) {
					cube ( [ pcb_width + (wall_thickness*4), pcb_depth + (2*wall_thickness), wall_thickness] );
				}
			}
        }
    }

	module walls() {
		union() {
			// front wall
			color ( "thistle" ) {
				cube ( [ pcb_width + (wall_thickness*4), wall_thickness, pcb_height + (2*wall_thickness) ] );
			}
			// back wall
			color ( "green" ) {
				translate ( [ 0, pcb_depth + (1*wall_thickness), 0 ] ) {
					cube ( [ pcb_width + (wall_thickness*4), wall_thickness, pcb_height + (2*wall_thickness)] );
				}
			}
            // left fastener tooth
			color ( "MediumSeaGreen" ) {
				translate ( [ wall_thickness, pcb_depth / 3 * 2, pcb_height - tooth_height + (1*wall_thickness) ] ) {
					cube ( [ tooth_width, tooth_depth, tooth_height ] );
				}
			}
            // right fastener tooth
			color ( "MediumSeaGreen" ) {
				translate ( [ pcb_width + (2*wall_thickness), pcb_depth / 3 * 2, pcb_height - tooth_height + (1*wall_thickness) ] ) {
					cube ( [ tooth_width, tooth_depth, tooth_height ] );
				}
			}

        } // union
	} // walls

	module cutouts() {
		// LEFT SIDE
		//

        // left fastener small hole
        translate ( [ 0, ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=0.75,r2=0.75 );
            }
        }

		// BACK
		//

		// USB power/serial
		translate ( [ 3 - jack_buffer, pcb_depth + (1*wall_thickness), 25 - jack_buffer + (1*wall_thickness) ] ) {
			cube( [ 7 + (2*jack_buffer), 20, 4 + (2*jack_buffer) ] );
		}
		// volume potwheel
		translate ( [ 16, pcb_depth + (1*wall_thickness), 25 - jack_buffer + (1*wall_thickness) ] ) {
			cube( [ 15 + (2*jack_buffer), 20, 2 + (2*jack_buffer) ] );
		}
		// PS/2 keyb
		translate ( [ 36, pcb_depth + (1*wall_thickness), 25 - jack_buffer + (1*wall_thickness) ] ) {
			cube( [ 15, 20, 15 + (2*jack_buffer) ] );
		}
		// VGA
		translate ( [ 55, pcb_depth + (1*wall_thickness), 25 - jack_buffer + (1*wall_thickness) ] ) {
			cube( [ 31, 20, 14 + (2*jack_buffer) ] );
		}

        // TOP
        //

   		// cart
		translate ( [ 30 + (wall_thickness*2), 2, 35 - jack_buffer + (1*wall_thickness) ] ) {
			cube( [ 82 + (2*jack_buffer), 25+(2*jack_buffer), 20 ] );
		}

        // RIGHT
        //

        // right fastener small hole
        translate ( [ pcb_width + (wall_thickness*2), ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=0.75,r2=0.75 );
            }
        }

	} // cutouts

    module cart_frame() {
		translate ( [ 25 + (wall_thickness*2), 2+(1*wall_thickness), pcb_height + (1*wall_thickness) ] ) {
			cube( [ 95, 35, wall_thickness ] );
		}
    }

    module toptrim_frame() {

        // frame
        translate ( [ 80, 60, pcb_height + (1*wall_thickness) ] )  {
            //cube ( [ 60, 40, wall_thickness ] );
            cylinder ( h=wall_thickness, r1=25, r2=25 );
        } // tr

     } // top trim frame

    module toptrim_text() {

        // ZZ
        translate ( [ 63, 52 + (1*wall_thickness), 39 + (1*wall_thickness) ] )  {
             linear_extrude(slices=1,height=15) {
                text ( text="ZZ", size=20, font="DejaVu Sans Mono:style=Bold" );
             } // ex
         } // tr

     } // top trim text

    difference() {
        union() {

            // top - fanholes
            if ( fan_holes == 1 ) {
                difference() {
                    top();
                    translate( [ 75, 55, 38 + pcb_slush_factor ] ) fanholes(dia=140,holedia=holedia_top,height=10);
                } // diff
            } else {
                difference() {
                    top();
                    // approximate holes with a single big cutout
                    translate ( [ 20, 15, pcb_height ] ) {
                        cube ( [ pcb_width - 40, pcb_depth - 30, wall_thickness ] );
                    } // tr
                }
            }
            // + walls
            // + outline frame
            // + cart frame
            // + top trim frame
            walls();
            translate ( [ 0, 0, pcb_height + (1*wall_thickness) ] ) {
                outline_frame ( 12 );
            }
            cart_frame();
            toptrim_frame();
        }
        // - cutouts
        // - top trim text
        cutouts();
        toptrim_text();
    } // diff

} // front back top

module leftrightbottom () {

	module walls() {
		union() {
			// left wall
			color ( "cadetblue" ) {
                translate ( [ 0, wall_thickness, 0 ] ) {
                    cube ( [ wall_thickness, pcb_depth, pcb_height + (1*wall_thickness) ] );
                }
			}
			// right wall
			color ( "thistle" ) {
                translate ( [ pcb_width + (wall_thickness*3), wall_thickness, 0 ] ) {
                    cube ( [ wall_thickness, pcb_depth, pcb_height + (1*wall_thickness) ] );
                }
			}

        } // union
	} // walls

    module bottom() {
        union() {
			// bottom
			color ( "MediumSeaGreen" ) {
                cube ( [ pcb_width + (wall_thickness*4), pcb_depth, wall_thickness] );
			}
        }
    }

	module cutouts() {
		// LEFT SIDE
		//

		// joy0 + headphone
		translate ( [ -10, 9 - jack_buffer, 25 - jack_buffer ] ) {
			cube( [ 20, 30 + 15 + (2*jack_buffer), 13 + (2*jack_buffer) ] );
		}

        // left fastener hole
        translate ( [ 0, ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=1,r2=1 );
            }
        }

        // RIGHT SIDE
        //

        // right fastener hole
        translate ( [ pcb_width + (3*wall_thickness), ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=1,r2=1 );
            }
        }

    } // cutouts

    difference() {
        union() {
            difference() {
                bottom();
                if ( fan_holes == 1 ) {
                    translate( [ 75, 55, 0 ] ) fanholes(dia=140,holedia=10,height=20);
                } // if
            }
            walls();
            outline_frame ( 12 );
        } // union
        cutouts();
    }

} // left right bottom

// DO IT
//

rotate ( [ 0, rotate_y, 0 ] ) {
    scale ( [ scale_factor_x, scale_factor_y, scale_factor_z ] ) {
        if ( target == "frontbacktop" ) {
            frontbacktop();
        } else if ( target == "leftrightbottom" ) {
            leftrightbottom();
        } else if ( target == "all" ) {
            frontbacktop();
            leftrightbottom();
        }
    }
}

/* Case parts for Zikzak rev3
 * www.zikzak.ca
 */
 use <_fanholes.scad>;
 use <_assert.scad>;

 // ref
// colours: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations#color

// TODO:
// - FIX: INVERT
// - VERIFY: HEIGHT positions to actually be right for jacks, relative to height padding
// - FIX: so it actually fits together; sloppyness ratio? or just use scaling?
// - add cart hole 'sides' as guides for the cart to go down nicely into edge connector
// - 2 fastener bits per side instead of 1 ... problem is jacks

// render target
// - frontbacktop
// - leftrightbottom
// - all
//target = "frontbacktop";
//target = "leftrightbottom";
target = "all";

// scale? Use this to print out tiny versions for inspection prior to printing real ones
//scale_factor_x = 1;
//scale_factor_y = 1;
//scale_factor_z = 1;
scale_factor_x = 0.4;
scale_factor_y = 0.4;
scale_factor_z = 0.4;

// invert for printing?
invert_y = 1;

// fan holes?
fan_holes = 1;      // render fan holes
//fan_holes = 0;    // skip fan holes
holedia_top = 10;   // larger (faster rendering) fan holes on top
//holedia_top = 5;    // nicer smaller fan holes on top

// facets
$fn = 50;

// board params
wall_thickness = 2.5;
jack_buffer = 2; 			// amount of extra cutout space around each side of a jack
tooth_width = wall_thickness;
tooth_depth = 20;
tooth_height = 20;

pcb_width = 150;
pcb_depth = 100;
pcb_height = 40 + (1*wall_thickness);

module outline_frame ( offset = 10 ) {
    // a thick edged rect frame
    // full surface - smaller surface
    // frame thickness is 'offset'

    difference() {

        // top or bottom full size surface
        cube ( [ pcb_width + (wall_thickness*4), pcb_depth + (2*wall_thickness), wall_thickness] );
        // top or bottom smaller surface
        translate ( [ offset, offset, 0 ] ) {
            cube ( [ (pcb_width + (wall_thickness*4)) - (2*offset), (pcb_depth + (2*wall_thickness)) - (2*offset), wall_thickness] );
        }

    } // diff

} // outline frame

module frontbacktop () {

    module top() {
        union() {
			// top
			color ( "MediumSeaGreen" ) {
				translate ( [ 0, 0, pcb_height ] ) {
					cube ( [ pcb_width + (wall_thickness*4), pcb_depth + (2*wall_thickness), wall_thickness] );
				}
			}
        }
    }

	module walls() {
		union() {
			// front wall
			color ( "thistle" ) {
				cube ( [ pcb_width + (wall_thickness*4), wall_thickness, pcb_height ] );
			}
			// back wall
			color ( "green" ) {
				translate ( [ 0, pcb_depth + (1*wall_thickness), 0 ] ) {
					cube ( [ pcb_width + (wall_thickness*4), wall_thickness, pcb_height ] );
				}
			}
            // left fastener tooth
			color ( "MediumSeaGreen" ) {
				translate ( [ wall_thickness, pcb_depth / 3 * 2, pcb_height - tooth_height ] ) {
					cube ( [ tooth_width, tooth_depth, tooth_height ] );
				}
			}
            // right fastener tooth
			color ( "MediumSeaGreen" ) {
				translate ( [ pcb_width + (2*wall_thickness), pcb_depth / 3 * 2, pcb_height - tooth_height ] ) {
					cube ( [ tooth_width, tooth_depth, tooth_height ] );
				}
			}

        } // union
	} // walls

	module cutouts() {
		// LEFT SIDE
		//

        // left fastener small hole
        translate ( [ 0, ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=1,r2=1 );
            }
        }

		// BACK
		//

		// USB power/serial
		translate ( [ 3 - jack_buffer, pcb_depth + (1*wall_thickness), 25 - jack_buffer ] ) {
			cube( [ 7 + (2*jack_buffer), 20, 4 + (2*jack_buffer) ] );
		}
		// volume potwheel
		translate ( [ 16, pcb_depth + (1*wall_thickness), 25 - jack_buffer ] ) {
			cube( [ 15 + (2*jack_buffer), 20, 2 + (2*jack_buffer) ] );
		}
		// PS/2 keyb
		translate ( [ 36, pcb_depth + (1*wall_thickness), 25 - jack_buffer ] ) {
			cube( [ 15, 20, 15 + (2*jack_buffer) ] );
		}
		// VGA
		translate ( [ 55, pcb_depth + (1*wall_thickness), 25 - jack_buffer ] ) {
			cube( [ 31, 20, 14 + (2*jack_buffer) ] );
		}

        // TOP
        //

   		// cart
		translate ( [ 30 + (wall_thickness*2), 2, 35 - jack_buffer ] ) {
			cube( [ 82 + (2*jack_buffer), 25+(2*jack_buffer), 20 ] );
		}

        // RIGHT
        //

        // right fastener small hole
        translate ( [ pcb_width + (wall_thickness*2), ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=1,r2=1 );
            }
        }

	} // cutouts

    module cart_frame() {
		translate ( [ 25 + (wall_thickness*2), 2+(1*wall_thickness), pcb_height ] ) {
			cube( [ 95, 35, wall_thickness ] );
		}
    }

    module toptrim_frame() {

        // frame
        translate ( [ 80, 60, pcb_height ] )  {
            //cube ( [ 60, 40, wall_thickness ] );
            cylinder ( h=wall_thickness, r1=25, r2=25 );
        } // tr

     } // top trim frame

    module toptrim_text() {

        // ZZ
        translate ( [ 63, 52 + (1*wall_thickness), 39 ] )  {
             linear_extrude(slices=1,height=15) {
                text ( text="ZZ", size=20, font="DejaVu Sans Mono:style=Bold" );
             } // ex
         } // tr

     } // top trim text

    difference() {
        union() {

            // top - fanholes
            if ( fan_holes == 1 ) {
                difference() {
                    top();
                    translate( [ 75, 55, 38 ] ) fanholes(dia=140,holedia=holedia_top,height=10);
                } // diff
            } else {
                difference() {
                    top();
                    // approximate holes with a single big cutout
                    translate ( [ 20, 15, pcb_height ] ) {
                        cube ( [ pcb_width - 40, pcb_depth - 30, wall_thickness ] );
                    } // tr
                }
            }
            // + walls
            // + outline frame
            // + cart frame
            // + top trim frame
            walls();
            translate ( [ 0, 0, pcb_height ] ) {
                outline_frame ( 12 );
            }
            cart_frame();
            toptrim_frame();
        }
        // - cutouts
        // - top trim text
        cutouts();
        toptrim_text();
    } // diff

} // front back top

module leftrightbottom () {

	module walls() {
		union() {
			// left wall
			color ( "cadetblue" ) {
                translate ( [ 0, wall_thickness, 0 ] ) {
                    cube ( [ wall_thickness, pcb_depth, pcb_height ] );
                }
			}
			// right wall
			color ( "thistle" ) {
                translate ( [ pcb_width + (wall_thickness*3), wall_thickness, 0 ] ) {
                    cube ( [ wall_thickness, pcb_depth, pcb_height ] );
                }
			}

        } // union
	} // walls

    module bottom() {
        union() {
			// bottom
			color ( "MediumSeaGreen" ) {
                cube ( [ pcb_width + (wall_thickness*4), pcb_depth, wall_thickness] );
			}
        }
    }

	module cutouts() {
		// LEFT SIDE
		//

		// joy0 + headphone
		translate ( [ -10, 9 - jack_buffer, 25 - jack_buffer ] ) {
			cube( [ 20, 30 + 15 + (2*jack_buffer), 13 + (2*jack_buffer) ] );
		}

        // left fastener hole
        translate ( [ 0, ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=2,r2=2 );
            }
        }

        // RIGHT SIDE
        //

        // right fastener hole
        translate ( [ pcb_width + (3*wall_thickness), ( pcb_depth / 3 * 2 ) + ( tooth_depth /2 ), pcb_height - ( tooth_height / 2 ) ] ) {
            rotate ( [ 0, 90, 0 ] ) {
                cylinder ( h=wall_thickness*2,r1=2,r2=2 );
            }
        }

    } // cutouts

    difference() {
        union() {
            difference() {
                bottom();
                if ( fan_holes == 1 ) {
                    translate( [ 75, 55, 0 ] ) fanholes(dia=140,holedia=10,height=20);
                } // if
            }
            walls();
            outline_frame ( 12 );
        } // union
        cutouts();
    }

} // left right bottom

// DO IT
//

rotate_y = 0;

if ( invert_y == 1 ) {
    //assert ( 1==2, "Invert not coded yet" );
    rotate_y = 180;
}
echo ("Rotate Y ", rotate_y );

rotate ( [ 0, rotate_y, 0 ] ) {
    scale ( [ scale_factor_x, scale_factor_y, scale_factor_z ] ) {
        if ( target == "frontbacktop" ) {
            frontbacktop();
        } else if ( target == "leftrightbottom" ) {
            leftrightbottom();
        } else if ( target == "all" ) {
            frontbacktop();
            leftrightbottom();
        }
    }
}