Update

Update

FFF=[false,false,false];FFT=[false,false,true];
FTF=[false,true,false];FTT=[false,true,true];
TFF=[true,false,false];TFT=[true,false,true];
TTF=[true,true,false];TTT=[true,true,true];
module cube2(size, center) {
  translate([
    center[0] ? -size[0]/2 : 0,
    center[1] ? -size[1]/2 : 0,
    center[2] ? -size[2]/2 : 0]) cube(size);
}
module mirror_x() {
  for(a=[0,1])mirror([a,0,0])children();
}
module mirror_y() {
  for(a=[0,1])mirror([0,a,0])children();
}
module mirror_z() {
  for(a=[0,1])mirror([0,0,a])children();
}

module switch_bracket() {
    dist_x=141/2;
    hook_x=34;
    points1=[[dist_x,hook_x],[dist_x,hook_x+47.5],[-dist_x,hook_x],[-dist_x,hook_x+47.5]];
    holes1=[[dist_x,hook_x],[-dist_x,hook_x+36.8],[-dist_x,hook_x],[dist_x,hook_x+47.5]];
    holes2=[[50,0],[0,0],[-50,0]];
    points=concat(points1,holes2);
    module joint(array, i1, i2) {
        hull() {
            translate(array[i1]) cylinder(d=12,h=4);
            translate(array[i2]) cylinder(d=12,h=4);
        }
    }
    difference() {
        union() {
            joint(points, 0, 1);
            joint(points, 1, 3);
            joint(points, 3, 2);
            joint(points, 2, 6);
            joint(points, 6, 5);
            joint(points, 5, 4);
            joint(points, 4, 0);
        }

        for(xy=holes2)translate(xy)cylinder(d=3.2,h=20,center=true,$fn=24);
        for(xy=holes1)translate(xy) {
            cylinder(d=3.2,h=20,center=true,$fn=24);
            translate([0,0,-0.01])cylinder(d1=6.2,d2=3.2,h=3,$fn=24);
        }
    }
}

module realsense_bracket() {
    distance_x=62.25/2;
    difference() {
        union() {
            cube2([72,6,50-6],TFF);
            mirror_x()translate([25,-12.5])cube2([12,13,25-6-2],TFF);

            mirror_x()translate([distance_x,0])cube2([12,35,5],TFF);
            mirror_x()translate([distance_x+6,0])hull() {
                cube2([3,35,5],TFF);
                cube2([3,6,50-6],TFF);
            }
        }
        mirror_x()translate([50/2,0,12.5-6]) {
            rotate([90,0])cylinder(d=3.2,h=100,center=true,$fn=24);
            translate([0,25-12.5+6])rotate([90,0])cylinder(d=6,h=25,$fn=36);
        }
        mirror_x()translate([45/2,0,25+12.5-6]) {
            rotate([90,0])cylinder(d=3.2,h=100,center=true,$fn=24);
        }
        mirror_x() for(a=[10,20,30])translate([distance_x,a])cylinder(d=3.2,h=30,$fn=24,center=true);
    }
}

switch_bracket();
//realsense_bracket();

FFF=[false,false,false];FFT=[false,false,true];
FTF=[false,true,false];FTT=[false,true,true];
TFF=[true,false,false];TFT=[true,false,true];
TTF=[true,true,false];TTT=[true,true,true];
module cube2(size, center) {
  translate([
    center[0] ? -size[0]/2 : 0,
    center[1] ? -size[1]/2 : 0,
    center[2] ? -size[2]/2 : 0]) cube(size);
}
module mirror_x() {
  for(a=[0,1])mirror([a,0,0])children();
}
module mirror_y() {
  for(a=[0,1])mirror([0,a,0])children();
}
module mirror_z() {
  for(a=[0,1])mirror([0,0,a])children();
}

module switch_bracket() {
    points1=[[70,35],[70,35+48],[-70,35],[-70,35+48]];
    holes1=[[70,35],[70,35+37],[-70,35],[-70,35+48]];
    holes2=[[50,0],[0,0],[-50,0]];
    points=concat(points1,holes2);
    module joint(array, i1, i2) {
        hull() {
            translate(array[i1]) cylinder(d=12,h=4);
            translate(array[i2]) cylinder(d=12,h=4);
        }
    }
    difference() {
        union() {
            joint(points, 0, 1);
            joint(points, 1, 3);
            joint(points, 3, 2);
            joint(points, 2, 6);
            joint(points, 6, 5);
            joint(points, 5, 4);
            joint(points, 4, 0);
        }

        for(xy=holes2)translate(xy)cylinder(d=3.2,h=20,center=true,$fn=24);
        for(xy=holes1)translate(xy) {
            cylinder(d=3.2,h=20,center=true,$fn=24);
            translate([0,0,-0.01])cylinder(d1=6.2,d2=3.2,h=3,$fn=24);
        }
    }
}

module realsense_bracket() {
    distance_x=62.25/2;
    difference() {
        union() {
            cube2([72,6,50-6],TFF);
            mirror_x()translate([25,-12.5])cube2([12,13,25-6-2],TFF);

            mirror_x()translate([distance_x,0])cube2([12,35,5],TFF);
            mirror_x()translate([distance_x+6,0])hull() {
                cube2([3,35,5],TFF);
                cube2([3,6,50-6],TFF);
            }
        }
        mirror_x()translate([50/2,0,12.5-6]) {
            rotate([90,0])cylinder(d=3.2,h=100,center=true,$fn=24);
            translate([0,25-12.5+6])rotate([90,0])cylinder(d=6,h=25,$fn=36);
        }
        mirror_x()translate([45/2,0,25+12.5-6]) {
            rotate([90,0])cylinder(d=3.2,h=100,center=true,$fn=24);
        }
        mirror_x() for(a=[10,20,30])translate([distance_x,a])cylinder(d=3.2,h=30,$fn=24,center=true);
    }
}

//switch_bracket();
realsense_bracket();


removed dead code

removed dead code

eps = 0.01;
d_rod = 8+0.5;
d_hand = 20;
h_hand = 110;

// standard ball bearing: 8x22x7mm
module bearing()
{
  difference()
  {
    cylinder(d=22, h=7, $fn=100);
    translate(eps*[0,0,-1])
      cylinder(d=8, h=7+2*eps, $fn=40);
  }
}

module bearing_mount()
{
  $fn=90;
  wall = 5;
  d_ext = 22+2*wall;

  // bottom bearing hold
  difference()
  {
    cylinder(d=d_ext, h=7);
    translate([0,0,-eps])
      cylinder(d=22+0.5, h=7+2*eps, $fn=180);
  }
  %bearing();

  // connecting element
  translate([0, 0, 7])
    difference()
    {
      cylinder(d1=d_ext, d2=d_hand, h=(d_ext-d_hand)/2);
      cylinder(d1=d_ext-2*wall+0.5, d2=d_rod, h=(d_ext-d_hand)/2);
    }
}

module handle()
{
  h_bm = 13;
  // bottom bearing mount
  bearing_mount();
  translate([0, 0, h_bm])
  {
    // center rod
    difference()
    {
      cylinder(d=d_hand, h=h_hand, $fn=100);
      cylinder(d=d_rod, h=h_hand, $fn=100);
    }
    // top bearing mount
    translate([0, 0, h_hand])
      translate([0, 0, h_bm])
        mirror([0,0,1])
          bearing_mount();
  }
}

for(i=[0:1])
  translate(i*[35,0,0])
    handle();

use <m3d/screw_head_hex.scad>

eps = 0.01;
d_rod = 8+0.5;
d_hand = 20;
h_hand = 110;

// standard ball bearing: 8x22x7mm
module bearing()
{
  difference()
  {
    cylinder(d=22, h=7, $fn=100);
    translate(eps*[0,0,-1])
      cylinder(d=8, h=7+2*eps, $fn=40);
  }
}

module screw_head() { screw_head_hex_m8(); }

module washer(dh=0)
{
  difference()
  {
    cylinder(d=15.5, h=1.4+dh);
    translate(eps*[0,0,-1])
      cylinder(d=8.5, h=1.4+dh+2*eps);
  }
}

// slot for ball bearing + 2 washers
module bearing_slot()
{
  d_spacing = 0.5;
  h_spacing = 0.2;
  screw_head_hex_m8(d_spacing, h_spacing);

  for(dh=[-1.4, 7+h_spacing])
    translate([0, 0, dh])
      washer();
}

module bearing_mount()
{
  $fn=90;
  wall = 5;
  d_ext = 22+2*wall;

  // bottom bearing hold
  difference()
  {
    cylinder(d=d_ext, h=7);
    translate([0,0,-eps])
      cylinder(d=22+0.5, h=7+2*eps, $fn=180);
  }
  %bearing();

  translate([0, 0, 7])
  {
    // connecting element
    difference()
    {
      cylinder(d1=d_ext, d2=d_hand, h=(d_ext-d_hand)/2);
      cylinder(d1=d_ext-2*wall+0.5, d2=d_rod, h=(d_ext-d_hand)/2);
    }

    // main e
  }
}

module handle()
{
  h_bm = 13;
  // bottom bearing mount
  bearing_mount();
  translate([0, 0, h_bm])
  {
    // center rod
    difference()
    {
      cylinder(d=d_hand, h=h_hand, $fn=100);
      cylinder(d=d_rod, h=h_hand, $fn=100);
    }
    // top bearing mount
    translate([0, 0, h_hand])
      translate([0, 0, h_bm])
        mirror([0,0,1])
          bearing_mount();
  }
}

for(i=[0:1])
  translate(i*[35,0,0])
    handle();


We don't love GPLv3 because it is not fair!

// helper module for drawing rectangles with rounded borders
//
// (c) 2016 Kees Jongenburger: Licence of this file only applies
// to this file and does not infect other parts of the design.
// (c) 2013 Felipe C. da S. Sanches <fsanches@metamaquina.com.br>
// Lincensed under the terms of the GNU General Public License
// version 3 (or later).
//https://raw.githubusercontent.com/Metamaquina/Metamaquina2Beta/master/rounded_square.scad

module rounded_square(dim, corners=[10,10,10,10], center=false){
  w=dim[0];
  h=dim[1];

  if (center){
    translate([-w/2, -h/2])
    rounded_square_(dim, corners=corners);
  }else{
    rounded_square_(dim, corners=corners);
  }
}

module rounded_square_(dim, corners, center=false){
  w=dim[0];
  h=dim[1];
  render(){
    difference(){
      square([w,h]);

      if (corners[0])
        square([corners[0], corners[0]]);

      if (corners[1])
        translate([w-corners[1],0])
        square([corners[1], corners[1]]);

      if (corners[2])
        translate([0,h-corners[2]])
        square([corners[2], corners[2]]);

      if (corners[3])
        translate([w-corners[3], h-corners[3]])
        square([corners[3], corners[3]]);
    }

    if (corners[0])
      translate([corners[0], corners[0]])
      intersection(){
        circle(r=corners[0]);
        translate([-corners[0], -corners[0]])
        square([corners[0], corners[0]]);
      }

    if (corners[1])
      translate([w-corners[1], corners[1]])
      intersection(){
        circle(r=corners[1]);
        translate([0, -corners[1]])
        square([corners[1], corners[1]]);
      }

    if (corners[2])
      translate([corners[2], h-corners[2]])
      intersection(){
        circle(r=corners[2]);
        translate([-corners[2], 0])
        square([corners[2], corners[2]]);
      }

    if (corners[3])
      translate([w-corners[3], h-corners[3]])
      intersection(){
        circle(r=corners[3]);
        square([corners[3], corners[3]]);
      }
  }
}

// helper module for drawing rectangles with rounded borders
//
// (c) 2013 Felipe C. da S. Sanches <fsanches@metamaquina.com.br>
// Lincensed under the terms of the GNU General Public License
// version 3 (or later).
//https://raw.githubusercontent.com/Metamaquina/Metamaquina2Beta/master/rounded_square.scad

module rounded_square(dim, corners=[10,10,10,10], center=false){
  w=dim[0];
  h=dim[1];

  if (center){
    translate([-w/2, -h/2])
    rounded_square_(dim, corners=corners);
  }else{
    rounded_square_(dim, corners=corners);
  }
}

module rounded_square_(dim, corners, center=false){
  w=dim[0];
  h=dim[1];
  render(){
    difference(){
      square([w,h]);

      if (corners[0])
        square([corners[0], corners[0]]);

      if (corners[1])
        translate([w-corners[1],0])
        square([corners[1], corners[1]]);

      if (corners[2])
        translate([0,h-corners[2]])
        square([corners[2], corners[2]]);

      if (corners[3])
        translate([w-corners[3], h-corners[3]])
        square([corners[3], corners[3]]);
    }

    if (corners[0])
      translate([corners[0], corners[0]])
      intersection(){
        circle(r=corners[0]);
        translate([-corners[0], -corners[0]])
        square([corners[0], corners[0]]);
      }

    if (corners[1])
      translate([w-corners[1], corners[1]])
      intersection(){
        circle(r=corners[1]);
        translate([0, -corners[1]])
        square([corners[1], corners[1]]);
      }

    if (corners[2])
      translate([corners[2], h-corners[2]])
      intersection(){
        circle(r=corners[2]);
        translate([-corners[2], 0])
        square([corners[2], corners[2]]);
      }

    if (corners[3])
      translate([w-corners[3], h-corners[3]])
      intersection(){
        circle(r=corners[3]);
        square([corners[3], corners[3]]);
      }
  }
}


xTubeConnecter

xTubeConnecter

V0.1

//Parameters
$fn=50;
dOne = 26.4;
dTwo = 30; // ?? Messen
hOne =10;
hTwo =20;
wall =1.5;
slot = 3;
screwHoleDiameter = 3;
screwNutRadius = screwHoleDiameter * 0.9;

module connector(d=20,h=10) {
    difference() {
        cylinder(r=d/2+wall,h=h,center=true);
        cylinder(r=d/2,h=h,center=true);
        translate([d/2,0])cube([d/2*1.1,slot,h],center=true);
    }

    translate([d/2+1.5*wall+screwHoleDiameter/2,slot])screwHold(a=2.5*wall+screwHoleDiameter,b=slot,c=h);
    translate([d/2+1.5*wall+screwHoleDiameter/2,-slot])screwHoldNut(a=2.5*wall+screwHoleDiameter,b=slot,c=h);
}
module screwHold(a,b,c) {
       difference() {
           cube([a,b,c],center=true);
           rotate([90])cylinder(r=screwHoleDiameter/2,h=c,center=true);
       }
}
module screwHoldNut(a,b,c) {
       difference() {
           cube([a,b,c],center=true);
           rotate([90])translate([0,0,-b])cylinder(r=screwHoleDiameter/2,h=c);
           rotate([90])translate([0,0,0])cylinder(r=screwNutRadius,h=b,$fn=6);
       }
}
module assembly() {
    connector(d=dOne,h=hOne);
    translate([-dOne-3*wall,0])rotate([90,0,180])connector(d=dTwo,h=hTwo);
}
module hullCut() {
    intersection() {
        assembly();
        translate([-dOne/2-wall/2,l,0])cube([wall,hTwo,hOne],center=true);
    }
}
assembly();
hull()hullCut();

error: pathspec 'xTubeConnector.scad' did not match any file(s) known to git


refactor: rewrite the repeat operators, using direct loops instead of nesting operators and mutiplying the translates

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2017-2022 Jean-Sebastien CONAN
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * Part of the camelSCAD library.
 *
 * Operators that repeat children modules with respect to particular rules.
 *
 * @package operator/repeat
 * @author jsconan
 */

/**
 * Repeats the children modules `count` times with the provided `interval`.
 *
 * @param Number [count] - The number of times the children must be repeated.
 * @param Vector [interval] - The interval between each repeated children.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 * @param Number [intervalX] - The X interval between each repeated children
 *                             (will overwrite the X coordinate in the `interval` vector).
 * @param Number [intervalY] - The Y interval between each repeated children
 *                             (will overwrite the Y coordinate in the `interval` vector).
 * @param Number [intervalZ] - The Z interval between each repeated children
 *                             (will overwrite the Z coordinate in the `interval` vector).
 */
module repeat(count    = 2,
              interval = 0,
              center   = false,
              intervalX, intervalY, intervalZ) {

    interval = apply3D(interval, intervalX, intervalY, intervalZ);
    offset = center ? -interval * (count - 1) / 2 : ORIGIN_3D;

    for (i = [0 : count - 1]) {
        translate(offset + interval * i) {
            children();
        }
    }
}

/**
 * Repeats the children modules in two directions `count` times with the provided `interval`.
 *
 * @param Number [countX] - The number of times the children must be repeated along the X axis.
 * @param Number [countY] - The number of times the children must be repeated along the Y axis.
 * @param Vector [intervalX] - The interval between each repeated children along the X axis.
 * @param Vector [intervalY] - The interval between each repeated children along the Y axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeat2D(countX    = 2,
                countY    = 2,
                intervalX = 0,
                intervalY = 0,
                center    = false) {

    intervalX = vector3D(intervalX);
    intervalY = vector3D(intervalY);
    offsetX = center ? -intervalX * (countX - 1) / 2 : ORIGIN_3D;
    offsetY = center ? -intervalY * (countY - 1) / 2 : ORIGIN_3D;
    offset = offsetX + offsetY;

    for (y = [0 : countY - 1] ) {
        for (x = [0 : countX - 1] ) {
            translate(offset + intervalX * x + intervalY * y) {
                children();
            }
        }
    }
}

/**
 * Repeats the children modules in three directions `count` times with the provided `interval`.
 *
 * @param Number [countX] - The number of times the children must be repeated along the X axis.
 * @param Number [countY] - The number of times the children must be repeated along the Y axis.
 * @param Number [countZ] - The number of times the children must be repeated along the Z axis.
 * @param Vector [intervalX] - The interval between each repeated children along the X axis.
 * @param Vector [intervalY] - The interval between each repeated children along the Y axis.
 * @param Vector [intervalZ] - The interval between each repeated children along the Z axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeat3D(countX    = 2,
                countY    = 2,
                countZ    = 2,
                intervalX = 0,
                intervalY = 0,
                intervalZ = 0,
                center    = false) {

    intervalX = vector3D(intervalX);
    intervalY = vector3D(intervalY);
    intervalZ = vector3D(intervalZ);
    offsetX = center ? -intervalX * (countX - 1) / 2 : ORIGIN_3D;
    offsetY = center ? -intervalY * (countY - 1) / 2 : ORIGIN_3D;
    offsetZ = center ? -intervalZ * (countZ - 1) / 2 : ORIGIN_3D;
    offset = offsetX + offsetY + offsetZ;

    for (z = [0 : countZ - 1] ) {
        for (y = [0 : countY - 1] ) {
            for (x = [0 : countX - 1] ) {
                translate(offset + intervalX * x + intervalY * y + intervalZ * z) {
                    children();
                }
            }
        }
    }
}

/**
 * Repeats horizontally a shape in two directions, the interval is set by the size of the shape.
 * @param Vector size - The size of the shape.
 * @param Vector [count] - The number of shapes on each axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeatShape2D(size, count = 1, center) {
    size = vector2D(size);
    count = vector2D(count);

    repeat2D(
        countX = count.x,
        countY = count.y,
        intervalX = xAxis3D(size.x),
        intervalY = yAxis3D(size.y),
        center = center
    ) {
        children();
    }
}

/**
 * Repeats a shape in three directions, the interval is set by the size of the shape.
 * @param Vector size - The size of the shape.
 * @param Vector [count] - The number of shapes on each axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeatShape3D(size, count = 1, center) {
    size = vector3D(size);
    count = vector3D(count);

    repeat3D(
        countX = count.x,
        countY = count.y,
        countZ = count.z,
        intervalX = xAxis3D(size.x),
        intervalY = yAxis3D(size.y),
        intervalZ = zAxis3D(size.z),
        center = center
    ) {
        children();
    }
}

/**
 * Repeats the children modules on every position given in the `map`.
 *
 * @param Vector[] map - The list of position at which place the children.
 * @param Vector [offset] - An offset to add on each position.
 * @param Number [x] - The X-coordinate to apply on the offset.
 * @param Number [y] - The Y-coordinate to apply on the offset.
 * @param Number [z] - The Z-coordinate to apply on the offset.
 */
module repeatMap(map, offset, x, y, z) {
    offset = apply3D(offset, x, y, z);

    for (at = map) {
        translate(offset + vector3D(at)) {
            children();
        }
    }
}

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2017-2022 Jean-Sebastien CONAN
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * Part of the camelSCAD library.
 *
 * Operators that repeat children modules with respect to particular rules.
 *
 * @package operator/repeat
 * @author jsconan
 */

/**
 * Repeats the children modules `count` times with the provided `interval`.
 *
 * @param Number [count] - The number of times the children must be repeated.
 * @param Vector [interval] - The interval between each repeated children.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 * @param Number [intervalX] - The X interval between each repeated children
 *                             (will overwrite the X coordinate in the `interval` vector).
 * @param Number [intervalY] - The Y interval between each repeated children
 *                             (will overwrite the Y coordinate in the `interval` vector).
 * @param Number [intervalZ] - The Z interval between each repeated children
 *                             (will overwrite the Z coordinate in the `interval` vector).
 */
module repeat(count    = 2,
              interval = 0,
              center   = false,
              intervalX, intervalY, intervalZ) {

    interval = apply3D(interval, intervalX, intervalY, intervalZ);
    offset = center ? -interval * (count - 1) / 2 : ORIGIN_3D;

    for (i = [0 : count - 1]) {
        translate(offset + interval * i) {
            children();
        }
    }
}

/**
 * Repeats the children modules in two directions `count` times with the provided `interval`.
 *
 * @param Number [countX] - The number of times the children must be repeated along the X axis.
 * @param Number [countY] - The number of times the children must be repeated along the Y axis.
 * @param Vector [intervalX] - The interval between each repeated children along the X axis.
 * @param Vector [intervalY] - The interval between each repeated children along the Y axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeat2D(countX    = 2,
                countY    = 2,
                intervalX = 0,
                intervalY = 0,
                center    = false) {

    repeat(count=countY, interval=vector3D(intervalY), center=center) {
        repeat(count=countX, interval=vector3D(intervalX), center=center) {
            children();
        }
    }
}

/**
 * Repeats the children modules in three directions `count` times with the provided `interval`.
 *
 * @param Number [countX] - The number of times the children must be repeated along the X axis.
 * @param Number [countY] - The number of times the children must be repeated along the Y axis.
 * @param Number [countZ] - The number of times the children must be repeated along the Z axis.
 * @param Vector [intervalX] - The interval between each repeated children along the X axis.
 * @param Vector [intervalY] - The interval between each repeated children along the Y axis.
 * @param Vector [intervalZ] - The interval between each repeated children along the Z axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeat3D(countX    = 2,
                countY    = 2,
                countZ    = 2,
                intervalX = 0,
                intervalY = 0,
                intervalZ = 0,
                center    = false) {

    repeat(count=countZ, interval=vector3D(intervalZ), center=center) {
        repeat(count=countY, interval=vector3D(intervalY), center=center) {
            repeat(count=countX, interval=vector3D(intervalX), center=center) {
                children();
            }
        }
    }
}

/**
 * Repeats horizontally a shape in two directions, the interval is set by the size of the shape.
 * @param Vector size - The size of the shape.
 * @param Vector [count] - The number of shapes on each axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeatShape2D(size, count = 1, center) {
    size = vector2D(size);
    count = vector2D(count);

    repeat2D(
        countX = count.x,
        countY = count.y,
        intervalX = [size.x, 0, 0],
        intervalY = [0, size.y, 0],
        center = center
    ) {
        children();
    }
}

/**
 * Repeats a shape in three directions, the interval is set by the size of the shape.
 * @param Vector size - The size of the shape.
 * @param Vector [count] - The number of shapes on each axis.
 * @param Boolean [center] - Whether or not center the repeated shapes.
 */
module repeatShape3D(size, count = 1, center) {
    size = vector3D(size);
    count = vector3D(count);

    repeat3D(
        countX = count.x,
        countY = count.y,
        countZ = count.z,
        intervalX = [size.x, 0, 0],
        intervalY = [0, size.y, 0],
        intervalZ = [0, 0, size.z],
        center = center
    ) {
        children();
    }
}

/**
 * Repeats the children modules on every position given in the `map`.
 *
 * @param Vector[] map - The list of position at which place the children.
 * @param Vector [offset] - An offset to add on each position.
 * @param Number [x] - The X-coordinate to apply on the offset.
 * @param Number [y] - The Y-coordinate to apply on the offset.
 * @param Number [z] - The Z-coordinate to apply on the offset.
 */
module repeatMap(map, offset, x, y, z) {
    offset = apply3D(offset, x, y, z);

    for (at = map) {
        translate(offset + vector3D(at)) {
            children();
        }
    }
}


bearing: use fallback func

bearing: use fallback func

use <transforms.scad>;
use <shapes.scad>
include <bearing_data.scad>
include <units.scad>
include <misc.scad>

module bearing(bearing_type, extra_h=0, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    h = fallback(override_h, bearing_type[2]) + extra_h;
    size_align(size=[bearing_type[1],bearing_type[1],h], align=align ,orient=orient)
    difference()
    {
        // outer
        cylindera(h=h, d=bearing_type[1], align=[0,0,0]);
        // inner
        cylindera(h=h+.1, d=bearing_type[0], align=[0,0,0]);
        // clips
        if(len(bearing_type) > 3)
        {
            clip_depth=.5;
            for(z=[-1,1])
            translate([0,0,z*bearing_type[3]/2])
            difference()
            {
                cylindera(h=1*mm, d=bearing_type[2]+1, align=[0,0,-z]);
                cylindera(h=1*mm+.1, d=bearing_type[4]-clip_depth, align=[0,0,-z]);
            }

        }
    }
}

module bearing_mount_holes(bearing_type, ziptie_type=[2*mm, 3*mm], ziptie_bearing_distance=3*mm, tolerance=1.01, orient=[1,0,0], ziptie_dist=undef, with_zips=true, show_zips=false)
{
    ziptie_thickness = ziptie_type[0];
    ziptie_width = ziptie_type[1]+0.6*mm;

    ziptie_dist_ = fallback(ziptie_dist, bearing_type[3]/2);

    orient(orient)
    {
        // Main bearing cut
        cylindera(h=bearing_type[2]*tolerance, d=bearing_type[1]*tolerance, orient=[0,0,1]);

        if(with_zips)
        {
            for(z=[-1,1])
                translate([0,0,z*ziptie_dist_ - z*1/2])
                    hollow_cylinder(
                            d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                            thickness = ziptie_thickness*2,
                            h = ziptie_width,
                            taper=true,
                            orient=[0,0,1],
                            align=[0,0,0]
                            );
        }

        // for linear rod
        cylindera(d=bearing_type[0]+1*mm, h=100, orient=[0,0,1]);

        if(show_zips)
        {
            for(z=[-1,1])
            translate([0,0,z*ziptie_dist_])
            {
                %hollow_cylinder(
                        d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                        thickness = ziptie_thickness,
                        h = ziptie_width,
                        orient=[0,0,1],
                        align=[0,0,-z]
                        );
            }
        }
    }
}

if(false)
{
    b=bearing_igus_rj4jp_01_12;
    bearing_mount_holes(b, orient=[0,0,1]);
    translate([30,0,0])
    bearing(b, orient=[0,0,1]);
}

use <transforms.scad>;
use <shapes.scad>
include <bearing_data.scad>
include <units.scad>

module bearing(bearing_type, extra_h=0, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    h = (override_h==undef ? bearing_type[2] : override_h) + extra_h;
    size_align(size=[bearing_type[1],bearing_type[1],h], align=align ,orient=orient)
    difference()
    {
        // outer
        cylindera(h=h, d=bearing_type[1], align=[0,0,0]);
        // inner
        cylindera(h=h+.1, d=bearing_type[0], align=[0,0,0]);
        // clips
        if(len(bearing_type) > 3)
        {
            clip_depth=.5;
            for(z=[-1,1])
            translate([0,0,z*bearing_type[3]/2])
            difference()
            {
                cylindera(h=1*mm, d=bearing_type[2]+1, align=[0,0,-z]);
                cylindera(h=1*mm+.1, d=bearing_type[4]-clip_depth, align=[0,0,-z]);
            }

        }
    }
}

module bearing_mount_holes(bearing_type, ziptie_type=[2*mm, 3*mm], ziptie_bearing_distance=3*mm, tolerance=1.01, orient=[1,0,0], ziptie_dist=undef, with_zips=true, show_zips=false)
{
    ziptie_thickness = ziptie_type[0];
    ziptie_width = ziptie_type[1]+0.6*mm;

    ziptie_dist_ = (ziptie_dist==undef?bearing_type[3]/2:ziptie_dist);

    orient(orient)
    {
        // Main bearing cut
        cylindera(h=bearing_type[2]*tolerance, d=bearing_type[1]*tolerance, orient=[0,0,1]);

        if(with_zips)
        {
            for(z=[-1,1])
                translate([0,0,z*ziptie_dist_ - z*1/2])
                    hollow_cylinder(
                            d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                            thickness = ziptie_thickness*2,
                            h = ziptie_width,
                            taper=true,
                            orient=[0,0,1],
                            align=[0,0,0]
                            );
        }

        // for linear rod
        cylindera(d=bearing_type[0]+1*mm, h=100, orient=[0,0,1]);

        if(show_zips)
        {
            for(z=[-1,1])
            translate([0,0,z*ziptie_dist_])
            {
                %hollow_cylinder(
                        d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                        thickness = ziptie_thickness,
                        h = ziptie_width,
                        orient=[0,0,1],
                        align=[0,0,-z]
                        );
            }
        }
    }
}

if(false)
{
    b=bearing_igus_rj4jp_01_12;
    bearing_mount_holes(b, orient=[0,0,1]);
    translate([30,0,0])
    bearing(b, orient=[0,0,1]);
}


Added a short OpenSCAD description to aid in the creation of simple modifier meshes that describe a change every N layers

// Used to generate a modifier mesh to do something every few layers.
// Load into OpenSCAD, tweak the variables below, export as STL and load as
// a modifier mesh. Then change settings for the modifier mesh.

// Written by Joseph Lenox; in public domain.

layer_height = 0.3; // set to layer height in slic3r for "best" results.
number_of_solid_layers = 2;
N = 4; // N > number_of_solid_layers or else the whole thing will be solid
model_height = 300.0;
model_width = 300.0; // these two should be at least as big as the model
model_depth = 300.0; // but bigger isn't a problem
initial_offset=0; // don't generate below this

position_on_bed=[0,0,0]; // in case you need to move it around

// don't touch below unless you know what you are doing.
simple_layers = round(model_height/0.3);
translate(position_on_bed)
  for (i = [initial_offset:N:simple_layers]) {
    translate([0,0,i*layer_height])
      translate([0,0,(layer_height*number_of_solid_layers)/2])
      cube([model_width,model_depth,layer_height*number_of_solid_layers], center=true);
  }

error: pathspec 'utils/modifier_helpers/solid_layers.scad' did not match any file(s) known to git


rocket nose cone

rocket nose cone

module cone()
{
  wall = 3*0.6; // i.e. 3x extrusion width
  difference()
  {
    cylinder(d1=80, d2=0.1, h=50, $fn=100);
    cylinder(d1=80-2*wall, d2=0.1, h=50-wall, $fn=100);
  }
}

cone();

error: pathspec 'water_rocket_launcher/rocket_cone.scad' did not match any file(s) known to git


misc: add v_xz

misc: add v_xz

include <system.scad>
use <scad-utils/linalg.scad>
use <scad-utils/lists.scad>

function posvec(path_vec) = [for(v=path_vec) translation_part(v)];
function posvec_x(path_vec) = [for(v=path_vec) translation_part(v)[0]];
function posvec_y(path_vec) = [for(v=path_vec) translation_part(v)[1]];
function posvec_z(path_vec) = [for(v=path_vec) translation_part(v)[2]];

function vec_i(v3,i) = [for(v=v3) v[i]];
function vec_0(v3) = vec_i(v3,0);
function vec_1(v3) = vec_i(v3,1);
function vec_2(v3) = vec_i(v3,2);

// if V is array and len == 1, return single value, otherwise return V
function singlify(V) = len(V)==1?V[0]:V;
if($test_mode)
{
    assert_v(singlify("t"),"t");
    assert_v(singlify("test"),"test");
    assert_v(singlify(0),0);
    assert_v(singlify([]),[]);
    assert_v(singlify([0]),0);
    assert_v(singlify([0,1]),[0,1]);
}

function v_itrlen(vec) = [0:1:len(vec)-1];

function range1(v1) = [min(v1), max(v1)];
function range3(v3) = [range1(vec_0(v3)), range1(vec_1(v3)), range1(vec_2(v3))];
function bbox(r) = [ [r[0][0],r[1][0],r[2][0]], [r[0][1],r[1][1],r[2][1]] ];
function transform_pp(vec_m, t_pre, t_post) = [for(m=vec_m) (t_pre*m)*t_post];
function transform_pre(vec_m, t) = [for(m=vec_m) t*m];
function transform_post(vec_m, t) = [for(m=vec_m) m*t];

function clamp(v, v1, v2) = min(max(v,v1),v2);

// from the start (or s'th element) to the e'th element - remember elements are zero based
function v_sum(v,e=U,start=0) =
let(e_= fallback(e, len(v)-1))
(e==start ? v[e] : v[e_] + v_sum(v,e_-1,start));

function v_i(vec,i) = [for(vv=vec) vv[i]];
function v_get(vec,key) = [for(vv=vec) get(key, vv)];
function v_add(vec,v) = [for(vv=vec) vv+v];
function v_sub(vec,v) = [for(vv=vec) vv+v];
function v_mul(A,B) = [for(i=[0:len(A)-1]) A[i]*B[i]];
function v_avg(v,e=U,start=0) = v_sum(v,e,start) / (len(v));
function v_abs(v, start=0) = [for(i=[start:1:len(v)-1]) abs(v[i])];
function v_sign(v, start=0) = [for(i=[start:1:len(v)-1]) sign(v[i])];
function v_max(v, m, start=0) = [for(i=[start:1:len(v)-1]) max(v[i],m)];
function v_min(v, m, start=0) = [for(i=[start:1:len(v)-1]) min(v[i],m)];
function v_clamp(v, v1, v2, start=0) = [for(i=[start:1:len(v)-1]) clamp(v[i],v1,v2)];

if($test_mode)
{
    assert_v(v_mul([0,1], [1,2]), [0, 2]);
    assert_v(v_mul([1,1], [1,2]), [1, 2]);
    assert_v(v_mul([1,2], [1,2]), [1, 4]);
}

function v_slice(v, start, end) =
let(start_ = start==U?0:start)
let(end_ = end==U?len(v)-1:end)
[for(i=[start_:1:end_]) v[i]];

if($test_mode)
{
    v=[0,1,2];
    assert_v(v_slice(v), [0, 1,2]);
    assert_v(v_slice(v,start=1), [1,2]);
    assert_v(v_slice(v,end=1), [0, 1]);
    assert_v(v_slice(v,start=1,end=1), [1]);
}

// cumulative sum of vector [1,2,3] = [1,3,6]
function v_cumsum(v, start=0, end) = [for(i=[start:1:end==U?len(v)-1:end]) v_sum(v,i)];

// filter/remove a val from a vec
function filter(vec,val=U) = [for(v=vec) if(v!=val) v];
function v_filter(vec,val=U) = filter(vec,val);

if($test_mode)
{
    v=[0,1,2];
    assert_v(filter(v, U), [0,1,2]);
    assert_v(filter(v, 1), [0,2]);
}

function vec_pair_double_transform_post(vec,t1,t2)=
    flatten(
        [for(v=vec)
            [transform_post(v,t1),transform_post(v,t2)]
        ]);

function vec_pair_double(vec)=flatten([for(v=vec) [v,v] ]);

/** Calculates length of hypotenuse according to pythagoras */
function pythag_hyp(a, b)=sqrt(a*a+b*b);
function pythag_leg(b, c)=sqrt(c*c-b*b);

function v_x(v) = [v[0],0,0];
function v_y(v) = [0,v[1],0];
function v_z(v) = [0,0,v[2]];

function v_xy(v) = [v[0],v[1],0];
function v_xz(v) = [v[0],0,v[2]];
function v_xyz(v) = [v[0],v[1],v[2]];
function v_yz(v) = [0,v[1],v[2]];
function v_z(v) = [0,0,v[2]];

//-- Calculate the module/magnitude of a vector
function v_mod(v) = (sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]));

//-- Calculate the cross product of two vectors
function v_cross(u,v) = [
  u[1]*v[2] - v[1]*u[2],
  u[2]*v[0] - v[2]*u[0],
  u[0]*v[1] - v[0]*u[1]
];

//-- Calculate the dot product of two vectors
function v_dot(u,v) = u[0]*v[0]+u[1]*v[1]+u[2]*v[2];

//-- Return the unit vector of a vector
function v_unitv(v) = v/v_mod(v);

//-- Return the angle between two vectores
function v_anglev(u,v) = acos( v_dot(u,v) / (v_mod(u)*v_mod(v)) );

function _orient_angles(zaxis)=
[
    -asin(zaxis.y / norm(zaxis)),
    atan2(zaxis.x, zaxis.z),
    0
];

/*function _orient_angles_(v)=*/
/*[*/
    /*-atan2(sqrt(v.x*v.x+v.y*v.y), v.z),*/
    /*0,*/
    /*-atan2(v.x, v.z)*/
/*];*/

//matrix rotation functions
function _rotate_x_matrix(a)=
[[1,0,0,0],
    [0,cos(a),-sin(a),0],
    [0,sin(a),cos(a),0],
    [0,0,0,1]];

function _rotate_y_matrix(a)=
[[cos(a),0,sin(a),0],
    [0,1,0,0],
    [-sin(a),0,cos(a),0],
    [0,0,0,1]];

function _rotate_z_matrix(a)=
[[cos(a),-sin(a),0,0],
    [sin(a),cos(a),0,0],
    [0,0,1,0],
    [0,0,0,1]];

function _rotate_matrix(a)=_rotate_z_matrix(a.z)*_rotate_y_matrix(a.y)*_rotate_x_matrix(a.x);

//hadamard product (aka "component-wise" product) for vectors
function hadamard(v1,v2) = [v1.x*v2.x, v1.y*v2.y, v1.z*v2.z];

// search in a dictionary (vector w/key-value pairs) and replace the value for a given key
function dict_replace(dict, key, newvalue)=[
        for(kv=dict)
        kv[0] == key ?
            [key, newvalue]
            :
            kv
        ];

function dict_replace_multiple(dict, newvaluesdict)=[
    for(kv_old=dict)
    let(r = search(kv_old[0], newvaluesdict, num_returns_per_match=0, index_col_num=0))
    r==[]?kv_old:newvaluesdict[r[0]]
    ];

function sinh(x) = (1 - pow(e, -2 * x)) / (2 * pow(e, -x));
function cosh(x) = (1 + pow(e, -2 * x)) / (2 * pow(e, -x));
function tanh(x) = sinh(x) / cosh(x);
function cot(x) = 1 / tan(x);

function factorial(n) = n == 0 ? 1 : factorial(n - 1) * n;

if(false)
{
    vec=[ 10, 20, 30, 40 ];
    echo("v_sum=", v_sum(vec,2,1)); // is 20+30=50
    echo("v_sum=", v_sum(vec));
    echo("v_cumsum=", v_cumsum(vec));
}

// FUNCTION: is_String(x)
//   Returns true if x is a string, false otherwise.
function is_string(x) =
	x == U || len(x) == U
		? false // if U, a boolean or a number
		: len(str(x,x)) == len(x)*2; // if an array, this is false

// FUNCTION: is_array(x)
//   Returns true if x is an array, false otherwise.
function is_array(x) = is_string(x) ? false : len(x) != U;

function identity(d)        = d == 0 ? 1 : [for(y=[1:d]) [for(x=[1:d]) x == y ? 1 : 0] ];
function unit_vector(v)     = let(x=v[0], y=v[1], z=v[2]) [x/norm(v), y/norm(v), z/norm(v)];
function skew_symmetric(v)  = let(x=v[0], y=v[1], z=v[2]) [[0, -z, y], [z, 0, -x], [-y, x, 0]];
function tensor_product1(u) = let(x=u[0], y=u[1], z=u[2]) [[x*x, x*y, x*z], [x*y, y*y, y*z], [x*z, y*z, z*z]];
function v_rotate(a, v)
    = is_array(a)
? let(rx=a[0], ry=a[1], rz=a[2])
    [[1, 0, 0],              [0, cos(rx), -sin(rx)], [0, sin(rx), cos(rx)]]
  * [[cos(ry), 0, sin(ry)],  [0, 1, 0],              [-sin(ry), 0, cos(ry)]]
  * [[cos(rz), -sin(rz), 0], [sin(rz), cos(rz), 0],  [0, 0, 1]]
    : let(uv=unit_vector(v))
    cos(a)*identity(3) + sin(a)*skew_symmetric(uv) + (1 - cos(a))*tensor_product1(uv);

 /*echo(rotate(90, X) * [1, 0, 0]);*/
 /*echo(rotate(90, X) * [0, 1, 0]);*/
 /*echo(rotate(90, X) * [0, 0, 1]);*/

function lerp(v0, v1, t) =  (1-t)*v0 + t*v1;

if($test_mode)
{
    assert(lerp(-1, 1, 0) == -1);
    assert(lerp(-1, 1, .5) == 0);
    assert(lerp(-1, 1, 1) == 1);
}

if($test_mode)
{
    assert_v(fallback(U,1), 1);
}

function fallback(a, b) = a==U?b:a;

// echo(fallback(U,[U, 1]));
function v_fallback(a,v,i=0) = (a!=U || i > len(v)-1) ? a : v_fallback(v[i],v,i+1);

function zip(a,b,start=0,end) = [for(i=[start:1:fallback(end,len(a)-1)]) [a[i],b[i]] ];
function zip_v(v,start=0,end) = [for(i=[start:1:fallback(end,len(v[0])-1)]) v_i(v,i) ];

if($test_mode)
{
    vec_a = [0,4];
    vec_b = [1,5];
    vec_c = [2,6];
    vec_d = [3,7];
    assert_v(zip(vec_a,vec_b),[[0,1],[4,5]]);
    assert_v(zip_v([vec_a,vec_b]),[[0,1],[4,5]]);
    assert_v(zip_v([vec_a,vec_b,vec_c,vec_d]), [[0,1,2,3],[4,5,6,7]]);
}

function vv_fallback(v,start=0,end) =
let(z = zip_v(v))
[
for(i=[start:1:fallback(end,len(v[0])-1)])
    v_fallback(v=z[i],i=0)
];

if($test_mode)
{
    vec_a=[ 10,  U,   U, 40 ];
    vec_b=[ 10,  U,  30,  U ];
    vec_c=[  U, 20,   U, 40 ];
    assert(v_fallback(a=5, v=vec_a), 5);
    assert_v(vv_fallback(v=[vec_a, vec_b,vec_c]), [10,20,30,40]);
}

function fn_from_r(r) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, r*2*PI / $fs), 5));

function fn_from_d(d) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, d*PI / $fs), 5));

module assert_v(val, expected, message)
{
    if(len(val) > 0)
    {
        if(len(val) != len(expected))
        {
            echo("assertion, not equal length of arrays", val, expected);
            assert(val == expected, message);
        }
        for(i=[0:len(val)])
        {
            if(val[i] != expected[i])
            {
                echo("assertion, unexpected value at index", i, val, expected);
                assert(val == expected, message);
            }
        }

    }
    else
    {
        if(val != expected)
        {
            echo("assertion, unexpected value", val, expected);
            assert(val == expected, message);
        }
    }
}

function spread(v0,v1,a) =
a>=2 ?
    [for(i=[0:1/(a-1):1]) lerp(v0,v1,i)]
:
    [lerp(v0,v1,.5)]
;

if($test_mode)
{
    assert_v(spread(-1, 1, 1), [0]);
    assert_v(spread(-1, 1, 2), [-1,1]);
    assert_v(spread(-1, 1, 3), [-1,0,1]);
    // fails because of float inaccuracy
    /*assert_v(spread(-1, 1, 4), [-1,-1/3,1/3,1]);*/
    assert_v(spread(-1, 1, 5), [-1,-.5,0,.5,1]);
}

// the apothem (inradius) is the distance from the center of a regular
// polygon to the flat side (not a vertex)
function apothem(circumradius, fn) = circumradius*cos(180/fn);
function inradius(circumradius, fn) = circumradius*cos(180/fn);;
// the circumradius (outradius) is the distance from the center of a regular
// polygon to a vertex
function circumradius(apothem, fn) = apothem / cos(180/fn);
function outradius(apothem, fn) = apothem / cos(180/fn);
function fn_radius(r, fn) = apothem(r,fn);
function hex_radius(r) = apothem(r, 6);

if($test_mode)
{
    assert_v(apothem(circumradius(5,6)), circumradius(apothem(5),6));
}

function header_col_index(v, col_keys) =
    singlify(
        [for(col_key=col_keys)
        let(result=search([col_key], v[0], index_col_number=0, num_returns_per_match=0))
        result[0][0]
        ]
    );

function geth(S, col_keys, row_index) =
    singlify(
        [for(col_key=col_keys)
            let(col_index = header_col_index(S, col_key))
            S[row_index+1][col_index]
        ]);

if($test_mode)
{
    A = [
    ["Tx", "Ty"],
    [1, 2],
    [3, 4],
    ];

    assert_v(header_col_index(A, "Tx"), 0);
    assert_v(header_col_index(A, "Ty"), 1);

    assert_v(header_col_index(A, ["Ty"]), 1);
    assert_v(header_col_index(A, ["Tx"]), 0);
    assert_v(header_col_index(A, ["Tx", "Ty"]), [0,1]);

    assert_v(geth(A, "Tx", 0), 1);
    assert_v(geth(A, "Tx", 1), 3);
    assert_v(geth(A, "Ty", 0), 2);
    assert_v(geth(A, "Ty", 1), 4);

    assert_v(geth(A, ["Tx","Ty"], 0), [1,2]);
    assert_v(geth(A, ["Tx","Ty"], 1), [3,4]);

}

function v_contains(V, val, start=0, end=U) =
    !is_array(V) ?
        V == val :
        let(e=end==U?len(V):end)
        V[start]==val ? true :
        start == len(V)-1 ? false :
        v_contains(V,val,start+1,end);

if($test_mode)
{
    assert_v(v_contains(0, 0), true);
    assert_v(v_contains(0, 1), false);
    assert_v(v_contains([0,1], 0), true);
    assert_v(v_contains([0,1], 1), true);
    assert_v(v_contains([0,1], 2), false);
}

// helper functions to work on headered-arrays (where first entry is column headers)
function reverse_header(V) = concat([V[0]], reverse(v_slice(V,start=1)));
function concat_header(A,B) =concat([A[0]], v_slice(A,start=1),v_slice(B,start=1));

// add value from these columns in headered array
function array_header_col_add(S, cols, val) =
    let(cols = header_col_index(S,cols))
    concat([S[0]],
    [
        for(i=[1:len(S)-1])
        [
        for(j=[0:len(S[0])-1])
            (v_contains(cols,j)) ?
                S[i][j] + val :
                S[i][j]
        ]
    ]);

function array_header_col_subtract(S, cols, val) = array_header_col_add(S,cols,-val);

if($test_mode)
{
    A = [
    ["Tx", "Ty"],
    [1, 2],
    [3, 4],
    ];

    assert_v(
        array_header_col_add(A, "Tx", -1),
        [["Tx","Ty"],
         [0,2],
         [2,4],
        ]);

    assert_v(
        array_header_col_add(A, ["Tx"], -1),
        [["Tx","Ty"],
         [0,2],
         [2,4],
        ]);

    assert_v(
        array_header_col_add(A, ["Tx","Ty"], -1),
        [["Tx","Ty"],
         [0,1],
         [2,3],
        ]);

    assert_v(
        array_header_col_add(A, ["Ty"], -1),
        [["Tx","Ty"],
         [1,1],
         [3,3],
        ]);
}

function take3(v) = [v[0],v[1],v[2]];

function vec3(V) =
let(l = len(V))
l == 3 ? V :
l < 3 ? vec3(concat(V,0)) :
take3(V);

if($test_mode)
{
    assert_v(take3([0,1,0,1]), [0,1,0]);

    assert_v(vec3([0,1]), [0,1,0]);
    assert_v(vec3([0]), [0,0,0]);
    assert_v(vec3([1]), [1,0,0]);
    assert_v(vec3([0,0,1]), [0,0,1]);
    assert_v(vec3([0,0,1,0]), [0,0,1]);
}

include <system.scad>
use <scad-utils/linalg.scad>
use <scad-utils/lists.scad>

function posvec(path_vec) = [for(v=path_vec) translation_part(v)];
function posvec_x(path_vec) = [for(v=path_vec) translation_part(v)[0]];
function posvec_y(path_vec) = [for(v=path_vec) translation_part(v)[1]];
function posvec_z(path_vec) = [for(v=path_vec) translation_part(v)[2]];

function vec_i(v3,i) = [for(v=v3) v[i]];
function vec_0(v3) = vec_i(v3,0);
function vec_1(v3) = vec_i(v3,1);
function vec_2(v3) = vec_i(v3,2);

// if V is array and len == 1, return single value, otherwise return V
function singlify(V) = len(V)==1?V[0]:V;
if($test_mode)
{
    assert_v(singlify("t"),"t");
    assert_v(singlify("test"),"test");
    assert_v(singlify(0),0);
    assert_v(singlify([]),[]);
    assert_v(singlify([0]),0);
    assert_v(singlify([0,1]),[0,1]);
}

function v_itrlen(vec) = [0:1:len(vec)-1];

function range1(v1) = [min(v1), max(v1)];
function range3(v3) = [range1(vec_0(v3)), range1(vec_1(v3)), range1(vec_2(v3))];
function bbox(r) = [ [r[0][0],r[1][0],r[2][0]], [r[0][1],r[1][1],r[2][1]] ];
function transform_pp(vec_m, t_pre, t_post) = [for(m=vec_m) (t_pre*m)*t_post];
function transform_pre(vec_m, t) = [for(m=vec_m) t*m];
function transform_post(vec_m, t) = [for(m=vec_m) m*t];

function clamp(v, v1, v2) = min(max(v,v1),v2);

// from the start (or s'th element) to the e'th element - remember elements are zero based
function v_sum(v,e=U,start=0) =
let(e_= fallback(e, len(v)-1))
(e==start ? v[e] : v[e_] + v_sum(v,e_-1,start));

function v_i(vec,i) = [for(vv=vec) vv[i]];
function v_get(vec,key) = [for(vv=vec) get(key, vv)];
function v_add(vec,v) = [for(vv=vec) vv+v];
function v_sub(vec,v) = [for(vv=vec) vv+v];
function v_mul(A,B) = [for(i=[0:len(A)-1]) A[i]*B[i]];
function v_avg(v,e=U,start=0) = v_sum(v,e,start) / (len(v));
function v_abs(v, start=0) = [for(i=[start:1:len(v)-1]) abs(v[i])];
function v_sign(v, start=0) = [for(i=[start:1:len(v)-1]) sign(v[i])];
function v_max(v, m, start=0) = [for(i=[start:1:len(v)-1]) max(v[i],m)];
function v_min(v, m, start=0) = [for(i=[start:1:len(v)-1]) min(v[i],m)];
function v_clamp(v, v1, v2, start=0) = [for(i=[start:1:len(v)-1]) clamp(v[i],v1,v2)];

if($test_mode)
{
    assert_v(v_mul([0,1], [1,2]), [0, 2]);
    assert_v(v_mul([1,1], [1,2]), [1, 2]);
    assert_v(v_mul([1,2], [1,2]), [1, 4]);
}

function v_slice(v, start, end) =
let(start_ = start==U?0:start)
let(end_ = end==U?len(v)-1:end)
[for(i=[start_:1:end_]) v[i]];

if($test_mode)
{
    v=[0,1,2];
    assert_v(v_slice(v), [0, 1,2]);
    assert_v(v_slice(v,start=1), [1,2]);
    assert_v(v_slice(v,end=1), [0, 1]);
    assert_v(v_slice(v,start=1,end=1), [1]);
}

// cumulative sum of vector [1,2,3] = [1,3,6]
function v_cumsum(v, start=0, end) = [for(i=[start:1:end==U?len(v)-1:end]) v_sum(v,i)];

// filter/remove a val from a vec
function filter(vec,val=U) = [for(v=vec) if(v!=val) v];
function v_filter(vec,val=U) = filter(vec,val);

if($test_mode)
{
    v=[0,1,2];
    assert_v(filter(v, U), [0,1,2]);
    assert_v(filter(v, 1), [0,2]);
}

function vec_pair_double_transform_post(vec,t1,t2)=
    flatten(
        [for(v=vec)
            [transform_post(v,t1),transform_post(v,t2)]
        ]);

function vec_pair_double(vec)=flatten([for(v=vec) [v,v] ]);

/** Calculates length of hypotenuse according to pythagoras */
function pythag_hyp(a, b)=sqrt(a*a+b*b);
function pythag_leg(b, c)=sqrt(c*c-b*b);

function v_x(v) = [v[0],0,0];
function v_y(v) = [0,v[1],0];
function v_z(v) = [0,0,v[2]];

function v_xy(v) = [v[0],v[1],0];
function v_xyz(v) = [v[0],v[1],v[2]];
function v_yz(v) = [0,v[1],v[2]];
function v_z(v) = [0,0,v[2]];

//-- Calculate the module/magnitude of a vector
function v_mod(v) = (sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]));

//-- Calculate the cross product of two vectors
function v_cross(u,v) = [
  u[1]*v[2] - v[1]*u[2],
  u[2]*v[0] - v[2]*u[0],
  u[0]*v[1] - v[0]*u[1]
];

//-- Calculate the dot product of two vectors
function v_dot(u,v) = u[0]*v[0]+u[1]*v[1]+u[2]*v[2];

//-- Return the unit vector of a vector
function v_unitv(v) = v/v_mod(v);

//-- Return the angle between two vectores
function v_anglev(u,v) = acos( v_dot(u,v) / (v_mod(u)*v_mod(v)) );

function _orient_angles(zaxis)=
[
    -asin(zaxis.y / norm(zaxis)),
    atan2(zaxis.x, zaxis.z),
    0
];

/*function _orient_angles_(v)=*/
/*[*/
    /*-atan2(sqrt(v.x*v.x+v.y*v.y), v.z),*/
    /*0,*/
    /*-atan2(v.x, v.z)*/
/*];*/

//matrix rotation functions
function _rotate_x_matrix(a)=
[[1,0,0,0],
    [0,cos(a),-sin(a),0],
    [0,sin(a),cos(a),0],
    [0,0,0,1]];

function _rotate_y_matrix(a)=
[[cos(a),0,sin(a),0],
    [0,1,0,0],
    [-sin(a),0,cos(a),0],
    [0,0,0,1]];

function _rotate_z_matrix(a)=
[[cos(a),-sin(a),0,0],
    [sin(a),cos(a),0,0],
    [0,0,1,0],
    [0,0,0,1]];

function _rotate_matrix(a)=_rotate_z_matrix(a.z)*_rotate_y_matrix(a.y)*_rotate_x_matrix(a.x);

//hadamard product (aka "component-wise" product) for vectors
function hadamard(v1,v2) = [v1.x*v2.x, v1.y*v2.y, v1.z*v2.z];

// search in a dictionary (vector w/key-value pairs) and replace the value for a given key
function dict_replace(dict, key, newvalue)=[
        for(kv=dict)
        kv[0] == key ?
            [key, newvalue]
            :
            kv
        ];

function dict_replace_multiple(dict, newvaluesdict)=[
    for(kv_old=dict)
    let(r = search(kv_old[0], newvaluesdict, num_returns_per_match=0, index_col_num=0))
    r==[]?kv_old:newvaluesdict[r[0]]
    ];

function sinh(x) = (1 - pow(e, -2 * x)) / (2 * pow(e, -x));
function cosh(x) = (1 + pow(e, -2 * x)) / (2 * pow(e, -x));
function tanh(x) = sinh(x) / cosh(x);
function cot(x) = 1 / tan(x);

function factorial(n) = n == 0 ? 1 : factorial(n - 1) * n;

if(false)
{
    vec=[ 10, 20, 30, 40 ];
    echo("v_sum=", v_sum(vec,2,1)); // is 20+30=50
    echo("v_sum=", v_sum(vec));
    echo("v_cumsum=", v_cumsum(vec));
}

// FUNCTION: is_String(x)
//   Returns true if x is a string, false otherwise.
function is_string(x) =
	x == U || len(x) == U
		? false // if U, a boolean or a number
		: len(str(x,x)) == len(x)*2; // if an array, this is false

// FUNCTION: is_array(x)
//   Returns true if x is an array, false otherwise.
function is_array(x) = is_string(x) ? false : len(x) != U;

function identity(d)        = d == 0 ? 1 : [for(y=[1:d]) [for(x=[1:d]) x == y ? 1 : 0] ];
function unit_vector(v)     = let(x=v[0], y=v[1], z=v[2]) [x/norm(v), y/norm(v), z/norm(v)];
function skew_symmetric(v)  = let(x=v[0], y=v[1], z=v[2]) [[0, -z, y], [z, 0, -x], [-y, x, 0]];
function tensor_product1(u) = let(x=u[0], y=u[1], z=u[2]) [[x*x, x*y, x*z], [x*y, y*y, y*z], [x*z, y*z, z*z]];
function v_rotate(a, v)
    = is_array(a)
? let(rx=a[0], ry=a[1], rz=a[2])
    [[1, 0, 0],              [0, cos(rx), -sin(rx)], [0, sin(rx), cos(rx)]]
  * [[cos(ry), 0, sin(ry)],  [0, 1, 0],              [-sin(ry), 0, cos(ry)]]
  * [[cos(rz), -sin(rz), 0], [sin(rz), cos(rz), 0],  [0, 0, 1]]
    : let(uv=unit_vector(v))
    cos(a)*identity(3) + sin(a)*skew_symmetric(uv) + (1 - cos(a))*tensor_product1(uv);

 /*echo(rotate(90, X) * [1, 0, 0]);*/
 /*echo(rotate(90, X) * [0, 1, 0]);*/
 /*echo(rotate(90, X) * [0, 0, 1]);*/

function lerp(v0, v1, t) =  (1-t)*v0 + t*v1;

if($test_mode)
{
    assert(lerp(-1, 1, 0) == -1);
    assert(lerp(-1, 1, .5) == 0);
    assert(lerp(-1, 1, 1) == 1);
}

if($test_mode)
{
    assert_v(fallback(U,1), 1);
}

function fallback(a, b) = a==U?b:a;

// echo(fallback(U,[U, 1]));
function v_fallback(a,v,i=0) = (a!=U || i > len(v)-1) ? a : v_fallback(v[i],v,i+1);

function zip(a,b,start=0,end) = [for(i=[start:1:fallback(end,len(a)-1)]) [a[i],b[i]] ];
function zip_v(v,start=0,end) = [for(i=[start:1:fallback(end,len(v[0])-1)]) v_i(v,i) ];

if($test_mode)
{
    vec_a = [0,4];
    vec_b = [1,5];
    vec_c = [2,6];
    vec_d = [3,7];
    assert_v(zip(vec_a,vec_b),[[0,1],[4,5]]);
    assert_v(zip_v([vec_a,vec_b]),[[0,1],[4,5]]);
    assert_v(zip_v([vec_a,vec_b,vec_c,vec_d]), [[0,1,2,3],[4,5,6,7]]);
}

function vv_fallback(v,start=0,end) =
let(z = zip_v(v))
[
for(i=[start:1:fallback(end,len(v[0])-1)])
    v_fallback(v=z[i],i=0)
];

if($test_mode)
{
    vec_a=[ 10,  U,   U, 40 ];
    vec_b=[ 10,  U,  30,  U ];
    vec_c=[  U, 20,   U, 40 ];
    assert(v_fallback(a=5, v=vec_a), 5);
    assert_v(vv_fallback(v=[vec_a, vec_b,vec_c]), [10,20,30,40]);
}

function fn_from_r(r) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, r*2*PI / $fs), 5));

function fn_from_d(d) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, d*PI / $fs), 5));

module assert_v(val, expected, message)
{
    if(len(val) > 0)
    {
        if(len(val) != len(expected))
        {
            echo("assertion, not equal length of arrays", val, expected);
            assert(val == expected, message);
        }
        for(i=[0:len(val)])
        {
            if(val[i] != expected[i])
            {
                echo("assertion, unexpected value at index", i, val, expected);
                assert(val == expected, message);
            }
        }

    }
    else
    {
        if(val != expected)
        {
            echo("assertion, unexpected value", val, expected);
            assert(val == expected, message);
        }
    }
}

function spread(v0,v1,a) =
a>=2 ?
    [for(i=[0:1/(a-1):1]) lerp(v0,v1,i)]
:
    [lerp(v0,v1,.5)]
;

if($test_mode)
{
    assert_v(spread(-1, 1, 1), [0]);
    assert_v(spread(-1, 1, 2), [-1,1]);
    assert_v(spread(-1, 1, 3), [-1,0,1]);
    // fails because of float inaccuracy
    /*assert_v(spread(-1, 1, 4), [-1,-1/3,1/3,1]);*/
    assert_v(spread(-1, 1, 5), [-1,-.5,0,.5,1]);
}

// the apothem (inradius) is the distance from the center of a regular
// polygon to the flat side (not a vertex)
function apothem(circumradius, fn) = circumradius*cos(180/fn);
function inradius(circumradius, fn) = circumradius*cos(180/fn);;
// the circumradius (outradius) is the distance from the center of a regular
// polygon to a vertex
function circumradius(apothem, fn) = apothem / cos(180/fn);
function outradius(apothem, fn) = apothem / cos(180/fn);
function fn_radius(r, fn) = apothem(r,fn);
function hex_radius(r) = apothem(r, 6);

if($test_mode)
{
    assert_v(apothem(circumradius(5,6)), circumradius(apothem(5),6));
}

function header_col_index(v, col_keys) =
    singlify(
        [for(col_key=col_keys)
        let(result=search([col_key], v[0], index_col_number=0, num_returns_per_match=0))
        result[0][0]
        ]
    );

function geth(S, col_keys, row_index) =
    singlify(
        [for(col_key=col_keys)
            let(col_index = header_col_index(S, col_key))
            S[row_index+1][col_index]
        ]);

if($test_mode)
{
    A = [
    ["Tx", "Ty"],
    [1, 2],
    [3, 4],
    ];

    assert_v(header_col_index(A, "Tx"), 0);
    assert_v(header_col_index(A, "Ty"), 1);

    assert_v(header_col_index(A, ["Ty"]), 1);
    assert_v(header_col_index(A, ["Tx"]), 0);
    assert_v(header_col_index(A, ["Tx", "Ty"]), [0,1]);

    assert_v(geth(A, "Tx", 0), 1);
    assert_v(geth(A, "Tx", 1), 3);
    assert_v(geth(A, "Ty", 0), 2);
    assert_v(geth(A, "Ty", 1), 4);

    assert_v(geth(A, ["Tx","Ty"], 0), [1,2]);
    assert_v(geth(A, ["Tx","Ty"], 1), [3,4]);

}

function v_contains(V, val, start=0, end=U) =
    !is_array(V) ?
        V == val :
        let(e=end==U?len(V):end)
        V[start]==val ? true :
        start == len(V)-1 ? false :
        v_contains(V,val,start+1,end);

if($test_mode)
{
    assert_v(v_contains(0, 0), true);
    assert_v(v_contains(0, 1), false);
    assert_v(v_contains([0,1], 0), true);
    assert_v(v_contains([0,1], 1), true);
    assert_v(v_contains([0,1], 2), false);
}

// helper functions to work on headered-arrays (where first entry is column headers)
function reverse_header(V) = concat([V[0]], reverse(v_slice(V,start=1)));
function concat_header(A,B) =concat([A[0]], v_slice(A,start=1),v_slice(B,start=1));

// add value from these columns in headered array
function array_header_col_add(S, cols, val) =
    let(cols = header_col_index(S,cols))
    concat([S[0]],
    [
        for(i=[1:len(S)-1])
        [
        for(j=[0:len(S[0])-1])
            (v_contains(cols,j)) ?
                S[i][j] + val :
                S[i][j]
        ]
    ]);

function array_header_col_subtract(S, cols, val) = array_header_col_add(S,cols,-val);

if($test_mode)
{
    A = [
    ["Tx", "Ty"],
    [1, 2],
    [3, 4],
    ];

    assert_v(
        array_header_col_add(A, "Tx", -1),
        [["Tx","Ty"],
         [0,2],
         [2,4],
        ]);

    assert_v(
        array_header_col_add(A, ["Tx"], -1),
        [["Tx","Ty"],
         [0,2],
         [2,4],
        ]);

    assert_v(
        array_header_col_add(A, ["Tx","Ty"], -1),
        [["Tx","Ty"],
         [0,1],
         [2,3],
        ]);

    assert_v(
        array_header_col_add(A, ["Ty"], -1),
        [["Tx","Ty"],
         [1,1],
         [3,3],
        ]);
}

function take3(v) = [v[0],v[1],v[2]];

function vec3(V) =
let(l = len(V))
l == 3 ? V :
l < 3 ? vec3(concat(V,0)) :
take3(V);

if($test_mode)
{
    assert_v(take3([0,1,0,1]), [0,1,0]);

    assert_v(vec3([0,1]), [0,1,0]);
    assert_v(vec3([0]), [0,0,0]);
    assert_v(vec3([1]), [1,0,0]);
    assert_v(vec3([0,0,1]), [0,0,1]);
    assert_v(vec3([0,0,1,0]), [0,0,1]);
}


removed test code and updated distances and hole diameters

h=11;      // height
w=14;      // width
d=(5+1.5)/2; // diameter of the holes

difference()
{
  union()
  {
    // lower arc
    difference()
    {
      cylinder(h=h, r=(80+2*w)/2);
      translate([-(80+2*w)/2, 0, 0])
        cube([80+2*w, (80+2*w)/2, h]);
      cylinder(h=h, r=(80)/2);
    }

    // leg
    translate([-w/2,-80-80/2,0])
      cube([w, 80, h]);

    // forks
    translate([40, 0, 0])
      cube([w, 40, h]);
    translate([-40-w, 0, 0])
      cube([w, 40, h]);
  }

  // lower hole for screw
  translate([0, -80-34.5, -1])
    cylinder(h=h+2, r=d);
  // higher hole for screw
  translate([0, -80-34.5+11, -1])
    cylinder(h=h+2, r=d);
}


font panel is now properly rotate by default

use <lcd.scad>
use <menu_knob.scad>
use <menu_knob_manipulator.scad>
use <reset_button.scad>
use <sdcard_reader.scad>
use <cable_guide.scad>
include <positions.scad>

module front()
{
  difference()
  {
    union()
    {
      // main mounting legs
      for(offset = [ [boxSize[0]-16, 0, boxSize[2]-7], [boxSize[0]-70, 0, boxSize[2]-7] ])
        translate(offset)
          cube([16, 40+13, 7]);
      // side mounting wing
      translate([15, 0, 6])
        cube([18, 20, 7]);

      difference()
      {
        // main working area
        cube(boxSize);
        // prusa's framework  corner
        translate([15, 0, 0])
          cube([143-15, 53, boxSize[2]]);
        // LCD
        translate(lcdOffset)
        {
          // LCD itself
          translate([1/2-2/2, 10-1, 2])
            cube([97+2, 40+2, 10]);
          // place for PCB
          translate([-2/2, -1/2, -lcdOffset[2]-7])
            cube([98+2, 60+1, 10]);
          // place for cables
          translate([9, 60-5, -2])
            cube([39,10,4]);
          lcdBoard();
        }
        // encoder
        translate(encoderOffset)
        {
          translate([-2, -1/2, -8])
            cube([14+2*2, 12+1, 8+6]);
          menuKnob();
        }
        // SD card mount
        translate(sdCardOffset)
          rotate([0,180,0])
            sdCardReader();
        // reset button
        translate(resetButtonOffset)
        {
          translate([0, 0, -resetButtonOffset[2]])
            cylinder(r=(16+1)/2, h=boxSize[2])
          resetButton();
        }
        // cable guide drill
        translate([boxSize[0]-98-5, 40+16+6, 0])
          cableGuide();
        // side mounting wing cut-in, so that screw will hold tight
        cube([15, 40, 6]);
      }
    }

    // SD card mounting drills
    translate(sdCardOffset)
      rotate([0,180,0])
      {
        for(offset = [
                       [3/2+1, 3/2+1, -20/2],
                       [3/2+1, 31-3/2-1, -20/2]
                     ])
        {
          translate(offset)
            cylinder(r=3/2, h=20, $fs=1);
        }
      }
    // SD card mounting place
    translate(sdCardOffset + [+2/2, -2/2, 0])
      rotate([0,180,0])
        cube([45+2,31+2,2+8]);

    // LCD mounting drills
    translate(lcdOffset)
      for(offset = [
                     [3/2+1, 3/2+1, -1],
                     [98-3/2-1, 3/2+1, -1],
                     [3/2+1, 60-3/2-1, -1],
                     [98-3/2-1, 60-3/2-1, -1]
                   ])
      {
        translate(offset)
          cylinder(r=3/2, h=20, $fs=1);
      }

    // frame-mounting drills
    for(offsetOY = [ 13/2+5/2, 40+13+5])
      for(offsetOX = [15/2, boxSize[0]-70+16/2, boxSize[0]-16/2])
        translate([offsetOX, offsetOY, -1])
          cylinder(r=5/2, h=6+7+2, $fs=1);
  }

  // LCD stub
  %translate(lcdOffset)
    lcdBoard();
  // encoder stub
  %translate(encoderOffset)
  {
    menuKnob();
    translate([7, 6, 23+4])
      rotate([180,0,0])
        menuKnobManipulator();
  }
  // SD card mount stub
  %translate(sdCardOffset)
    rotate([0,180,0])
      sdCardReader();
  // reset button stub
  %translate(resetButtonOffset)
    resetButton();
}

rotate([180, 0, 0])
  front();

use <lcd.scad>
use <menu_knob.scad>
use <menu_knob_manipulator.scad>
use <reset_button.scad>
use <sdcard_reader.scad>
use <cable_guide.scad>
include <positions.scad>

module front()
{
  difference()
  {
    union()
    {
      // main mounting legs
      for(offset = [ [boxSize[0]-16, 0, boxSize[2]-7], [boxSize[0]-70, 0, boxSize[2]-7] ])
        translate(offset)
          cube([16, 40+13, 7]);
      // side mounting wing
      translate([15, 0, 6])
        cube([18, 20, 7]);

      difference()
      {
        // main working area
        cube(boxSize);
        // prusa's framework  corner
        translate([15, 0, 0])
          cube([143-15, 53, boxSize[2]]);
        // LCD
        translate(lcdOffset)
        {
          // LCD itself
          translate([1/2-2/2, 10-1, 2])
            cube([97+2, 40+2, 10]);
          // place for PCB
          translate([-2/2, -1/2, -lcdOffset[2]-7])
            cube([98+2, 60+1, 10]);
          // place for cables
          translate([9, 60-5, -2])
            cube([39,10,4]);
          lcdBoard();
        }
        // encoder
        translate(encoderOffset)
        {
          translate([-2, -1/2, -8])
            cube([14+2*2, 12+1, 8+6]);
          menuKnob();
        }
        // SD card mount
        translate(sdCardOffset)
          rotate([0,180,0])
            sdCardReader();
        // reset button
        translate(resetButtonOffset)
        {
          translate([0, 0, -resetButtonOffset[2]])
            cylinder(r=(16+1)/2, h=boxSize[2])
          resetButton();
        }
        // cable guide drill
        translate([boxSize[0]-98-5, 40+16+6, 0])
          cableGuide();
        // side mounting wing cut-in, so that screw will hold tight
        cube([15, 40, 6]);
      }
    }

    // SD card mounting drills
    translate(sdCardOffset)
      rotate([0,180,0])
      {
        for(offset = [
                       [3/2+1, 3/2+1, -20/2],
                       [3/2+1, 31-3/2-1, -20/2]
                     ])
        {
          translate(offset)
            cylinder(r=3/2, h=20, $fs=1);
        }
      }
    // SD card mounting place
    translate(sdCardOffset + [+2/2, -2/2, 0])
      rotate([0,180,0])
        cube([45+2,31+2,2+8]);

    // LCD mounting drills
    translate(lcdOffset)
      for(offset = [
                     [3/2+1, 3/2+1, -1],
                     [98-3/2-1, 3/2+1, -1],
                     [3/2+1, 60-3/2-1, -1],
                     [98-3/2-1, 60-3/2-1, -1]
                   ])
      {
        translate(offset)
          cylinder(r=3/2, h=20, $fs=1);
      }

    // frame-mounting drills
    for(offsetOY = [ 13/2+5/2, 40+13+5])
      for(offsetOX = [15/2, boxSize[0]-70+16/2, boxSize[0]-16/2])
        translate([offsetOX, offsetOY, -1])
          cylinder(r=5/2, h=6+7+2, $fs=1);
  }

  // LCD stub
  %translate(lcdOffset)
    lcdBoard();
  // encoder stub
  %translate(encoderOffset)
  {
    menuKnob();
    translate([7, 6, 23+4])
      rotate([180,0,0])
        menuKnobManipulator();
  }
  // SD card mount stub
  %translate(sdCardOffset)
    rotate([0,180,0])
      sdCardReader();
  // reset button stub
  %translate(resetButtonOffset)
    resetButton();
}

//rotate([180, 0, 0])
  front();


Create AluFrame.scad

Create AluFrame.scad

include <20x20_Profile.scad>

module AluFrame (){

// frame vertical
    AluProfile(410);            // South West corner
    translate([420,0,0]) {      // South East corner
    AluProfile(410);
    }
    translate([0,320,0]) {      // North West corner
    AluProfile(410);
    }
    translate([420,320,0]) {    // North East corner
    AluProfile(410);
    }

// frame horisontal North and South
    translate([20,0,410]) { // Front top
     rotate ([0,90,0]){
    AluProfile(400);
    }}

    translate([20,0,320]) { // Front top 2
     rotate ([0,90,0]){
    AluProfile(400);
    }}

    translate([20,0,105]) { // Front bottom
     rotate ([0,90,0]){
    AluProfile(400);
    }}

    translate([20,0,35]) { // Front bottom 2
     rotate ([0,90,0]){
    AluProfile(400);
    }}

    translate([20,320,410]) { // Back top
     rotate ([0,90,0]){
    AluProfile(400);
    }}

    translate([20,320,320]) { // Back top 2
     rotate ([0,90,0]){
    AluProfile(400);
    }}

    translate([20,320,35]) { // Back bottom
     rotate ([0,90,0]){
    AluProfile(400);
    }}

// frame horisontal West and East
    translate([0,20,320]) { // West top
     rotate ([270,0,0]){
    AluProfile(300);
    }}

    translate([0,20,105]) { // West bottom
     rotate ([270,0,0]){
    AluProfile(300);
    }}

    translate([420,20,320]) { // East Ttop
     rotate ([270,0,0]){
    AluProfile(300);
    }}

    translate([420,20,105]) { // East bottom
     rotate ([270,0,0]){
    AluProfile(300);
    }}

}

AluFrame ();

error: pathspec 'AluFrame.scad' did not match any file(s) known to git


kantcon

kantcon

fixing comments

use <small_step.scad>

echo(version=version());
echo("main picker assembly");

fanrad = 57/2;
fanheight = 25;
screwcenter = 48.5/2;
// actual box outside is 2mm bigger due to curve
outsidebox = 64.5;
outextendo = 25.5;
screwd = 1.6;
gearheight = 7.1;
pinrad = .865;
// bearing placements
bx=28;
by=30+outextendo;
bz=20;
// flaps for airflow valve
flapthick = 1.2;
truss = 9;		// knobby bit on flap
sprd = .6;
knobrad = 1.2;		// on side of chamber
lowknob = 9.5 + knobrad;  // distance from wire

module gearmotor(l=25, w=12, h=10, r=3
){
	cube([l,w,h], center=true);
	translate([-5,0,0]) rotate([0,90,0])
		cylinder(r=r, h=l+10+1, center=true, $fn=16);
	translate([l/2,0,0]) rotate([0,90,0])
		cylinder(r=1, h=w, center=true, $fn=16);
	translate([l/2,0,0]) rotate([90,0,0])
		cylinder(r=1.5, h=65, center=true, $fn=16);

}

module flapattach(ra=3, t=flapthick, trusslen=truss){
// the little bit going from rod to flap
	difference(){
	// disk-hull-disk
	hull(){
		union(){
			cylinder(r1=ra-1, r2=ra, h=t, center=true, $fn=36);
			translate([0,0,t])
				cylinder(r1=ra, r2=ra-1, h=t, center=true, $fn=36);
		}
		translate([trusslen,0,0])
		 union(){
			 cylinder(r1=ra-1, r2=ra, h=t, center=true, $fn=36);
			 translate([0,0,t])
				 cylinder(r1=ra, r2=ra-1, h=t, center=true, $fn=36);
		 }
		} // end of disk-hull-disk
	// hole for metal pin or wire (to swivel on)
	cylinder(r=pinrad+.1, h=t*6, center=true, $fn=12);
	}
}

module flap(rd=fanrad-1, t=flapthick, trs=truss){
// make quarter-section of stopper
	difference(){
		union(){
		difference(){
			cylinder(r=rd, h=t, center=false, $fn=128);
			translate([rd/2, -rd/2, t/2])
				cube([rd,rd,t*2], center=true);
			translate([-rd/2, -rd/2, t/2])
				cube([rd,rd,t*2], center=true);
			translate([-rd/2, rd/2, t/2])
				cube([rd,rd,t*2], center=true);
		}
		translate([0, trs, -6]) rotate([90,-60,0])
			flapattach();
		translate([0, rd-trs, -6]) rotate([90,-60,0])
			flapattach();
		}
	translate([rd/2, rd/2, t*3])
		cube([rd*2,rd,t*4], center=true);
	}
}

module flaps(){
	difference(){
		flap();
		rotate([0,0,-90])
			flap();
	}
}

module nodule(rat=45){
// cam for valve leaves
	rotate([0,0,rat]) scale([2,1,.5])
		sphere(r=2.42, $fn=64);
}

// nut at end of bolt for cut-outs
module m3nut(d=6.4,
				h=2.7,
				rodd=3.1,
				rodlen=19,
				hdled=3,
				hdrad=6/2,
				capture_channel=20,
				channel_dir=90,
				fat=.1){
	translate([0,0,-rodlen/2+h/2]){
		// nut on end of threads
		cylinder(r=d/2, h=h, center=true, $fn=6);
		// 'tunnel' to nut
		translate([(capture_channel/2)*cos(channel_dir-90), (capture_channel/2)*sin(channel_dir-90), 0]) rotate([0,0,channel_dir])
			cube([d, capture_channel, h], center=true);
	}
	// head of bolt is hexxed even if round
	translate([0,0,rodlen/2 - hdled/2])
		cylinder(r=hdrad+fat, h=hdled, center=true, $fn=6);
	// threaded part
	cylinder(r=rodd/2+fat, h=rodlen, center=true, $fn=12);
}

module z608z(mntrad=7.45/2, ht=7, innermnt=2.35, race=2.6, edge=2.35){
// 608zz bearing mount cutter
	translate([0,0,0])
		union(){
			ring((innermnt+mntrad)*2, mntrad*2,  height=ht-.1);
			ring((innermnt+mntrad+race)*2, (innermnt+mntrad)*2-.1,  height=ht-.25);
			ring((innermnt+mntrad+race+edge)*2, (innermnt+mntrad+race)*2-.1,  height=ht+.1);
			// thread-groove cutter:
			ring((innermnt+mntrad+race+edge)*2+.35, (innermnt+mntrad+race+edge)*2-.1, height=.7);
		}
}

// linear bearing for 6mm w/ rod sticking through it
module LMB6mm(OD=12,
				OD_groove=11.41,
				ID=6, len=18.86,
				stripe=1.25,
				stripe_to_end=2.66,
				rodlen=100){
difference(){
	union(){
		// body of bearing, stacked up
		translate([0,0,-.05])
			cylinder(r=OD/2, h=stripe_to_end+.1, $fn=64);
		translate([0,0,stripe_to_end])
			cylinder(r=OD_groove/2, h=stripe+.05, $fn=48);
		translate([0,0,stripe_to_end+stripe])
			cylinder(r=OD/2, h=len-stripe*2-stripe_to_end*2+.05, $fn=64);
		translate([0,0,len-stripe-stripe_to_end])
			cylinder(r=OD_groove/2, h=stripe+.05, $fn=48);
		translate([0,0,len-stripe_to_end])
			cylinder(r=OD/2, h=stripe_to_end+.05, $fn=64);
		// transition for printing upright
		translate([0,0,len])
			cylinder(r=OD/2-1.1, h=stripe/2, $fn=64);
		}
	// hole for rod
	cylinder(r=ID/2, h=stripe_to_end, $fn=64);
}
// rod stand-in for use in cuts
cylinder(r=ID/2, h=rodlen, center=true, $fn=36);
}

module ring(rOD, rID, height)
{
difference(){
	cylinder(r=rOD/2, h=height, center=true, $fn=96);
	cylinder(r=rID/2, h=height, center=true, $fn=96);
	}
}

module fan(side=57.2, w=fanheight, curve=3, screwcenter=screwcenter){
// box fan 60mm sqr nominal x 25mm high + 50mm OD air hole
// fan is a tad bigger here to allow for shrink
	realside = curve*2 + side;
	oldz = 1;
	translate([29,18,0])
		cube(size=[7, 2, 22], center=true);
	translate([38,18,5])
		cube(size=[35, 2, 5], center=true);
	translate([0,0,-32.4])
		cylinder(r=fanrad, h=40, center=true, $fn=64);
	for (z = [[screwcenter-9.4,screwcenter+7.7],
				[screwcenter-9.4,-screwcenter-7.7],
				[-screwcenter+9.4,screwcenter+7.7],
				[-screwcenter+9.4,-screwcenter-7.7]]){
		translate(z)
			cylinder(r=1.6, h=130, center=true, $fn=12);
	}
	difference(){
		minkowski(){
			cube(size=[side-curve, side-curve, w], center=true);
			cylinder(r=curve, h=.1, $fn=20);
		}
	//translate([29,20,0])
	//	cube(size=[4, 2, 2], center=true);
	echo("realside = ", realside);
	}
}

module nut_insert(nutrad=6.15/2, nutheight=2.6, into=6, direction=-1) {
// slide in captured nuts
	hull(){
		cylinder(r=nutrad, h=nutheight, center=true, $fn=6);
		translate([into*direction,0, 0])
			cylinder(r=nutrad, h=nutheight, center=true, $fn=6);
		}
}

module forbearance(screw=screwcenter+4.75){
// bearings cut into four corners
	for (z = [[screw,screw],
				[screw,-screw],
				[-screw,screw],
				[-screw,-screw]]){
		translate(z) translate([0,0,-15.9])
			z608z();
		// m3 bolts through 608zz centers
		translate(z) translate([0,0,-15.9])
			cylinder(r=1.6, h=30, center=true, $fn=12);
		translate(z) translate([0,0,0])
			cylinder(r=2.65, h=22, center=true, $fn=12);
		translate(z) translate([0,0,-26]){
			cylinder(r=3.1, h=2.5, center=true, $fn=6);
		translate([3*(z[0]/z[1]),0,0])
			cylinder(r=3.6, h=2.5, center=true, $fn=6);
		translate([-3*(z[0]/z[1]),0,0])
			cylinder(r=3.6, h=2.5, center=true, $fn=6);
		}
	}
}
//small switch
module contact_switch(len=13.4, dp=5.4, ht=6.4, blade_ht=65, bladew=1.8, bladethk=1){
	cube([len, dp, ht], center=false);
	for (i=[.5, 5, 10.5]){
		translate([i+bladethk/2, dp/2, ht+blade_ht/2])
			cube([bladethk, bladew, blade_ht], center=true);
	}
}

// main section
module main(baselevel=-46.8){
	difference(){
		translate([0,outextendo/2,-18])
		union(){
		// the big box
			minkowski(){
			cube([outsidebox,outsidebox+outextendo,60], center=true);
			cylinder(r=2, h=.1, $fn=20);
		}
		translate([-bx, by*.6, bz/2+4.5]) scale([.4,1.8,1])
			cylinder(r=8.8, h=26, center=true, $fn=64);
		translate([bx, by*.6, bz/2+4.5]) scale([.4,1.8,1])
			cylinder(r=8.8, h=26, center=true, $fn=64);
		//translate([0, -by*1.48, bz/2+3]) scale([1.8,.4,1])
		//	cylinder(r=9, h=26, center=true, $fn=64);
		//translate([0,33.15/2,-12]) rotate([0,90,0])
		//	cylinder(r=screwd, h=71, center=true, $fn=20);
		//translate([0,0,-12]) rotate([0,90,0])
		//	cylinder(r=screwd, h=71, center=true, $fn=20);
		}
		fan();
		// groove for gear
		translate([0,0,-16])
			ring(fanrad*2+5, fanrad*2-.1, gearheight);
		// m3bearing bolt-head access

		// 4 pin holes for flap control
		translate([0,0,-17-lowknob]) rotate([0,90,45])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		translate([0,0,-17-lowknob]) rotate([0,90,-45])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		translate([0,0,-21.5]) rotate([0,90,51])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		translate([0,0,-21.5]) rotate([0,90,-39])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		// can is gripped here
		translate([0,0,-38])
			ring(64.75, 63, 25);
		// taper to can
		translate([0,0,-45])
			cylinder(r1=62.2/2, r2=fanrad, 12, center=true, $fn=64);
		// linear bearing holders
		translate([-bx, by*.8, bz])
			LMB6mm(rodlen=120);
		translate([bx, by*.8, bz])
			LMB6mm(rodlen=120);
		// captured nuts for attachments
		translate([-screwcenter, -(outsidebox-fanrad)+3, 0])
			rotate([90,0,0]) m3nut();
		translate([screwcenter, -(outsidebox-fanrad)+3, 0])
			rotate([90,0,0]) m3nut();
		translate([0, -(outsidebox-fanrad)+3, 0])
			rotate([90,0,0]) m3nut();
		//switches man, switches
		translate([-29, (outsidebox+outextendo)/2+8, -48.1])
			contact_switch();
		translate([8, (outsidebox+outextendo)/2-9, -48.1]) rotate([0,0,-7])
			contact_switch();
		translate([-8, -(outsidebox)/2-5.5, -48.1]) rotate([0,0,0])
			contact_switch();
		// ring mount bearings
		forbearance();
		// stepper for turning ring
		//translate([0,52,-11]) rotate([90,0,0])
		//	small_stepper();
		// tiny gearmotor
		translate([0,34.7,-1]) rotate([0,-90,90])
			gearmotor();
		// spool mount hole for thread-drive of ring
		translate([0,outsidebox/2+2.5, -16.0]) rotate([0,0,0])
			cylinder(r=4.68, h=7, center=true, $fn=128);
		// thread passageway
		translate([0,outsidebox/2+2.5, -16.3]) rotate([0,90,0])
			cylinder(r=.7, h=60, center=true, $fn=6);
	}
/*
// flaps for valve
translate([sprd, -sprd, baselevel]) rotate([180,0,0])
	flaps();
translate([sprd, sprd, baselevel]) rotate([180,0,90])
	flaps();
translate([-sprd, sprd, baselevel]) rotate([180,0,180])
	flaps();
translate([-sprd, -sprd, baselevel]) rotate([180,0,-90])
	flaps();
*/
// knobs for valve leaves
for (i = [45,-45,135,-135]){
	translate([.5*fanrad/sin(i),.5*fanrad/cos(180-i),-17.8-lowknob])
		nodule(rat=i);
	//translate([fanrad*sin(i+40),fanrad*cos(180-(i+40)),-13.7-lowknob])
	//	nodule(rat=(i+40));
// push_down the flaps as they rotate
	translate([fanrad*sin(i+40),fanrad*cos(180-(i+40)),-13.7-lowknob])
		sphere(r=3.2);
	}
}

module main_bottom(){
	difference(){
		main();
		translate([0,0+outextendo/2,-.1])
			cube([100,100+outextendo,fanheight], center=true);
	}
}

module main_top(){
translate([(outsidebox+10), +29, 26/2])
	difference(){
		main();
		translate([0,0+outextendo/2,-42.4])
			cube([100,100+outextendo,60], center=true);
	}
}
module inner_ring(){
// inner ring for printing
translate([outsidebox+3, +outextendo, 0])
	difference(){
		union(){
			// bottom bevel grabber
			cylinder(r1=fanrad+2.1, r2=fanrad+1.8, h=.5, center=false, $fn=128);
			// mid-section w/ thread
			translate([0,0,0.5])
				cylinder(r1=fanrad+1.8, r2=fanrad+1.4, h=3, center=false, $fn=128);
			translate([0,0,3.5])
				cylinder(r1=fanrad+1.4, r2=fanrad+1.8, h=3, center=false, $fn=128);
			// top bevel grabber
			translate([0,0,6.5])
				cylinder(r1=fanrad+1.8, r2=fanrad+2.1, h=.5, center=false, $fn=128);
		}
		// cut out from center
		translate([0,0,-0.1])
			cylinder(r1=fanrad-.6, r2=fanrad-.1, h=7.15, center=false, $fn=128);
		// wire holder holes
		translate([0,0,.9]) rotate([0,90,0])
			cylinder(r=pinrad, h=(fanrad+1)*2, center=true, $fn=6);
		translate([0,0,.9]) rotate([90,0,0])
			cylinder(r=pinrad, h=(fanrad+1)*2, center=true, $fn=6);
	}
}

module print_gasket(screw=screwcenter+4.75){
translate([(outsidebox+8), -(outsidebox+5), 21])
	difference(){
		main_bottom();

		translate([0,0+outextendo/2,-50])
			cube([100,100+outextendo,58], center=true);
		translate([0,0+outextendo/2,-10])
			cube([100,100+outextendo,15.5], center=true);
		translate([0,0+outextendo/2,-10])
			cube([100,100+outextendo,15.5], center=true);
		for (z = [[screw,screw],
				[screw,-screw],
				[-screw,screw],
				[-screw,-screw]]){
			// m3 hex-bolt-heads
			translate(z) translate([0,0,-21.75+1.5])
				cylinder(r=2.75, h=3, center=true, $fn=12);
		}
	}
}
module can_holder(){
translate([0, 0, 23+(50/2)])
	difference(){
	translate([0,-(outsidebox+5),0])
		main_bottom();
	translate([0,-(outsidebox+5)+outextendo/2,-16])
		cube([70,70+outextendo+10,12], center=true);
	}
}

module ring_holder(){
translate([0,0+outextendo+1,22])
	difference(){
		main_bottom();
		translate([0,0+outextendo/2,-50])
			cube([100,100+outextendo,56], center=true);
	}
}

module spool(){
// round thread puller for motor attach
//first some spacers for flap-wires
translate([15,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([-15,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([-10,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([10,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([0,-10+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([0,10+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
	// remember to adjust socket in main if changing radius here
translate([0,+outextendo,7/2]){
	difference(){
	translate([0,0,-7/2]) union(){
	cylinder(r1=4.5,r2=4.2, h=1.5, center=false, $fn=128);
	translate([0,0,1.5])
		cylinder(r1=4.2,r2=4.65, h=1.5, center=false, $fn=128);
	translate([0,0,3])
		cylinder(r1=4.65,r2=4.3, h=2, center=false, $fn=128);
	translate([0,0,5])
		cylinder(r1=4.3,r2=4.65, h=2, center=false, $fn=128);
			}
		// nub insert for gear-motor
		translate([0,0,(7-5.25)/2]){
			difference(){
				cylinder(r=3.63, h=5.3, center=true, $fn=64);
				translate([3.57,0,0])
					cube([(3.57-4.9/2)*2, 5.4, 6], center=true);
				translate([-3.57,0,0])
					cube([(3.57-4.9/2)*2, 5.4, 6], center=true);
			}
		}
		cylinder(r=1, h=8, center=true, $fn=12);
		translate([0,0,1.35]) rotate([0,90,0])
			cylinder(r=.6, h=10, center=true, $fn=12);
		translate([0,0,0])
			cube([5.7,.4,3.7], center=true);
	}
}}

// samples for export
//rotate([0,180,0]) translate([0,0,-fanheight])
//	main_top();

//print_gasket();

//inner_ring();

//spool();
//rotate([0,0,-90]) translate([50,20,0])
//	can_holder();

//ring_holder();

m3nut(capture_channel=50, channel_dir=13);
//pusher_down();

//contact_switch();

// sample bearing
//z608z();

use <small_step.scad>

echo(version=version());
echo("main picker assembly");

fanrad = 57/2;
fanheight = 25;
screwcenter = 48.5/2;
// actual box outside is 2mm bigger due to curve
outsidebox = 64.5;
outextendo = 25.5;
screwd = 1.6;
gearheight = 7.1;
pinrad = .865;
// bearing placements
bx=28;
by=30+outextendo;
bz=20;
// flaps for airflow valve
flapthick = 1.2;
truss = 9;		// knobby bit on flap
sprd = .6;
knobrad = 1.2;		// on side of chamber
lowknob = 9.5 + knobrad;  // distance from wire

module gearmotor(l=25, w=12, h=10, r=3
){
	cube([l,w,h], center=true);
	translate([-5,0,0]) rotate([0,90,0])
		cylinder(r=r, h=l+10+1, center=true, $fn=16);
	translate([l/2,0,0]) rotate([0,90,0])
		cylinder(r=1, h=w, center=true, $fn=16);
	translate([l/2,0,0]) rotate([90,0,0])
		cylinder(r=1.5, h=65, center=true, $fn=16);

}

module flapattach(ra=3, t=flapthick, trusslen=truss){
// the little bit going from rod to flap
	difference(){
	// disk-hull-disk
	hull(){
		union(){
			cylinder(r1=ra-1, r2=ra, h=t, center=true, $fn=36);
			translate([0,0,t])
				cylinder(r1=ra, r2=ra-1, h=t, center=true, $fn=36);
		}
		translate([trusslen,0,0])
		 union(){
			 cylinder(r1=ra-1, r2=ra, h=t, center=true, $fn=36);
			 translate([0,0,t])
				 cylinder(r1=ra, r2=ra-1, h=t, center=true, $fn=36);
		 }
		} // end of disk-hull-disk
	// hole for metal pin or wire (to swivel on)
	cylinder(r=pinrad+.1, h=t*6, center=true, $fn=12);
	}
}

module flap(rd=fanrad-1, t=flapthick, trs=truss){
// make quarter-section of stopper
	difference(){
		union(){
		difference(){
			cylinder(r=rd, h=t, center=false, $fn=128);
			translate([rd/2, -rd/2, t/2])
				cube([rd,rd,t*2], center=true);
			translate([-rd/2, -rd/2, t/2])
				cube([rd,rd,t*2], center=true);
			translate([-rd/2, rd/2, t/2])
				cube([rd,rd,t*2], center=true);
		}
		translate([0, trs, -6]) rotate([90,-60,0])
			flapattach();
		translate([0, rd-trs, -6]) rotate([90,-60,0])
			flapattach();
		}
	translate([rd/2, rd/2, t*3])
		cube([rd*2,rd,t*4], center=true);
	}
}

module flaps(){
	difference(){
		flap();
		rotate([0,0,-90])
			flap();
	}
}

module nodule(rat=45){
// cam for valve leaves
	rotate([0,0,rat]) scale([2,1,.5])
		sphere(r=2.42, $fn=64);
}

// nut at end of bolt for cut-outs
module m3nut(d=6.4, h=2.7, rodd=3.1, rodlen=19, hdled=3, hdrad=6/2, capture_channel=20, channel_dir=90, fat=.1){
	translate([0,0,-rodlen/2+h/2]){
		cylinder(r=d/2, h=h, center=true, $fn=6);
		//translate([rad*cos(i), rad*sin(i), lift]) rotate([0,0,i])
		translate([(capture_channel/2)*cos(channel_dir-90), (capture_channel/2)*sin(channel_dir-90), 0]) rotate([0,0,channel_dir])
			cube([d, capture_channel, h], center=true);
	}
	// head of bolt is hexxed even if round
	translate([0,0,rodlen/2 - hdled/2])
		cylinder(r=hdrad, h=hdled, center=true, $fn=6);

	// threaded part
	cylinder(r=rodd/2+fat, h=rodlen, center=true, $fn=12);
}

// 608zz bearing mount cutter
module z608z(mntrad=7.45/2, ht=7, innermnt=2.35, race=2.6, edge=2.35){
	translate([0,0,0])
		union(){
			ring((innermnt+mntrad)*2, mntrad*2,  height=ht-.1);
			ring((innermnt+mntrad+race)*2, (innermnt+mntrad)*2-.1,  height=ht-.25);
			ring((innermnt+mntrad+race+edge)*2, (innermnt+mntrad+race)*2-.1,  height=ht+.1);
			// thread-groove cutter:
			ring((innermnt+mntrad+race+edge)*2+.35, (innermnt+mntrad+race+edge)*2-.1, height=.7);
		}
}

// linear bearing for 6mm w/ rod sticking through it
module LMB6mm(OD=12, OD_groove=11.41, ID=6, len=18.86, stripe=1.25, stripe_to_end=2.66, rodlen=100){
difference(){
	union(){
		// body of bearing, stacked up
		translate([0,0,-.05])
			cylinder(r=OD/2, h=stripe_to_end+.1, $fn=64);
		translate([0,0,stripe_to_end])
			cylinder(r=OD_groove/2, h=stripe+.05, $fn=48);
		translate([0,0,stripe_to_end+stripe])
			cylinder(r=OD/2, h=len-stripe*2-stripe_to_end*2+.05, $fn=64);
		translate([0,0,len-stripe-stripe_to_end])
			cylinder(r=OD_groove/2, h=stripe+.05, $fn=48);
		translate([0,0,len-stripe_to_end])
			cylinder(r=OD/2, h=stripe_to_end+.05, $fn=64);
		// transition for printing upright
		translate([0,0,len])
			cylinder(r=OD/2-1.1, h=stripe/2, $fn=64);
		}
	// hole for rod
	cylinder(r=ID/2, h=stripe_to_end, $fn=64);
}
// rod stand-in for use in cuts
cylinder(r=ID/2, h=rodlen, center=true, $fn=36);
}

module ring(rOD, rID, height)
{
difference(){
	cylinder(r=rOD/2, h=height, center=true, $fn=96);
	cylinder(r=rID/2, h=height, center=true, $fn=96);
	}
}

module fan(side=57.2, w=fanheight, curve=3, screwcenter=screwcenter){
// box fan 60mm sqr nominal x 25mm high + 50mm OD air hole
// fan is a tad bigger here to allow for shrink
	realside = curve*2 + side;
	oldz = 1;
	translate([29,18,0])
		cube(size=[7, 2, 22], center=true);
	translate([38,18,5])
		cube(size=[35, 2, 5], center=true);
	translate([0,0,-32.4])
		cylinder(r=fanrad, h=40, center=true, $fn=64);
	for (z = [[screwcenter-9.4,screwcenter+7.7],
				[screwcenter-9.4,-screwcenter-7.7],
				[-screwcenter+9.4,screwcenter+7.7],
				[-screwcenter+9.4,-screwcenter-7.7]]){
		translate(z)
			cylinder(r=1.6, h=130, center=true, $fn=12);
	}
	difference(){
		minkowski(){
			cube(size=[side-curve, side-curve, w], center=true);
			cylinder(r=curve, h=.1, $fn=20);
		}
	//translate([29,20,0])
	//	cube(size=[4, 2, 2], center=true);
	echo("realside = ", realside);
	}
}

module nut_insert(nutrad=6.15/2, nutheight=2.6, into=6, direction=-1) {
// slide in captured nuts
	hull(){
		cylinder(r=nutrad, h=nutheight, center=true, $fn=6);
		translate([into*direction,0, 0])
			cylinder(r=nutrad, h=nutheight, center=true, $fn=6);
		}
}

module forbearance(screw=screwcenter+4.75){
// bearings cut into four corners
	for (z = [[screw,screw],
				[screw,-screw],
				[-screw,screw],
				[-screw,-screw]]){
		translate(z) translate([0,0,-15.9])
			z608z();
		// m3 bolts through 608zz centers
		translate(z) translate([0,0,-15.9])
			cylinder(r=1.6, h=30, center=true, $fn=12);
		translate(z) translate([0,0,0])
			cylinder(r=2.65, h=22, center=true, $fn=12);
		translate(z) translate([0,0,-26]){
			cylinder(r=3.1, h=2.5, center=true, $fn=6);
		translate([3*(z[0]/z[1]),0,0])
			cylinder(r=3.6, h=2.5, center=true, $fn=6);
		translate([-3*(z[0]/z[1]),0,0])
			cylinder(r=3.6, h=2.5, center=true, $fn=6);
		}
	}
}
//small switch
module contact_switch(len=13.4, dp=5.4, ht=6.4, blade_ht=65, bladew=1.8, bladethk=1){
	cube([len, dp, ht], center=false);
	for (i=[.5, 5, 10.5]){
		translate([i+bladethk/2, dp/2, ht+blade_ht/2])
			cube([bladethk, bladew, blade_ht], center=true);
	}
}

// main section
module main(baselevel=-46.8){
	difference(){
		translate([0,outextendo/2,-18])
		union(){
		// the big box
			minkowski(){
			cube([outsidebox,outsidebox+outextendo,60], center=true);
			cylinder(r=2, h=.1, $fn=20);
		}
		translate([-bx, by*.6, bz/2+4.5]) scale([.4,1.8,1])
			cylinder(r=8.8, h=26, center=true, $fn=64);
		translate([bx, by*.6, bz/2+4.5]) scale([.4,1.8,1])
			cylinder(r=8.8, h=26, center=true, $fn=64);
		//translate([0, -by*1.48, bz/2+3]) scale([1.8,.4,1])
		//	cylinder(r=9, h=26, center=true, $fn=64);
		//translate([0,33.15/2,-12]) rotate([0,90,0])
		//	cylinder(r=screwd, h=71, center=true, $fn=20);
		//translate([0,0,-12]) rotate([0,90,0])
		//	cylinder(r=screwd, h=71, center=true, $fn=20);
		}
		fan();
		// groove for gear
		translate([0,0,-16])
			ring(fanrad*2+5, fanrad*2-.1, gearheight);
		// m3bearing bolt-head access

		// 4 pin holes for flap control
		translate([0,0,-17-lowknob]) rotate([0,90,45])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		translate([0,0,-17-lowknob]) rotate([0,90,-45])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		translate([0,0,-21.5]) rotate([0,90,51])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		translate([0,0,-21.5]) rotate([0,90,-39])
			cylinder(r=pinrad, h=fanrad*2+10, center=true, $fn=5);
		// can is gripped here
		translate([0,0,-38])
			ring(64.75, 63, 25);
		// taper to can
		translate([0,0,-45])
			cylinder(r1=62.2/2, r2=fanrad, 12, center=true, $fn=64);
		// linear bearing holders
		translate([-bx, by*.8, bz])
			LMB6mm(rodlen=120);
		translate([bx, by*.8, bz])
			LMB6mm(rodlen=120);
		// captured nuts for attachments
		translate([-screwcenter, -(outsidebox-fanrad)+3, 0])
			rotate([90,0,0]) m3nut();
		translate([screwcenter, -(outsidebox-fanrad)+3, 0])
			rotate([90,0,0]) m3nut();
		translate([0, -(outsidebox-fanrad)+3, 0])
			rotate([90,0,0]) m3nut();
		//switches man, switches
		translate([-29, (outsidebox+outextendo)/2+8, -48.1])
			contact_switch();
		translate([8, (outsidebox+outextendo)/2-9, -48.1]) rotate([0,0,-7])
			contact_switch();
		translate([-8, -(outsidebox)/2-5.5, -48.1]) rotate([0,0,0])
			contact_switch();
		// ring mount bearings
		forbearance();
		// stepper for turning ring
		//translate([0,52,-11]) rotate([90,0,0])
		//	small_stepper();
		// tiny gearmotor
		translate([0,34.7,-1]) rotate([0,-90,90])
			gearmotor();
		// spool mount hole for thread-drive of ring
		translate([0,outsidebox/2+2.5, -16.0]) rotate([0,0,0])
			cylinder(r=4.68, h=7, center=true, $fn=128);
		// thread passageway
		translate([0,outsidebox/2+2.5, -16.3]) rotate([0,90,0])
			cylinder(r=.7, h=60, center=true, $fn=6);
	}
/*
// flaps for valve
translate([sprd, -sprd, baselevel]) rotate([180,0,0])
	flaps();
translate([sprd, sprd, baselevel]) rotate([180,0,90])
	flaps();
translate([-sprd, sprd, baselevel]) rotate([180,0,180])
	flaps();
translate([-sprd, -sprd, baselevel]) rotate([180,0,-90])
	flaps();
*/
// knobs for valve leaves
for (i = [45,-45,135,-135]){
	translate([.5*fanrad/sin(i),.5*fanrad/cos(180-i),-17.8-lowknob])
		nodule(rat=i);
	//translate([fanrad*sin(i+40),fanrad*cos(180-(i+40)),-13.7-lowknob])
	//	nodule(rat=(i+40));
// push_down the flaps as they rotate
	translate([fanrad*sin(i+40),fanrad*cos(180-(i+40)),-13.7-lowknob])
		sphere(r=3.2);
	}
}

module main_bottom(){
	difference(){
		main();
		translate([0,0+outextendo/2,-.1])
			cube([100,100+outextendo,fanheight], center=true);
	}
}

module main_top(){
translate([(outsidebox+10), +29, 26/2])
	difference(){
		main();
		translate([0,0+outextendo/2,-42.4])
			cube([100,100+outextendo,60], center=true);
	}
}
module inner_ring(){
// inner ring for printing
translate([outsidebox+3, +outextendo, 0])
	difference(){
		union(){
			// bottom bevel grabber
			cylinder(r1=fanrad+2.1, r2=fanrad+1.8, h=.5, center=false, $fn=128);
			// mid-section w/ thread
			translate([0,0,0.5])
				cylinder(r1=fanrad+1.8, r2=fanrad+1.4, h=3, center=false, $fn=128);
			translate([0,0,3.5])
				cylinder(r1=fanrad+1.4, r2=fanrad+1.8, h=3, center=false, $fn=128);
			// top bevel grabber
			translate([0,0,6.5])
				cylinder(r1=fanrad+1.8, r2=fanrad+2.1, h=.5, center=false, $fn=128);
		}
		// cut out from center
		translate([0,0,-0.1])
			cylinder(r1=fanrad-.6, r2=fanrad-.1, h=7.15, center=false, $fn=128);
		// wire holder holes
		translate([0,0,.9]) rotate([0,90,0])
			cylinder(r=pinrad, h=(fanrad+1)*2, center=true, $fn=6);
		translate([0,0,.9]) rotate([90,0,0])
			cylinder(r=pinrad, h=(fanrad+1)*2, center=true, $fn=6);
	}
}

module print_gasket(screw=screwcenter+4.75){
translate([(outsidebox+8), -(outsidebox+5), 21])
	difference(){
		main_bottom();

		translate([0,0+outextendo/2,-50])
			cube([100,100+outextendo,58], center=true);
		translate([0,0+outextendo/2,-10])
			cube([100,100+outextendo,15.5], center=true);
		translate([0,0+outextendo/2,-10])
			cube([100,100+outextendo,15.5], center=true);
		for (z = [[screw,screw],
				[screw,-screw],
				[-screw,screw],
				[-screw,-screw]]){
			// m3 hex-bolt-heads
			translate(z) translate([0,0,-21.75+1.5])
				cylinder(r=2.75, h=3, center=true, $fn=12);
		}
	}
}
module can_holder(){
translate([0, 0, 23+(50/2)])
	difference(){
	translate([0,-(outsidebox+5),0])
		main_bottom();
	translate([0,-(outsidebox+5)+outextendo/2,-16])
		cube([70,70+outextendo+10,12], center=true);
	}
}

module ring_holder(){
translate([0,0+outextendo+1,22])
	difference(){
		main_bottom();
		translate([0,0+outextendo/2,-50])
			cube([100,100+outextendo,56], center=true);
	}
}

module spool(){
// round thread puller for motor attach
//first some spacers for flap-wires
translate([15,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([-15,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([-10,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([10,+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([0,-10+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
translate([0,10+outextendo,0])
	difference(){
		cylinder(r=pinrad+1, h=12, center=false, $fn=18);
		cylinder(r=pinrad+.1, h=12.1, center=false, $fn=18);
	}
	// remember to adjust socket in main if changing radius here
translate([0,+outextendo,7/2]){
	difference(){
	translate([0,0,-7/2]) union(){
	cylinder(r1=4.5,r2=4.2, h=1.5, center=false, $fn=128);
	translate([0,0,1.5])
		cylinder(r1=4.2,r2=4.65, h=1.5, center=false, $fn=128);
	translate([0,0,3])
		cylinder(r1=4.65,r2=4.3, h=2, center=false, $fn=128);
	translate([0,0,5])
		cylinder(r1=4.3,r2=4.65, h=2, center=false, $fn=128);
			}
		// nub insert for gear-motor
		translate([0,0,(7-5.25)/2]){
			difference(){
				cylinder(r=3.63, h=5.3, center=true, $fn=64);
				translate([3.57,0,0])
					cube([(3.57-4.9/2)*2, 5.4, 6], center=true);
				translate([-3.57,0,0])
					cube([(3.57-4.9/2)*2, 5.4, 6], center=true);
			}
		}
		cylinder(r=1, h=8, center=true, $fn=12);
		translate([0,0,1.35]) rotate([0,90,0])
			cylinder(r=.6, h=10, center=true, $fn=12);
		translate([0,0,0])
			cube([5.7,.4,3.7], center=true);
	}
}}

// samples for export
//rotate([0,180,0]) translate([0,0,-fanheight])
//	main_top();

//print_gasket();

//inner_ring();

//spool();
//rotate([0,0,-90]) translate([50,20,0])
//	can_holder();

//ring_holder();

m3nut(capture_channel=50, channel_dir=13);
//pusher_down();

//contact_switch();

// sample bearing
//z608z();