projector

projector

module projector(width, depth, height, feetHeight, lensDiameter, lensX, lensY) {
  color("White")
    difference() {
      translate([0, 0, feetHeight])
        cube([width, depth, height]);
      rotate([90,0,90])
        translate([lensX, lensY, -2])
          cylinder(20, lensDiameter, lensDiameter / 3 * 2);
    }
}

module pyramid(w, l, h) {
  mw = w/2;
  ml = l/2;
  polyhedron(points = [
    [0,  0,  0],
    [w,  0,  0],
    [0,  l,  0],
    [w,  l,  0],
    [mw, ml, h]
  ], faces = [
    [4, 1, 0],
    [4, 3, 1],
    [4, 2, 3],
    [4, 0, 2],
    //base
    [0, 1, 2],
    [2, 1, 3]
  ]);
}

module projection(center, distToWidth, widthToHeight, dist) {
  w = distToWidth * dist;
  h = widthToHeight * w;
  rotate([0,90,0])
    translate([-w/2,-h/2,-dist])
      translate(center)
        pyramid(w, h, dist);
}

module Acer_H6510BD(showProjection=true) {
  feetHeight = 8; // ~
  height = 78;
  lensDiameter = 30; // ~
  lensX = 55; // ~
  lensY = feetHeight + height / 2;
  projector(264, 220, height, feetHeight, lensDiameter, lensX, lensY);
  if(showProjection) {
    distToWidth = 192/225;
    widthToHeight = 16/9;
    dist = 225;
    color([0.75,1,1,0.5])
      projection([-lensY,lensX,0], distToWidth, widthToHeight, dist);
  }
}

Acer_H6510BD();

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


minor alignment updated, for visualization

use <support.scad>
use <wallMount.scad>

module bothElements()
{
  translate([0, 55, 0])
    support();
  wallMount();
}

module closedPosition()
{
  wallMount();
  translate([150, 0, 16])
    rotate([0, 180, 0])
      support();
}

module openPosition()
{
  wallMount();
  translate([150, -4, 4])
    rotate([-90, 180, 0])
      support();
}

//%closedPosition();
//%openPosition();

rotate([0, 0, 90])
  bothElements();

use <support.scad>
use <wallMount.scad>

module bothElements()
{
  translate([0, 55, 0])
    support();
  wallMount();
}

module closedPosition()
{
  wallMount();
  translate([150, 0, 14.5])
    rotate([0, 180, 0])
      support();
}

module openPosition()
{
  wallMount();
  translate([150, -4, 4])
    rotate([-90, 180, 0])
      support();
}

//%closedPosition();
//%openPosition();

rotate([0, 0, 90])
  bothElements();


final frame code is now ready

final frame code is now ready

eps = 0.01;
wall = 2;
spacing = 1;

module model_import(file)
{
  offset(eps)
    import(file);
}

module shape_placeholder()
{
  linear_extrude(2*wall)
    model_import("random_shape.svg");
}

module model_slot_base(name)
{
  module extended_base(r)
  {
    minkowski()
    {
      model_import(name);
      circle(r);
    }
  }

  module frame(wall)
  {
    difference()
    {
      extended_base(spacing + wall/2 + 0.5);
      extended_base(spacing + wall/2);
    }
  }

  module cut_in_frame()
  {
    difference()
    {
      // model
      frame(wall);
      // side wall cuts
      children();
    }
  }

  cut_in_frame()
    children();
}

module model_slot(name)
{
  intersection()
  {
    minkowski()
    {
      linear_extrude(2.7)
        model_slot_base(name)
          children();
      sphere(r=wall/2);
    }
    // cut-off for the bottom roundings
    translate([-50, -50, 0])
      cube(300*[1,1,1]);
  }
}

%shape_placeholder();
model_slot("random_shape.svg")
{
  // some location(s) that shall be removed from the overall shape:
  // bottom
  translate([40, -10, 0])
    square([60, 20]);
  // top
  translate([60, 110, 0])
    square([50, 20]);
}

eps = 0.01;
wall = 2;
spacing = 1;

module model_import(file)
{
  offset(eps)
    import(file);
}

module shape_placeholder()
{
  linear_extrude(2*wall)
    model_import("random_shape.svg");
}

module model_slot_base(name)
{
  module extended_base(r)
  {
    minkowski()
    {
      model_import(name);
      circle(r);
    }
  }

  module frame(wall)
  {
    difference()
    {
      extended_base(spacing + wall/2 + 0.5);
      extended_base(spacing + wall/2);
    }
  }

  module cut_in_frame()
  {
    difference()
    {
      // model
      frame(wall);
      // side wall cuts
      children(0);
    }
  }

  cut_in_frame()
    children(0);
}

module model_slot(name)
{
  intersection()
  {
    minkowski()
    {
//      linear_extrude(2.7)
        model_slot_base(name)
          children(0);
//      sphere(r=wall/2);
    }
    // cut-off for the bottom roundings
    #translate([-50, -50, 0])
      cube([200, 200, 100]);
  }
}

%shape_placeholder();
model_slot("random_shape.svg")
{
  translate([0, 40, 0])
    square([70, 20]);
}


use original x,y locations

use original x,y locations

/**
  * This version is a replica of the one from 3D_Lover found on Thingiverse.
  *
  *     https://www.thingiverse.com/thing:1746053
  */

use <../../half-circle-phone-stand.scad>

//difference()
{
//    import("/home/roberto/Versioning/world/betoland/electronics/phone-stand/half-circle/3D_Lover/files/Part1.STL",
//           convexity=10);

    xTranslate = 7.65;
    yTranslate = 6;

    translate([xTranslate, yTranslate, 0])
    halfCirclePhoneStand(base_xTranslate = 0,
                         base_yTranslate = 0,
                         stand_top_xTranslate = 8.105,
                         stand_top_yTranslate = 8.85);
}

/**
  * This version is a replica of the one from 3D_Lover found on Thingiverse.
  *
  *     https://www.thingiverse.com/thing:1746053
  */

use <../../half-circle-phone-stand.scad>

//difference()
{
//    import("/home/roberto/Versioning/world/betoland/electronics/phone-stand/half-circle/3D_Lover/files/Part1.STL",
//           convexity=10);

    xTranslate = 7.65;
    yTranslate = 6;

    translate([xTranslate, yTranslate, 0])
    halfCirclePhoneStand();
}


simple schema for printing multiple instances

use <rollup_string_fix.scad>
use <rollup_string_limiter.scad>

// required quantities. adjust according to your needs.
// (note: you need two identical elements per functional part)
fixes = 2*2;
limiters = 3*2;

// ball-size
r=(3+2)/2;

for(i = [0:fixes-1])
  translate([0, 10*i, 0])
    rollup_string_fix(r);

for(i = [0:limiters-1])
  translate([20, 10*i, 0])
    rollup_string_limiter(r);

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


[scad] go for a hollow support

[scad] go for a hollow support

/*
	kube.scad - OpenSCAD code generating the Fluksokube enclosure

	Copyright (C) 2014 Bart Van Der Meerssche <bart@flukso.net>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

$fn = 100; /* number of facets per circle */

/*
	_h = height
	_w = width
	_r = radius
	_g = gap
	_t = tolerance
	_s = scaling factor (printer specific)
*/

dim_s = 1;
dim_s = 1.015; /* makerbot */
kube_s = [dim_s, dim_s, dim_s];

magnet_h = 5;
magnet_t = 0.1;
magnet_r = 8 - magnet_t; /* inner diameter now! */

flk_h = 23.5;
flk_t = 0.2;
flk_w = 30.5 + 2*flk_t;

wall_w = 1;
outer_w = flk_w + 2*wall_w;

support_h = outer_w - wall_w - flk_h;
support_r = magnet_r;
support_w = 1;
support_g = 3;

ring_h = support_h - magnet_h;
ring_t = 0.2;
ring_inner_r = support_r + ring_t;
ring_outer_r = ring_inner_r + 2;

echo("*** kube dimensions ***");
echo("scaling vector", "=", kube_s);
echo("magnet diameter", "=", magnet_r*2);
echo("flk width", "=", flk_w);
echo("kube width", "=", outer_w);
echo("support diameter", "=", support_r*2);
echo("support height", "=", support_h);
echo("ring height", "=", ring_h);
echo("ring inner diameter", "=", ring_inner_r*2);
echo("ring outer diameter", "=", ring_outer_r*2);
echo("***********************");

module kube()
{
	difference() {
		cube(outer_w);
		translate([wall_w, wall_w, wall_w]) {
			cube([flk_w, flk_w, outer_w - wall_w]);
		}
	}

	translate([outer_w/2, outer_w/2, wall_w]) {
		rotate(a=[0, 0, 45]) difference() {
			cylinder(r=support_r, h=support_h);
			cylinder(r=support_r - support_w, h=support_h);
			translate([-support_r, -support_g/2, 0]) cube([support_r*2, support_g, support_h]);
			translate([-support_g/2, -support_r, 0]) cube([support_g, support_r*2, support_h]);
		}
	}
}

module ring()
{
	difference() {
		cylinder(r=ring_outer_r, h=ring_h);
		cylinder(r=ring_inner_r, h=ring_h);
	}
}

scale(kube_s) {
	kube();
	translate([1.5*outer_w, outer_w/2, 0]) ring();
}

/*
	kube.scad - OpenSCAD code generating the Fluksokube enclosure

	Copyright (C) 2014 Bart Van Der Meerssche <bart@flukso.net>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/*$fn = 100; /* number of facets per circle */

/*
	_h = height
	_w = width
	_r = radius
	_t = tolerance
	_s = scaling factor (printer specific)
*/

dim_s = 1;
dim_s = 1.015; /* makerbot */
kube_s = [dim_s, dim_s, dim_s];

magnet_h = 5;
magnet_t = 0.2;
magnet_r = 8 - magnet_t; /* inner diameter now! */

flk_h = 23.5;
flk_t = 0.2;
flk_w = 30.5 + 2*flk_t;

wall_w = 1;
outer_w = flk_w + 2*wall_w;

support_h = outer_w - wall_w - flk_h;
support_r = magnet_r;

ring_h = support_h - magnet_h;
ring_t = 0.2;
ring_inner_r = support_r + ring_t;
ring_outer_r = ring_inner_r + 2;

echo("*** kube dimensions ***");
echo("scaling vector", "=", kube_s);
echo("magnet diameter", "=", magnet_r*2);
echo("flk width", "=", flk_w);
echo("kube width", "=", outer_w);
echo("support diameter", "=", support_r*2);
echo("support height", "=", support_h);
echo("ring height", "=", ring_h);
echo("ring inner diameter", "=", ring_inner_r*2);
echo("ring outer diameter", "=", ring_outer_r*2);
echo("***********************");

module kube()
{
	difference() {
		cube(outer_w);
		translate([wall_w, wall_w, wall_w]) {
			cube([flk_w, flk_w, outer_w - wall_w]);
		}
	}

	translate([outer_w/2, outer_w/2, wall_w]) {
		cylinder(r=support_r, h=support_h);
	}
}

module ring()
{
	difference() {
		cylinder(r=ring_outer_r, h=ring_h);
		cylinder(r=ring_inner_r, h=ring_h);
	}
}

scale(kube_s) {
	kube();
	translate([1.5*outer_w, outer_w/2, 0]) ring();
}


customize the side tiles

customize the side tiles

leftTileOn = "Yes"; // [Yes, No]

leftTileIcon = "Mario";   // [Autobot, Decepticon, Luigi, Mario, Rebel, Trooper]

rightTileOn = "Yes"; // [Yes, No]

rightTileIcon = "Luigi";   // [Autobot, Decepticon, Luigi, Mario, Rebel, Trooper]

/* [Hidden] */

use <../face-plate-with-icons.scad>

facePlateWithIcons(leftTileIcon = leftTileIcon,
                   leftTileOn = leftTileOn,
                   rightTileIcon = rightTileIcon,
                   rightTileOn = rightTileOn);

error: pathspec 'openscad/models/src/main/openscad/microcontrollers/adafruit/trellis/face-plate-with-icons/cusomizer/face-plate-with-icons-customizer.scad' did not match any file(s) known to git


Parameter cleanup to match modified module signature.

// fishbox.scad
//  - Enclosure design for zebrafish Skinner box
//
//  Author: Mark Liffiton
//  Date: Oct, 2014 - Jan, 2015
//
//  Units are mm

// Set thickness to account for thickness of material
// PLUS needed clearance for cuts into which material should fit.

// 0.100" Acrylic:
window_thickness = 2.4;  // 2.4mm = 0.094" (looks good based on cut test piece)

// 1/8" Hardboard:
thickness = 3.1;  // 3.1mm = 0.122" (best guess for hardboard for now)

// Interior box dimensions
// The variables are used below to control placement of **centerpoints** centerpoints of walls,
// so material thickness is added to account for that.
width = 290 + thickness;    // x = 29cm wall-to-wall
depth = 348 + thickness;    // y = 34.8cm to accomodate 34.8cm tank length
i_height = 190 + thickness; // z = 19cm to accomodate 16cm tank height plus light holder, plus cover
                            // (i_height because 'height' is used for height of entire box)

// amount faces extend past each other at corners
overhang = 15;

// amount horizontal support tabs extend past side supports (technically, it will go this distance minus thickness/2)
outset = thickness+2;

// tank base plate
base_depth = depth-thickness*2;
base_height = overhang/2;

// create height var to account for raised base
height = i_height + base_height;

// light bar
light_bar_width = 30;
light_pos_x = 50;
light_height = height-thickness*2-8;

// hanging support thickness and drop (distance it "sits down" after in place)
support_w = 10;
support_drop = support_w/2;

// for slight offsets/tweaks
epsilon = 1;

// vars to control projection for DXF export (see makefile)
if (DXF_TOP) {
    projection() top_cover();
} else if (DXF_BOTTOM) {
    projection() tank_base();
} else if (DXF_SIDE1) {
    projection() rotate(a=[90,0,0]) side(y=0);
} else if (DXF_SIDE2) {
    projection() rotate(a=[90,0,0]) side(y=depth);
} else if (DXF_END1) {
    projection() rotate(a=[0,90,0]) vert_face(x=0);
} else if (DXF_END2) {
    projection() rotate(a=[0,90,0]) vert_face(x=width);
} else if (DXF_LIGHT_BAR) {
    projection() light_bar();
} else if (DXF_CAMERA_SUPPORT) {
    projection() rotate(a=[90,0,0]) camera_supports(justone=true);
} else if (DXF_RPI_SUPPORT) {
    projection() rotate(a=[90,0,0]) rpi_support();
}
else {
    // 3D model
    // components to include (comment out unwanted)
    vert_face(x=0);      // "near face, transparent for monitor
    vert_face(x=width);  // "far" face, with camera, rpi, etc.
    side(y=0);
    side(y=depth);
    tank_base();
    light_bar();
    camera_supports(x=width, y=depth/2, z=height/2);
    rpi_support(x=width, y=depth/2-50, z=height/2);
    mock_rpi(x=width, y=depth/2-60, z=height/2);
    top_cover();
}

//////////////////////////////////////////////////////////////////
// Module definitions
//

module vert_face(x=0) {
    difference() {
        if (x == 0) {
            vert_face_base(0, window_thickness);
        }
        else {
            vert_face_base(x);
            camera_opening();
            camera_supports(x=width+thickness*2, y=depth/2, z=height/2+support_drop);
            rpi_support(x=width+thickness*2, y=depth/2-50, z=height/2+support_drop);
        }
        // CAUTION: OpenSCAD won't let me make a projection of vert_face(x=0)
        //   if tank_base is put after side(y=depth) here... bug.  :(
        tank_base();
        top_cover();
        side(y=0);
        side(y=depth);
    }
}

module vert_face_base(x, face_thickness=thickness) {
    translate([x-face_thickness/2,-overhang, 0])
    difference() {
        cube([face_thickness,depth+overhang*2,height]);
        translate([0, depth+overhang+thickness/2, 3/4*height])
            rounded_truncation(up=true);
        translate([0, 0, 3/4*height])
            rounded_truncation(up=true);
    }
}

module rounded_truncation(up, rot=[0,0,0]) {
    real_overhang=overhang-thickness/2;
    truncation_height=height/4;
    rotate(a=rot)
    difference() {
        translate([-50,0,0])
            cube([100, real_overhang, truncation_height]);
        if (up) {
            translate([-50, real_overhang/2, 0])
            rotate(a=[0, 90, 0])
                cylinder(d=real_overhang, h=100);
        }
        else {
            translate([-50, real_overhang/2, truncation_height])
            rotate(a=[0, 90, 0])
                cylinder(d=real_overhang, h=100);
        }
    }
}

module camera_opening() {
    translate([width, depth/2, height/2])
        cube([thickness*2,10,10], center=true);
}

module camera_supports(x, y, z, justone=false) {
    spacing = 20;
    out = 10;
    up = 36;
    hanging_support(x, y + spacing/2, z, out, up);
    if (!justone) {
        hanging_support(x, y - spacing/2, z, out, up);
    }
}

module rpi_support(x, y, z) {
    hanging_support(x, y, z, out=3.2, up=85);
}

module hanging_support(x, y, z, out, up) {
    inner_w = out+thickness;
    inner_h = up;
    color("Red", alpha=0.5)
    translate([x,y,z])
    // push inner opening up against vert face
    translate([inner_w/2,0,0])
    difference() {
        cube([inner_w+support_w*2, thickness, inner_h+support_w*2], center=true);
        // cutout for object
        cube([inner_w, thickness*2, inner_h], center=true);
        // cutout for passing through vertface
        translate([-(inner_w/2+support_w/2),0,-support_drop/2])
            cube([support_w+2*epsilon, thickness*2, inner_h-support_drop], center=true);
        // bottom cutout
        translate([-inner_w/2,0,-(inner_h/2+support_w-support_drop/2)])
            cube([thickness, thickness*2, support_drop], center=true);
    }
}

module top_cover() {
    color("Grey", alpha=0.5)
    translate([-outset,-outset,height-thickness])
    difference() {
        cube([width+outset*2,depth+outset*2,thickness]);
        cutouts(5,width-thickness,outset,rot=0,trans=[width/2+outset,0,0]);
        cutouts(5,width-thickness,outset,rot=180,trans=[width/2+outset,depth+outset*2,0]);
        cutouts(5,depth-thickness,outset,rot=-90,trans=[0,depth/2+outset,0]);
        cutouts(5,depth-thickness,outset,rot=90,trans=[width+outset*2,depth/2+outset,0]);
    }
}

module tank_base() {
    translate([-outset,thickness,base_height])
    difference() {
        cube([width+outset*2, base_depth, thickness]);
        cutouts(5,depth,outset,rot=-90,trans=[0,base_depth/2,0]);
        cutouts(5,depth,outset,rot=90,trans=[width+outset*2,base_depth/2,0]);
    }
}

module light_bar_opening() {
    // A wider opening, so the bar can slide out of the way of placing or removing the fish tank
    translate([light_pos_x-light_bar_width/4, -outset, light_height])
        cube([light_bar_width*2, depth+outset*2, thickness]);
}

module light_bar() {
    translate([light_pos_x-light_bar_width/2, -outset, light_height])
    difference() {
        cube([light_bar_width, depth+outset*2, thickness]);
        cutouts(2,light_bar_width,outset,rot=0,trans=[light_bar_width/2,0,0]);
        cutouts(2,light_bar_width,outset,rot=180,trans=[light_bar_width/2,depth+outset*2,0]);
    }
}

module cutouts(num, width, outset, rot, trans) {
    w = width / ((num-1) * 2);
    translate(trans)
    rotate([0,0,1], a=rot)
    for (i = [0 : num-1]) {
        translate([-width/2 - w/2 + i*2*w, -thickness/2, -epsilon])
            cube([w, outset+thickness, thickness+epsilon*2]);
    }
}

module light_wire_opening() {
    translate([light_pos_x, 0, light_height-10])
    scale([2,1,1])
    rotate(v=[1,0,0], a=90)
        cylinder(d=10, h=thickness+epsilon, center=true);
}

module side(y=0) {
    difference() {
        side_base(y);
        scale([1,1,0.5])
            vert_face_base(0, window_thickness);
        scale([1,1,0.5])
            vert_face_base(width);
        light_bar_opening();
        light_wire_opening();
        top_cover();
    }
}

module side_base(y) {
    translate([-overhang,y-thickness/2,overhang/2])
    difference() {
        cube([width+overhang*2, thickness, height-overhang/2]);
        translate([width+overhang+thickness/2, 0, 0])
            rounded_truncation(up=false, rot=[0,0,-90]);
        translate([0, 0, 0])
            rounded_truncation(up=false, rot=[0,0,-90]);
    }
}

module mock_rpi(x, y, z) {
    color("Blue", alpha=0.5)
    translate([x+thickness, y, z])
        rotate(v=[0,1,0], a=90)
            translate([-85/2, -56/2, 0])
                union() {
                    cube([85, 56, 2]);
                    // USB1
                    translate([67, 41, 2])
                        cube([20, 12, 12]);
                    // USB2
                    translate([67, 23, 2])
                        cube([20, 12, 12]);
                }
}

// fishbox.scad
//  - Enclosure design for zebrafish Skinner box
//
//  Author: Mark Liffiton
//  Date: Oct, 2014 - Jan, 2015
//
//  Units are mm

// Set thickness to account for thickness of material
// PLUS needed clearance for cuts into which material should fit.

// 0.100" Acrylic:
window_thickness = 2.4;  // 2.4mm = 0.094" (looks good based on cut test piece)

// 1/8" Hardboard:
thickness = 3.1;  // 3.1mm = 0.122" (best guess for hardboard for now)

// Interior box dimensions
// The variables are used below to control placement of **centerpoints** centerpoints of walls,
// so material thickness is added to account for that.
width = 290 + thickness;    // x = 29cm wall-to-wall
depth = 348 + thickness;    // y = 34.8cm to accomodate 34.8cm tank length
i_height = 190 + thickness; // z = 19cm to accomodate 16cm tank height plus light holder, plus cover
                            // (i_height because 'height' is used for height of entire box)

// amount faces extend past each other at corners
overhang = 15;

// amount horizontal support tabs extend past side supports (technically, it will go this distance minus thickness/2)
outset = thickness+2;

// tank base plate
base_depth = depth-thickness*2;
base_height = overhang/2;

// create height var to account for raised base
height = i_height + base_height;

// light bar
light_bar_width = 30;
light_pos_x = 50;
light_height = height-thickness*2-8;

// hanging support thickness and drop (distance it "sits down" after in place)
support_w = 10;
support_drop = support_w/2;

// for slight offsets/tweaks
epsilon = 1;

// vars to control projection for DXF export (see makefile)
if (DXF_TOP) {
    projection() top_cover();
} else if (DXF_BOTTOM) {
    projection() tank_base();
} else if (DXF_SIDE1) {
    projection() rotate(a=[90,0,0]) side(y=0);
} else if (DXF_SIDE2) {
    projection() rotate(a=[90,0,0]) side(y=depth);
} else if (DXF_END1) {
    projection() rotate(a=[0,90,0]) vert_face(x=0);
} else if (DXF_END2) {
    projection() rotate(a=[0,90,0]) vert_face(x=width);
} else if (DXF_LIGHT_BAR) {
    projection() light_bar();
} else if (DXF_CAMERA_SUPPORT) {
    projection() rotate(a=[90,0,0]) camera_supports(justone=true);
} else if (DXF_RPI_SUPPORT) {
    projection() rotate(a=[90,0,0]) rpi_support();
}
else {
    // 3D model
    // components to include (comment out unwanted)
    vert_face(x=0);      // "near face, transparent for monitor
    vert_face(x=width);  // "far" face, with camera, rpi, etc.
    side(y=0);
    side(y=depth);
    tank_base();
    light_bar();
    camera_supports(x=width, y=depth/2, z=height/2);
    rpi_support(x=width, y=depth/2-50, z=height/2);
    mock_rpi(x=width, y=depth/2-60, z=height/2);
    top_cover();
}

//////////////////////////////////////////////////////////////////
// Module definitions
//

module vert_face(x=0) {
    difference() {
        if (x == 0) {
            vert_face_base(0, window_thickness);
        }
        else {
            vert_face_base(x);
            camera_opening();
            camera_supports(x=width+thickness*2, y=depth/2, z=height/2+support_drop);
            rpi_support(x=width+thickness*2, y=depth/2-50, z=height/2+support_drop);
        }
        // CAUTION: OpenSCAD won't let me make a projection of vert_face(x=0)
        //   if tank_base is put after side(y=depth) here... bug.  :(
        tank_base();
        top_cover();
        side(y=0);
        side(y=depth);
    }
}

module vert_face_base(x, face_thickness=thickness) {
    translate([x-face_thickness/2,-overhang, 0])
    difference() {
        cube([face_thickness,depth+overhang*2,height]);
        translate([0, depth+overhang+thickness/2, 3/4*height])
            rounded_truncation(up=true, rot=false);
        translate([0, 0, 3/4*height])
            rounded_truncation(up=true, rot=false);
    }
}

module rounded_truncation(up, rot=[0,0,0]) {
    real_overhang=overhang-thickness/2;
    truncation_height=height/4;
    rotate(a=rot)
    difference() {
        translate([-50,0,0])
            cube([100, real_overhang, truncation_height]);
        if (up) {
            translate([-50, real_overhang/2, 0])
            rotate(a=[0, 90, 0])
                cylinder(d=real_overhang, h=100);
        }
        else {
            translate([-50, real_overhang/2, truncation_height])
            rotate(a=[0, 90, 0])
                cylinder(d=real_overhang, h=100);
        }
    }
}

module camera_opening() {
    translate([width, depth/2, height/2])
        cube([thickness*2,10,10], center=true);
}

module camera_supports(x, y, z, justone=false) {
    spacing = 20;
    out = 10;
    up = 36;
    hanging_support(x, y + spacing/2, z, out, up);
    if (!justone) {
        hanging_support(x, y - spacing/2, z, out, up);
    }
}

module rpi_support(x, y, z) {
    hanging_support(x, y, z, out=3.2, up=85);
}

module hanging_support(x, y, z, out, up) {
    inner_w = out+thickness;
    inner_h = up;
    color("Red", alpha=0.5)
    translate([x,y,z])
    // push inner opening up against vert face
    translate([inner_w/2,0,0])
    difference() {
        cube([inner_w+support_w*2, thickness, inner_h+support_w*2], center=true);
        // cutout for object
        cube([inner_w, thickness*2, inner_h], center=true);
        // cutout for passing through vertface
        translate([-(inner_w/2+support_w/2),0,-support_drop/2])
            cube([support_w+2*epsilon, thickness*2, inner_h-support_drop], center=true);
        // bottom cutout
        translate([-inner_w/2,0,-(inner_h/2+support_w-support_drop/2)])
            cube([thickness, thickness*2, support_drop], center=true);
    }
}

module top_cover() {
    color("Grey", alpha=0.5)
    translate([-outset,-outset,height-thickness])
    difference() {
        cube([width+outset*2,depth+outset*2,thickness]);
        cutouts(5,width-thickness,outset,rot=0,trans=[width/2+outset,0,0]);
        cutouts(5,width-thickness,outset,rot=180,trans=[width/2+outset,depth+outset*2,0]);
        cutouts(5,depth-thickness,outset,rot=-90,trans=[0,depth/2+outset,0]);
        cutouts(5,depth-thickness,outset,rot=90,trans=[width+outset*2,depth/2+outset,0]);
    }
}

module tank_base() {
    translate([-outset,thickness,base_height])
    difference() {
        cube([width+outset*2, base_depth, thickness]);
        cutouts(5,depth,outset,rot=-90,trans=[0,base_depth/2,0]);
        cutouts(5,depth,outset,rot=90,trans=[width+outset*2,base_depth/2,0]);
    }
}

module light_bar_opening() {
    // A wider opening, so the bar can slide out of the way of placing or removing the fish tank
    translate([light_pos_x-light_bar_width/4, -outset, light_height])
        cube([light_bar_width*2, depth+outset*2, thickness]);
}

module light_bar() {
    translate([light_pos_x-light_bar_width/2, -outset, light_height])
    difference() {
        cube([light_bar_width, depth+outset*2, thickness]);
        cutouts(2,light_bar_width,outset,rot=0,trans=[light_bar_width/2,0,0]);
        cutouts(2,light_bar_width,outset,rot=180,trans=[light_bar_width/2,depth+outset*2,0]);
    }
}

module cutouts(num, width, outset, rot, trans) {
    w = width / ((num-1) * 2);
    translate(trans)
    rotate([0,0,1], a=rot)
    for (i = [0 : num-1]) {
        translate([-width/2 - w/2 + i*2*w, -thickness/2, -epsilon])
            cube([w, outset+thickness, thickness+epsilon*2]);
    }
}

module light_wire_opening() {
    translate([light_pos_x, 0, light_height-10])
    scale([2,1,1])
    rotate(v=[1,0,0], a=90)
        cylinder(d=10, h=thickness+epsilon, center=true);
}

module side(y=0) {
    difference() {
        side_base(y);
        scale([1,1,0.5])
            vert_face_base(0, window_thickness);
        scale([1,1,0.5])
            vert_face_base(width);
        light_bar_opening();
        light_wire_opening();
        top_cover();
    }
}

module side_base(y) {
    translate([-overhang,y-thickness/2,overhang/2])
    difference() {
        cube([width+overhang*2, thickness, height-overhang/2]);
        translate([width+overhang+thickness/2, 0, 0])
            rounded_truncation(up=false, rot=[0,0,-90]);
        translate([0, 0, 0])
            rounded_truncation(up=false, rot=[0,0,-90]);
    }
}

module mock_rpi(x, y, z) {
    color("Blue", alpha=0.5)
    translate([x+thickness, y, z])
        rotate(v=[0,1,0], a=90)
            translate([-85/2, -56/2, 0])
                union() {
                    cube([85, 56, 2]);
                    // USB1
                    translate([67, 41, 2])
                        cube([20, 12, 12]);
                    // USB2
                    translate([67, 23, 2])
                        cube([20, 12, 12]);
                }
}


lock cut-in now will not require support; fixing wall-support element in progress

use <joint.scad>

module _supportSurface()
{
  difference()
  {
    union()
    {
      difference()
      {
        // root element
        cube([150, 40, 2]);
        // holes
        for(offset = [30, 90])
          translate([offset, 10, 0])
            cube([30, 20, 2]);
      }
      // lower stripe for wall-support
      off = 4;
      translate([0, -off, 0])
        cube([150, off, 3]);
      // upport tablet block-wall
      translate([0, 40-2, 0])
        cube([150, 2, 9]);
    }
    // lock cut-in
    radius = 7.5;
    translate([150/2, 40-(10), 0])
      rotate([-90, 0, 0])
        cylinder(h=10, r=radius, $fs=0.01);
  }
}

module support()
{
  _supportSurface();
  for(offset = [10+(3+1), 150-10-3-(3+1)])
    translate([offset, 0, 2])
      joint(2);
}

support();

use <joint.scad>

module _supportSurface()
{
  difference()
  {
    union()
    {
      difference()
      {
        // root element
        cube([150, 40, 2]);
        // holes
        for(offset = [30, 90])
          translate([offset, 10, 0])
            cube([30, 20, 2]);
      }
      // lower stripe for wall-support
      off = 6.5/2+2+2;
      translate([0, -off, 0])
        cube([150, off, 3]);
      // upport tablet block-wall
      translate([0, 40, 0])
        cube([150, 2, 9]);
    }
    // lock cut-in
    translate([(150-10)/2, 40-(10-2), 0])
      rotate([-90, 0, 0])
        cylinder(h=10, r=7.5, $fs=0.01);
  }
}

module support()
{
  _supportSurface();
  for(offset = [10+(3+1), 150-10-3-(3+1)])
    translate([offset, 0, 2])
      joint(2);
}

support();


bearing/data: add KH1026,KH1228

bearing/data: add KH1026,KH1228


make B node on BH a little thicker.  Want to get leg assembles a little farther from main bar

// $URL: svn+ssh://mrwhat@ssh.boim.com/home/mrwhat/svn/Walker/trunk/Jansen.scad $
// $Id: Jansen.scad 427 2014-08-23 20:42:39Z mrwhat $

// Define PART to one of the following values to generate model for desired part.
//       HipA, HipB, FootA, FootB, CH, CD, EF, BH, AC, pulley, mainBar, drivePulley
// For a 2-leg (half-trotter) :  print (2) CH, CD ; (4) EF, BH ; (1) of the others
// For a production robot, glue a rectangular support/spacer between BH pairs for stiffness
//
// Combined parts with pads, optimized for RepRap :
// Hip, Feet, crankLinks, EFs, BHs    : main, pulley, drivePulley as above (brace)
PART="HipA";
hingeStyle = "spacer"; //6standoff";  // spacer or standoff
echo(str("PART=",PART));

HoleFuzz = 0.1;  // extra radius (mm) to add to holes to account for printer slop
dHoleFuzz = 0.2;  // extra radius (mm) to add to d-shaft holes.  Sometimes not the same as HoleFuzz since it is harder to clean D-shaft holes

// measured diameter of '3/16"' spacer was 4.74 to 4.76, not 4.7625 as expected.
// Current design:  3/16" axles at B
//                  3/16" long 1/4"  OD standoffs at D, E, F
//                  1/2"  long 3/16" OD standoffs at H, A, C
Bdiam = 4.7625;  // larger brass tube rdiameter, measured on 3/16" tube
Adiam = 4.76;
Hdiam = Adiam;
//Ddiam = 6.35;  // 1/4" OD standoffs
Ddiam = Adiam;  // 3/16" OD standoffs/spacers
diam4 = 2.76;  // diameter of #4-40 screw, OD of thread

// on spacer tests:
//    Brad +.125 mm was snug, but not quite free rotating
//         +.05     turned, but not freely
//         -.025    still turned,  was very close
//    Arad +.125    turned free (good for main, crankLinks)
//         +.05     was press-to-fit (good for crankArm)
//    rad4 + 0.2    allowed a screw to go through snugly but freely
//         +.125    only threaded through
//  1/4"OD + 0.2    was a snug fit, no turn.  almost press-to-fit
//         + 0.275  snug, free turn... good for hinges on standoff

Brad = Bdiam/2 + HoleFuzz/2;    // 4.76mm diam brass tube -- critical fit
Arad = Adiam/2 + HoleFuzz/2.3;  // 3.18mm diam brass tube -- critical fit
rad4 = diam4/2 + HoleFuzz;  // good for screw pass-through
Hrad = Hdiam/2 + HoleFuzz*1.35;  // standoff holes for freely-moving hinges
Drad = Ddiam/2 + HoleFuzz;
AradFree = Adiam/2 + .25;  // loose fit on spindle

BradTight = Brad-0.05;
BradFree  = Brad+0.05;

Frad = 7;  // radius of beefed-up "knee"

LinkRad=3;
BarHeight=3;
//NodeHeight=5.8;  // gives a little room for 6.35 mm (.25 inch) standoffs
layer1bias=0.4;  // shift up for 1 layer of brim/raft

// 9/16" spacer height 14.37mm, not 14.287 as expected
// 3/16" spacer height  4.79mm, not 4.7625 as expected (*3=14.37)
// 1/4" Al standoffs were pretty close, and 3x was 19.02mm high, not 19.05 as expected, pretty close
//TrueNodeHeight = (hingeStyle == "spacer") ? 4.79-.2 : 6.35;
TrueNodeHeight = 4.79-.2;
NodeHeight = TrueNodeHeight - 0.3;  // a little room for hinges
//NodeHeight3 = 25.4*((hingeStyle == "spacer") ? 9/16 : 3/4) - 0.3;
// moving to slightly thinner 1/2" standoffs at H
NodeHeight3 = 25.4/2 - 0.3;

// Forks should be 1 node thick for standoffs...  short screws go into variable length standoff.
// they should be 2 nodes thick for spacers, extra long so that ALL hinges are 5 nodes wide.
//ForkHeight = (hingeStyle == "spacer") ? 2*NodeHeight : NodeHeight;
ForkHeight = NodeHeight;

// definitions of linkage lengths: AC;Ay;Bx;BH;EF;CD;CH;BD;BE;DE;FG;FH;GH
// derived quantities: DEleft;FGleft;DEperp;FGperp
include <JansenDefs.scad>;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//       define some standard part dimensions for re-use in groups, etc.
acH0 = NodeHeight;
acH1 = acH0+2;
acOR = Arad+3;
acIR = rad4;
pulleyR = 17;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

module nodeCyl(nodeHeight,nodeRad)
{ cylinder(h=nodeHeight,r1=nodeRad+.3,r2=nodeRad-.3,$fn=64); }  // set to 20+ for production quality

module drillHole(holeRad) { translate([0,0,-.03])
   cylinder(h=22,r=holeRad,$fn=80); } // set to 80 for production run

module footpad(r) { // pad to help a part stick to plate for 3D printer
   color("Cyan") cylinder(h=layer1bias,r1=r,r2=r-.3,$fn=11); }

// place a point, usually inside a linkage bar, to use in a hull
// with a part connected to a linkage bar
module barAnchor(dx) {
   translate([dx,0,0]) cylinder(h=BarHeight,r1=.8,r2=.6,$fn=6);
}

use <pulley.scad>;

include <JansenMain.scad>
include <JansenFoot.scad>

module monoBracedLinkage(len) {
lro = Drad+1.8;  // outisde link radius
difference() { union() { //------------------------ EF
    difference() { hull() {  // main bar
      translate([ 0 ,0,0])   nodeCyl(BarHeight,Frad);
      translate([len,0,0])   nodeCyl(BarHeight,lro*.5);
      }
      hull() { translate([0,0,-1.5]) { nodeCyl(BarHeight,Frad-1.5);
               translate([len,0 ,0])   nodeCyl(BarHeight,1); }}
    }

    // non-braced (BED side) reinforcement
    translate(  [len,0,0]) { nodeCyl(BarHeight,lro);
       hull() { translate([0,0,BarHeight-1])
          cylinder(h=ForkHeight-BarHeight+1,r1=lro-.8,r2=rad4+1.2,$fn=64);
          cylinder(h=NodeHeight-1,r1=lro+.1,r2=rad4+1.2,$fn=16);
          //barAnchor(-1.5*lro);
          translate([-2*lro,0,2]) sphere(r=.4,$fn=12);
       }
    }
    hull() { translate([1.3*Frad,0,1.5]) scale([1,1,1]) sphere(r=1,$fn=16);
        translate([0,0,BarHeight-1])
          cylinder(h=ForkHeight-BarHeight+1,r1=Frad-.6,r2=rad4+1.6,$fn=64); }
    cylinder(h=BarHeight-1,r1=rad4+2.2,r2=rad4+3.3,$fn=32);

    hull() { // out-of-plane ridge/brace
        translate([ 0 ,0,2.2]) scale([1,1,2]) sphere(r=1,$fn=16);
        translate([len,0,2.2]) scale([1,1,2]) sphere(r=1,$fn=16); }
    }
    drillHole(rad4);
    translate([len,0,0]) drillHole(rad4);
}}

//------------------------------------------------------------ crankLink
// linkage with thin, wide connection for crank side, full node height on other
module crankLink(len,dFlat,rHole) {
// changing crank spindle to a 3/16 spacer instead of the 1/8" brass tube
radA = AradFree;  // free spinning on 3/16 OD standoff
echo(str("rHole=",rHole));
difference() { union() {
   hull() { cylinder(h=NodeHeight/2,r1=radA+3,r2=radA+2.8,$fn=48);
      translate([dFlat+4,0,0]) cylinder(h=NodeHeight/2,r1=1.2,r2=1,$fn=6); }
   translate([len,0,0]) { hull() {
      cylinder(h=NodeHeight,r1=rHole+2.2,r2=rHole+2,$fn=48);
         translate([-rHole-4,0,0]) cylinder(h=BarHeight,r1=1.2,r2=1,$fn=6); }}
   hull() {                  cylinder(h=NodeHeight/2,r1=LinkRad+.4,r2=LinkRad,$fn=6);
      translate([len/2,0,0]) cylinder(h=NodeHeight/2,r1=LinkRad+.4,r2=LinkRad,$fn=6); }
   hull() { translate([dFlat-2,0,0]) cylinder(h=BarHeight,r1=LinkRad+.4,r2=LinkRad-.2,$fn=6);
            translate([len-2  ,0,0]) cylinder(h=BarHeight,r1=LinkRad+.4,r2=LinkRad-.2,$fn=6); }
   hull() {
      translate([dFlat+2  ,0,BarHeight-1]) scale([1,1,1.5]) sphere(r=1,$fn=12);
      translate([len-rHole,0,BarHeight-1]) scale([1,1,1.5]) sphere(r=1,$fn=12);
      translate([len*0.65 ,0,BarHeight+.5]){scale([0.6,0.6,1.7]) sphere(r=1,$fn=12);
         translate([0,0,-2]) scale([2,2,1]) sphere(r=1,$fn=12);}
      translate([dFlat+15 ,0,BarHeight+.5]){scale([0.6,0.6,1.7]) sphere(r=1,$fn=12);
         translate([0,0,-2]) scale([2,2,1]) sphere(r=1,$fn=12);}
      }
   }
   drillHole(radA);  // a little extra to move freely
   translate([len,0,0]) drillHole(rHole);
}}

module linkage1(len,h0,r0,d0,h1,r1,d1) { difference() { union() {
   hull() { barAnchor(1.5*r0);
      cylinder(h=h0,r1=r0+.2,r2=r0-0.2,$fn=48); }
   translate([len,0,0]) { hull() { barAnchor(-1.5*r1);
      cylinder(h=h1,r1=r1,r2=0.4*r1+0.6*d1,$fn=48); }}
   hull() {                cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6);
      translate([len,0,0]) cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6); }
   hull() {                   cylinder(h=h0-0.5,r1=2,r2=.6,$fn=6);
      translate([len-d1,0,0]) cylinder(h=h1-0.5,r1=2,r2=.6,$fn=6); }
   }
   drillHole(d0);
   translate([len,0,0]) drillHole(d1);
}}

module linkage(len,h0,r0,d0,h1,r1,d1) { difference() { union() {
echo(str("linkage:",len,",",h0,",",r0,",",d0,",",h1,",",r1,",",d1));
   hull() { barAnchor(2.5*r0);
      cylinder(h=h0,r1=r0+.1,r2=r0-0.2,$fn=48); }
   translate([len,0,0]) { hull() { barAnchor(-2.5*r1);
      cylinder(h=h1,r1=r1+.1,r2=r1-.2,$fn=48); }}
   hull() {                cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6);
      translate([len,0,0]) cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6); }
   hull() {                   cylinder(h=h0-0.5,r1=2,r2=.6,$fn=6);
      translate([len-d1,0,0]) cylinder(h=h1-0.5,r1=2,r2=.6,$fn=6); }
   }
   drillHole(d0);
   translate([len,0,0]) drillHole(d1);
}}

// beefier version of a simple linkage, with same ends on both sides
module brace(len,h0,r0) {
r1=r0+1.7;
difference() { union() {
   difference() { hull() { cylinder(h=3,r1=r1+.1,r2=r1-.1,$fn=48);
      translate([len,0,0]) cylinder(h=3,r1=r1+.1,r2=r1-.1,$fn=48); }
      translate([1.5,0,-1.5]) hull() { cylinder(h=3,r1=r1-1.6,r2=r1-1.4,$fn=6);
             translate([len-1.5,0,0])  cylinder(h=3,r1=r1-1.6,r2=r1-1.4,$fn=6); }}

   translate([len,0,0])
   hull() { cylinder(h=h0,r1=r1+.2,r2=r1-.1,$fn=48);
      translate([-r1-4,0,2]) sphere(r=1,$fn=12); }
   hull() { cylinder(h=h0,r1=r1+.2,r2=r1-.1,$fn=48);
      translate([ r1+4,0,2]) sphere(r=1,$fn=12); }

   translate([0,0,h0/2]) hull() { scale([1,1,h0/2]) sphere(r=1,$fn=12);
            translate([len,0,0])  scale([1,1,h0/2]) sphere(r=1,$fn=12); }

   }
   drillHole(r0);
   translate([len,0,0]) drillHole(r0);
}}

module crankArm(len,h0,r0,d0,h1,r1,d1) { difference() {
  linkage(len,h0,r0,d0,h1,r1,d1);

  //// add little hole for "cotter-wire" on crank arm
  //translate([0,0,h0/2]) rotate(a=[90,0,0])
  //   cylinder(h=3*r0,r=.8,$fn=80,center=true);

  // for change to using standoffs as axle, counter sink axle screw
  translate([0,0,h0-1.5]) cylinder(h=2+.2,r1=rad4,r2=3.3,$fn=24);

  // try another countersink for spindle screw.
  translate([len,0,-1]) cylinder(h=2.2,r1=3.3,r2=rad4,$fn=24);
}}

include <JansenBED.scad>;

//============================================ Parts with brim/pads underneath

module BED_pad(left,base,perp) { union() { color("Cyan") hull() {
  translate([-left,0,0]) footpad(11);
  translate([base-left,0,0]) footpad(11);
  translate([0,perp,0]) footpad(13);}
  translate([0,0,.2]) imBED(left,base,perp);
}}
module bracedFoot_pad(left,base,perp) { union() { color("Cyan") hull() {
  translate([-left,0,0]) footpad(17);
  translate([0,perp,0]) footpad(11);
  translate([base-left,0,0]) footpad(13); }
  translate([0,0,.2]) bracedFoot(left,base,perp);
}}
module crankLink_pad(len,dFlat,rHole) { union() { color("Cyan") hull() {
   footpad(13);
   translate([len,0,0]) footpad(13); }
   translate([0,0,.2]) crankLink(len,dFlat,rHole);
}}
module monoBracedLinkage_pad(len) { union() { color("Cyan") hull() {
   footpad(15);
   translate([len,0,0]) footpad(13); }
   translate([0,0,.2]) monoBracedLinkage(len);
}}
module linkage_pad(len,h0,r0,d0,h1,r1,d1) { union() { color("Cyan") hull() {
   footpad(11);
   translate([len,0,0]) footpad(11); }
   translate([0,0,.2]) linkage(len,h0,r0,d0,h1,r1,d1);
}}
module brace_pad(len,h0,r0) { union() { color("Cyan") hull() { translate([len,0,0])
   footpad(r0+7);
   footpad(r0+7); }
   translate([0,0,.2]) brace(len,h0,r0);
}}
module linkage1_pad(len,h0,r0,d0,h1,r1,d1) { union() { color("Cyan") hull() {
   footpad(11);
   translate([len,0,0]) footpad(11); }
   translate([0,0,.2]) linkage1(len,h0,r0,d0,h1,r1,d1);
}}
module crankArm_pad(len,h0,r0,d0,h1,r1,d1) { union() { color("Cyan") hull() {
   footpad(11);
   translate([len,0,0]) footpad(11); }
   translate([0,0,.2]) crankArm(len,h0,r0,d0,h1,r1,d1);
}}
module mainBar_pad(x,y) { union() { color("Cyan") hull() {
  translate([-x,0,0]) footpad(12);
  translate([ x,0,0]) footpad(12);
  }
  translate([0,0,.2]) mainBar(x,y);
}}

//---------------------
module BED(dLeft,dBase,dPerp) { union() { rotate([0,0,50]) {
   translate([dBase*0.2,-3*Drad,0]) rotate([0,0,180]) mirror([1,0,0])
   BED_pad(dLeft,dBase,dPerp);
   BED_pad(dLeft,dBase,dPerp);
}}}
module feet(dLeft,dBase,dPerp) { union() {
   translate([dBase*0.15,-0.5*dPerp,0]) rotate([0,0,190]) mirror([1,0,0])
   bracedFoot_pad(dLeft,dBase,dPerp);
   bracedFoot_pad(dLeft,dBase,dPerp);
}}
module crankLinks() {
spc=6;
union() {
   translate([0,spc*3.2,0])
     crankLink_pad(CH,15,Hrad);
     crankLink_pad(CH,15,Hrad);
   rotate([0,0,180]) {
     translate([-CD-1.5*spc, spc*1.6,0]) crankLink_pad(CD,15,Drad);
     translate([-CD-1.5*spc,-spc*1.6,0]) crankLink_pad(CD,15,Drad); }

   // tight holes to glue to axle, not spin
   translate([5*spc,-3.2*spc,0])
     crankArm_pad(AC,acH0,acOR,acIR,acH1,acOR,acIR);
}}
module EFs(len) {
spc=6;
union() {
  translate([len-spc, 1.9*spc,0]) rotate([0,0,180]) monoBracedLinkage_pad(len);
  translate([len-spc,-1.9*spc,0]) rotate([0,0,180]) monoBracedLinkage_pad(len);
  translate([0,-3.8*spc,0]) monoBracedLinkage_pad(len);
  monoBracedLinkage_pad(len);
}}
module BHs(len,h0,r0,d0,h1,r1,d1) {
spc = 2*Brad;
union() {
  translate([spc*1.1, 1.6*spc,0]) linkage1_pad(len,h0,r0,d0,h1,r1,d1);
  translate([spc*1.1,-1.6*spc,0]) linkage1_pad(len,h0,r0,d0,h1,r1,d1);
  translate([ 0     ,-3.2*spc,0]) linkage1_pad(len,h0,r0,d0,h1,r1,d1);
                                  linkage1_pad(len,h0,r0,d0,h1,r1,d1);
}}

//=====================================================
use <spacers.scad>;

if      (PART=="BED"  ) {                   BED(DEleft,DE,DEperp); }
//else if (PART=="BEDa" ) {                 imBED(DEleft,DE,DEperp); }
//else if (PART=="BEDb" ) { mirror([1,0,0]) imBED(DEleft,DE,DEperp); }
else if (PART=="HipA" ) {                 BED1(DEleft,DE,DEperp); }
else if (PART=="HipB" ) { mirror([1,0,0]) BED1(DEleft,DE,DEperp); }
else if (PART=="jHipA" ) { imBEDj(DEleft,DE,DEperp); }
else if (PART=="jHipB" ) { mirror([1,0,0]) imBEDj(DEleft,DE,DEperp); }
else if (PART=="Feet" )                         feet(FGleft,FG,FGperp);
else if (PART=="FootA")
//difference() {
                  bracedFoot(FGleft,FG,FGperp);
  //  translate([0,0,18]) cube([200,200,20],center=true); }
else if (PART=="FootB") { mirror([1,0,0]) bracedFoot(FGleft,FG,FGperp); }
else if (PART=="crankLinks") crankLinks();
else if (PART=="CH"   ) crankLink(CH,15,Hrad); // build 2
else if (PART=="CD"   ) crankLink(CD,15,Drad); // build 2
else if (PART=="EF"   ) monoBracedLinkage(EF); // build 4
else if (PART=="EFs"  ) EFs(EF);
else if (PART=="BHs"  ) BHs(BH,2.5*NodeHeight,Brad+1.8,BradFree,ForkHeight,Hrad+2,rad4);
else if (PART=="BH"   ) linkage1(BH,2.5*NodeHeight+1.5,Brad+1.8,BradFree,ForkHeight,Hrad+2,rad4); // build 4, glue them together with a rectangle if desired
else if (PART=="AC"   ) crankArm(AC,acH0,acOR,acIR,acH1,acOR,acIR);
else if (PART=="halfPulley") { union() { color("Cyan") footpad(pulleyR+7);
       translate([0,0,.2]) halfPulley(pulleyR,2.5,AradTight,-1); }}
else if (PART=="halfDrivePulley") { union() { color("Cyan") footpad(15);
       translate([0,0,.1]) halfPulley(8,2.5,1.5+.3,1+.3); }}
else if (PART=="pulley_pad") { union() { footpad(23); translate([0,0,.2])
                            pulley(18,5,rad4,0); }}
else if (PART=="pulley") {  pulley(18,5,rad4,0); }
else if (PART=="drivePulley_pad") { union() { footpad(15); translate([0,0,.2])
                                pulley(10,4,1.5+dHoleFuzz,1+dHoleFuzz); }}
else if (PART=="drivePulley") { pulley(10,4,1.5+dHoleFuzz,1+dHoleFuzz); }
else if (PART=="mainBar") {  mainBar(Bx,Ay); }
else if (PART=="main") { mainBar_pad(Bx,Ay); }
else if (PART=="braceBar") {  brace(2*Bx,8,Brad); }
else if (PART=="brace") { brace_pad(2*Bx,8,Brad); }
else if (PART=="spacers") { union(){ spacers(NodeHeight/2,Bhole+3,Brad);
   translate([0,-7.5*LinkRad*1.414+1,0]) spacers(NodeHeight/2,LinkRad,rad4); }}
//else if (PART=="demo") { //show all unique parts
else {
   mainBar(Bx,Ay);
   #translate([0,Ay,20]) pulley(18,5,rad4,0);
   translate([0,-50,0])  pulley(10,4,1.5+.3,1+.3);
   translate([ 50, 30,0]) BED1(DEleft,DE,DEperp);//imBED(DEleft,DE,DEperp);
   translate([-26, 44,0]) rotate([0,0,180]) bracedFoot(FGleft,FG,FGperp);
   translate([-12,-16,0]) crankLink(CD,15,Drad);
   translate([-36,-28,0]) crankLink(CH,15,Hrad);
   translate([-73,-15,0]) monoBracedLinkage(EF);
   translate([-60, 15,0]) crankArm(AC,acH0,acOR,acIR,acH1,acOR,acIR);

   translate([ 25, 15,0]) linkage1(BH,2.5*NodeHeight,Brad+1.8,BradFree,ForkHeight,Hrad+2,rad4);
   translate([40,-42,0]) rotate([0,0,50]) brace(2*Bx,8,Brad);
}

// $URL: svn+ssh://mrwhat@ssh.boim.com/home/mrwhat/svn/Walker/trunk/Jansen.scad $
// $Id: Jansen.scad 427 2014-08-23 20:42:39Z mrwhat $

// Define PART to one of the following values to generate model for desired part.
//       HipA, HipB, FootA, FootB, CH, CD, EF, BH, AC, pulley, mainBar, drivePulley
// For a 2-leg (half-trotter) :  print (2) CH, CD ; (4) EF, BH ; (1) of the others
// For a production robot, glue a rectangular support/spacer between BH pairs for stiffness
//
// Combined parts with pads, optimized for RepRap :
// Hip, Feet, crankLinks, EFs, BHs    : main, pulley, drivePulley as above (brace)
PART="HipA";
hingeStyle = "spacer"; //6standoff";  // spacer or standoff
echo(str("PART=",PART));

HoleFuzz = 0.1;  // extra radius (mm) to add to holes to account for printer slop
dHoleFuzz = 0.2;  // extra radius (mm) to add to d-shaft holes.  Sometimes not the same as HoleFuzz since it is harder to clean D-shaft holes

// measured diameter of '3/16"' spacer was 4.74 to 4.76, not 4.7625 as expected.
// Current design:  3/16" axles at B
//                  3/16" long 1/4"  OD standoffs at D, E, F
//                  1/2"  long 3/16" OD standoffs at H, A, C
Bdiam = 4.7625;  // larger brass tube rdiameter, measured on 3/16" tube
Adiam = 4.76;
Hdiam = Adiam;
//Ddiam = 6.35;  // 1/4" OD standoffs
Ddiam = Adiam;  // 3/16" OD standoffs/spacers
diam4 = 2.76;  // diameter of #4-40 screw, OD of thread

// on spacer tests:
//    Brad +.125 mm was snug, but not quite free rotating
//         +.05     turned, but not freely
//         -.025    still turned,  was very close
//    Arad +.125    turned free (good for main, crankLinks)
//         +.05     was press-to-fit (good for crankArm)
//    rad4 + 0.2    allowed a screw to go through snugly but freely
//         +.125    only threaded through
//  1/4"OD + 0.2    was a snug fit, no turn.  almost press-to-fit
//         + 0.275  snug, free turn... good for hinges on standoff

Brad = Bdiam/2 + HoleFuzz/2;    // 4.76mm diam brass tube -- critical fit
Arad = Adiam/2 + HoleFuzz/2.3;  // 3.18mm diam brass tube -- critical fit
rad4 = diam4/2 + HoleFuzz;  // good for screw pass-through
Hrad = Hdiam/2 + HoleFuzz*1.35;  // standoff holes for freely-moving hinges
Drad = Ddiam/2 + HoleFuzz;
AradFree = Adiam/2 + .25;  // loose fit on spindle

BradTight = Brad-0.05;
BradFree  = Brad+0.05;

Frad = 7;  // radius of beefed-up "knee"

LinkRad=3;
BarHeight=3;
//NodeHeight=5.8;  // gives a little room for 6.35 mm (.25 inch) standoffs
layer1bias=0.4;  // shift up for 1 layer of brim/raft

// 9/16" spacer height 14.37mm, not 14.287 as expected
// 3/16" spacer height  4.79mm, not 4.7625 as expected (*3=14.37)
// 1/4" Al standoffs were pretty close, and 3x was 19.02mm high, not 19.05 as expected, pretty close
//TrueNodeHeight = (hingeStyle == "spacer") ? 4.79-.2 : 6.35;
TrueNodeHeight = 4.79-.2;
NodeHeight = TrueNodeHeight - 0.3;  // a little room for hinges
//NodeHeight3 = 25.4*((hingeStyle == "spacer") ? 9/16 : 3/4) - 0.3;
// moving to slightly thinner 1/2" standoffs at H
NodeHeight3 = 25.4/2 - 0.3;

// Forks should be 1 node thick for standoffs...  short screws go into variable length standoff.
// they should be 2 nodes thick for spacers, extra long so that ALL hinges are 5 nodes wide.
//ForkHeight = (hingeStyle == "spacer") ? 2*NodeHeight : NodeHeight;
ForkHeight = NodeHeight;

// definitions of linkage lengths: AC;Ay;Bx;BH;EF;CD;CH;BD;BE;DE;FG;FH;GH
// derived quantities: DEleft;FGleft;DEperp;FGperp
include <JansenDefs.scad>;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//       define some standard part dimensions for re-use in groups, etc.
acH0 = NodeHeight;
acH1 = acH0+2;
acOR = Arad+3;
acIR = rad4;
pulleyR = 17;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

module nodeCyl(nodeHeight,nodeRad)
{ cylinder(h=nodeHeight,r1=nodeRad+.3,r2=nodeRad-.3,$fn=64); }  // set to 20+ for production quality

module drillHole(holeRad) { translate([0,0,-.03])
   cylinder(h=22,r=holeRad,$fn=80); } // set to 80 for production run

module footpad(r) { // pad to help a part stick to plate for 3D printer
   color("Cyan") cylinder(h=layer1bias,r1=r,r2=r-.3,$fn=11); }

// place a point, usually inside a linkage bar, to use in a hull
// with a part connected to a linkage bar
module barAnchor(dx) {
   translate([dx,0,0]) cylinder(h=BarHeight,r1=.8,r2=.6,$fn=6);
}

use <pulley.scad>;

include <JansenMain.scad>
include <JansenFoot.scad>

module monoBracedLinkage(len) {
lro = Drad+1.8;  // outisde link radius
difference() { union() { //------------------------ EF
    difference() { hull() {  // main bar
      translate([ 0 ,0,0])   nodeCyl(BarHeight,Frad);
      translate([len,0,0])   nodeCyl(BarHeight,lro*.5);
      }
      hull() { translate([0,0,-1.5]) { nodeCyl(BarHeight,Frad-1.5);
               translate([len,0 ,0])   nodeCyl(BarHeight,1); }}
    }

    // non-braced (BED side) reinforcement
    translate(  [len,0,0]) { nodeCyl(BarHeight,lro);
       hull() { translate([0,0,BarHeight-1])
          cylinder(h=ForkHeight-BarHeight+1,r1=lro-.8,r2=rad4+1.2,$fn=64);
          cylinder(h=NodeHeight-1,r1=lro+.1,r2=rad4+1.2,$fn=16);
          //barAnchor(-1.5*lro);
          translate([-2*lro,0,2]) sphere(r=.4,$fn=12);
       }
    }
    hull() { translate([1.3*Frad,0,1.5]) scale([1,1,1]) sphere(r=1,$fn=16);
        translate([0,0,BarHeight-1])
          cylinder(h=ForkHeight-BarHeight+1,r1=Frad-.6,r2=rad4+1.6,$fn=64); }
    cylinder(h=BarHeight-1,r1=rad4+2.2,r2=rad4+3.3,$fn=32);

    hull() { // out-of-plane ridge/brace
        translate([ 0 ,0,2.2]) scale([1,1,2]) sphere(r=1,$fn=16);
        translate([len,0,2.2]) scale([1,1,2]) sphere(r=1,$fn=16); }
    }
    drillHole(rad4);
    translate([len,0,0]) drillHole(rad4);
}}

//------------------------------------------------------------ crankLink
// linkage with thin, wide connection for crank side, full node height on other
module crankLink(len,dFlat,rHole) {
// changing crank spindle to a 3/16 spacer instead of the 1/8" brass tube
radA = AradFree;  // free spinning on 3/16 OD standoff
echo(str("rHole=",rHole));
difference() { union() {
   hull() { cylinder(h=NodeHeight/2,r1=radA+3,r2=radA+2.8,$fn=48);
      translate([dFlat+4,0,0]) cylinder(h=NodeHeight/2,r1=1.2,r2=1,$fn=6); }
   translate([len,0,0]) { hull() {
      cylinder(h=NodeHeight,r1=rHole+2.2,r2=rHole+2,$fn=48);
         translate([-rHole-4,0,0]) cylinder(h=BarHeight,r1=1.2,r2=1,$fn=6); }}
   hull() {                  cylinder(h=NodeHeight/2,r1=LinkRad+.4,r2=LinkRad,$fn=6);
      translate([len/2,0,0]) cylinder(h=NodeHeight/2,r1=LinkRad+.4,r2=LinkRad,$fn=6); }
   hull() { translate([dFlat-2,0,0]) cylinder(h=BarHeight,r1=LinkRad+.4,r2=LinkRad-.2,$fn=6);
            translate([len-2  ,0,0]) cylinder(h=BarHeight,r1=LinkRad+.4,r2=LinkRad-.2,$fn=6); }
   hull() {
      translate([dFlat+2  ,0,BarHeight-1]) scale([1,1,1.5]) sphere(r=1,$fn=12);
      translate([len-rHole,0,BarHeight-1]) scale([1,1,1.5]) sphere(r=1,$fn=12);
      translate([len*0.65 ,0,BarHeight+.5]){scale([0.6,0.6,1.7]) sphere(r=1,$fn=12);
         translate([0,0,-2]) scale([2,2,1]) sphere(r=1,$fn=12);}
      translate([dFlat+15 ,0,BarHeight+.5]){scale([0.6,0.6,1.7]) sphere(r=1,$fn=12);
         translate([0,0,-2]) scale([2,2,1]) sphere(r=1,$fn=12);}
      }
   }
   drillHole(radA);  // a little extra to move freely
   translate([len,0,0]) drillHole(rHole);
}}

module linkage1(len,h0,r0,d0,h1,r1,d1) { difference() { union() {
   hull() { barAnchor(1.5*r0);
      cylinder(h=h0,r1=r0+.2,r2=r0-0.2,$fn=48); }
   translate([len,0,0]) { hull() { barAnchor(-1.5*r1);
      cylinder(h=h1,r1=r1,r2=0.4*r1+0.6*d1,$fn=48); }}
   hull() {                cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6);
      translate([len,0,0]) cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6); }
   hull() {                   cylinder(h=h0-0.5,r1=2,r2=.6,$fn=6);
      translate([len-d1,0,0]) cylinder(h=h1-0.5,r1=2,r2=.6,$fn=6); }
   }
   drillHole(d0);
   translate([len,0,0]) drillHole(d1);
}}

module linkage(len,h0,r0,d0,h1,r1,d1) { difference() { union() {
echo(str("linkage:",len,",",h0,",",r0,",",d0,",",h1,",",r1,",",d1));
   hull() { barAnchor(2.5*r0);
      cylinder(h=h0,r1=r0+.1,r2=r0-0.2,$fn=48); }
   translate([len,0,0]) { hull() { barAnchor(-2.5*r1);
      cylinder(h=h1,r1=r1+.1,r2=r1-.2,$fn=48); }}
   hull() {                cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6);
      translate([len,0,0]) cylinder(h=BarHeight,r1=LinkRad+.2,r2=LinkRad-.2,$fn=6); }
   hull() {                   cylinder(h=h0-0.5,r1=2,r2=.6,$fn=6);
      translate([len-d1,0,0]) cylinder(h=h1-0.5,r1=2,r2=.6,$fn=6); }
   }
   drillHole(d0);
   translate([len,0,0]) drillHole(d1);
}}

// beefier version of a simple linkage, with same ends on both sides
module brace(len,h0,r0) {
r1=r0+1.7;
difference() { union() {
   difference() { hull() { cylinder(h=3,r1=r1+.1,r2=r1-.1,$fn=48);
      translate([len,0,0]) cylinder(h=3,r1=r1+.1,r2=r1-.1,$fn=48); }
      translate([1.5,0,-1.5]) hull() { cylinder(h=3,r1=r1-1.6,r2=r1-1.4,$fn=6);
             translate([len-1.5,0,0])  cylinder(h=3,r1=r1-1.6,r2=r1-1.4,$fn=6); }}

   translate([len,0,0])
   hull() { cylinder(h=h0,r1=r1+.2,r2=r1-.1,$fn=48);
      translate([-r1-4,0,2]) sphere(r=1,$fn=12); }
   hull() { cylinder(h=h0,r1=r1+.2,r2=r1-.1,$fn=48);
      translate([ r1+4,0,2]) sphere(r=1,$fn=12); }

   translate([0,0,h0/2]) hull() { scale([1,1,h0/2]) sphere(r=1,$fn=12);
            translate([len,0,0])  scale([1,1,h0/2]) sphere(r=1,$fn=12); }

   }
   drillHole(r0);
   translate([len,0,0]) drillHole(r0);
}}

module crankArm(len,h0,r0,d0,h1,r1,d1) { difference() {
  linkage(len,h0,r0,d0,h1,r1,d1);

  //// add little hole for "cotter-wire" on crank arm
  //translate([0,0,h0/2]) rotate(a=[90,0,0])
  //   cylinder(h=3*r0,r=.8,$fn=80,center=true);

  // for change to using standoffs as axle, counter sink axle screw
  translate([0,0,h0-1.5]) cylinder(h=2+.2,r1=rad4,r2=3.3,$fn=24);

  // try another countersink for spindle screw.
  translate([len,0,-1]) cylinder(h=2.2,r1=3.3,r2=rad4,$fn=24);
}}

include <JansenBED.scad>;

//============================================ Parts with brim/pads underneath

module BED_pad(left,base,perp) { union() { color("Cyan") hull() {
  translate([-left,0,0]) footpad(11);
  translate([base-left,0,0]) footpad(11);
  translate([0,perp,0]) footpad(13);}
  translate([0,0,.2]) imBED(left,base,perp);
}}
module bracedFoot_pad(left,base,perp) { union() { color("Cyan") hull() {
  translate([-left,0,0]) footpad(17);
  translate([0,perp,0]) footpad(11);
  translate([base-left,0,0]) footpad(13); }
  translate([0,0,.2]) bracedFoot(left,base,perp);
}}
module crankLink_pad(len,dFlat,rHole) { union() { color("Cyan") hull() {
   footpad(13);
   translate([len,0,0]) footpad(13); }
   translate([0,0,.2]) crankLink(len,dFlat,rHole);
}}
module monoBracedLinkage_pad(len) { union() { color("Cyan") hull() {
   footpad(15);
   translate([len,0,0]) footpad(13); }
   translate([0,0,.2]) monoBracedLinkage(len);
}}
module linkage_pad(len,h0,r0,d0,h1,r1,d1) { union() { color("Cyan") hull() {
   footpad(11);
   translate([len,0,0]) footpad(11); }
   translate([0,0,.2]) linkage(len,h0,r0,d0,h1,r1,d1);
}}
module brace_pad(len,h0,r0) { union() { color("Cyan") hull() { translate([len,0,0])
   footpad(r0+7);
   footpad(r0+7); }
   translate([0,0,.2]) brace(len,h0,r0);
}}
module linkage1_pad(len,h0,r0,d0,h1,r1,d1) { union() { color("Cyan") hull() {
   footpad(11);
   translate([len,0,0]) footpad(11); }
   translate([0,0,.2]) linkage1(len,h0,r0,d0,h1,r1,d1);
}}
module crankArm_pad(len,h0,r0,d0,h1,r1,d1) { union() { color("Cyan") hull() {
   footpad(11);
   translate([len,0,0]) footpad(11); }
   translate([0,0,.2]) crankArm(len,h0,r0,d0,h1,r1,d1);
}}
module mainBar_pad(x,y) { union() { color("Cyan") hull() {
  translate([-x,0,0]) footpad(12);
  translate([ x,0,0]) footpad(12);
  }
  translate([0,0,.2]) mainBar(x,y);
}}

//---------------------
module BED(dLeft,dBase,dPerp) { union() { rotate([0,0,50]) {
   translate([dBase*0.2,-3*Drad,0]) rotate([0,0,180]) mirror([1,0,0])
   BED_pad(dLeft,dBase,dPerp);
   BED_pad(dLeft,dBase,dPerp);
}}}
module feet(dLeft,dBase,dPerp) { union() {
   translate([dBase*0.15,-0.5*dPerp,0]) rotate([0,0,190]) mirror([1,0,0])
   bracedFoot_pad(dLeft,dBase,dPerp);
   bracedFoot_pad(dLeft,dBase,dPerp);
}}
module crankLinks() {
spc=6;
union() {
   translate([0,spc*3.2,0])
     crankLink_pad(CH,15,Hrad);
     crankLink_pad(CH,15,Hrad);
   rotate([0,0,180]) {
     translate([-CD-1.5*spc, spc*1.6,0]) crankLink_pad(CD,15,Drad);
     translate([-CD-1.5*spc,-spc*1.6,0]) crankLink_pad(CD,15,Drad); }

   // tight holes to glue to axle, not spin
   translate([5*spc,-3.2*spc,0])
     crankArm_pad(AC,acH0,acOR,acIR,acH1,acOR,acIR);
}}
module EFs(len) {
spc=6;
union() {
  translate([len-spc, 1.9*spc,0]) rotate([0,0,180]) monoBracedLinkage_pad(len);
  translate([len-spc,-1.9*spc,0]) rotate([0,0,180]) monoBracedLinkage_pad(len);
  translate([0,-3.8*spc,0]) monoBracedLinkage_pad(len);
  monoBracedLinkage_pad(len);
}}
module BHs(len,h0,r0,d0,h1,r1,d1) {
spc = 2*Brad;
union() {
  translate([spc*1.1, 1.6*spc,0]) linkage1_pad(len,h0,r0,d0,h1,r1,d1);
  translate([spc*1.1,-1.6*spc,0]) linkage1_pad(len,h0,r0,d0,h1,r1,d1);
  translate([ 0     ,-3.2*spc,0]) linkage1_pad(len,h0,r0,d0,h1,r1,d1);
                                  linkage1_pad(len,h0,r0,d0,h1,r1,d1);
}}

//=====================================================
use <spacers.scad>;

if      (PART=="BED"  ) {                   BED(DEleft,DE,DEperp); }
//else if (PART=="BEDa" ) {                 imBED(DEleft,DE,DEperp); }
//else if (PART=="BEDb" ) { mirror([1,0,0]) imBED(DEleft,DE,DEperp); }
else if (PART=="HipA" ) {                 BED1(DEleft,DE,DEperp); }
else if (PART=="HipB" ) { mirror([1,0,0]) BED1(DEleft,DE,DEperp); }
else if (PART=="jHipA" ) { imBEDj(DEleft,DE,DEperp); }
else if (PART=="jHipB" ) { mirror([1,0,0]) imBEDj(DEleft,DE,DEperp); }
else if (PART=="Feet" )                         feet(FGleft,FG,FGperp);
else if (PART=="FootA")
//difference() {
                  bracedFoot(FGleft,FG,FGperp);
  //  translate([0,0,18]) cube([200,200,20],center=true); }
else if (PART=="FootB") { mirror([1,0,0]) bracedFoot(FGleft,FG,FGperp); }
else if (PART=="crankLinks") crankLinks();
else if (PART=="CH"   ) crankLink(CH,15,Hrad); // build 2
else if (PART=="CD"   ) crankLink(CD,15,Drad); // build 2
else if (PART=="EF"   ) monoBracedLinkage(EF); // build 4
else if (PART=="EFs"  ) EFs(EF);
else if (PART=="BHs"  ) BHs(BH,2.5*NodeHeight,Brad+1.8,BradFree,ForkHeight,Hrad+2,rad4);
else if (PART=="BH"   ) linkage1(BH,2.5*NodeHeight,Brad+1.8,BradFree,ForkHeight,Hrad+2,rad4); // build 4, glue them together with a rectangle if desired
else if (PART=="AC"   ) crankArm(AC,acH0,acOR,acIR,acH1,acOR,acIR);
else if (PART=="halfPulley") { union() { color("Cyan") footpad(pulleyR+7);
       translate([0,0,.2]) halfPulley(pulleyR,2.5,AradTight,-1); }}
else if (PART=="halfDrivePulley") { union() { color("Cyan") footpad(15);
       translate([0,0,.1]) halfPulley(8,2.5,1.5+.3,1+.3); }}
else if (PART=="pulley_pad") { union() { footpad(23); translate([0,0,.2])
                            pulley(18,5,rad4,0); }}
else if (PART=="pulley") {  pulley(18,5,rad4,0); }
else if (PART=="drivePulley_pad") { union() { footpad(15); translate([0,0,.2])
                                pulley(10,4,1.5+dHoleFuzz,1+dHoleFuzz); }}
else if (PART=="drivePulley") { pulley(10,4,1.5+dHoleFuzz,1+dHoleFuzz); }
else if (PART=="mainBar") {  mainBar(Bx,Ay); }
else if (PART=="main") { mainBar_pad(Bx,Ay); }
else if (PART=="braceBar") {  brace(2*Bx,8,Brad); }
else if (PART=="brace") { brace_pad(2*Bx,8,Brad); }
else if (PART=="spacers") { union(){ spacers(NodeHeight/2,Bhole+3,Brad);
   translate([0,-7.5*LinkRad*1.414+1,0]) spacers(NodeHeight/2,LinkRad,rad4); }}
//else if (PART=="demo") { //show all unique parts
else {
   mainBar(Bx,Ay);
   #translate([0,Ay,20]) pulley(18,5,rad4,0);
   translate([0,-50,0])  pulley(10,4,1.5+.3,1+.3);
   translate([ 50, 30,0]) BED1(DEleft,DE,DEperp);//imBED(DEleft,DE,DEperp);
   translate([-26, 44,0]) rotate([0,0,180]) bracedFoot(FGleft,FG,FGperp);
   translate([-12,-16,0]) crankLink(CD,15,Drad);
   translate([-36,-28,0]) crankLink(CH,15,Hrad);
   translate([-73,-15,0]) monoBracedLinkage(EF);
   translate([-60, 15,0]) crankArm(AC,acH0,acOR,acIR,acH1,acOR,acIR);

   translate([ 25, 15,0]) linkage1(BH,2.5*NodeHeight,Brad+1.8,BradFree,ForkHeight,Hrad+2,rad4);
   translate([40,-42,0]) rotate([0,0,50]) brace(2*Bx,8,Brad);
}


ajout du modèle de pignon

ajout du modèle de pignon

error: pathspec 'méca/pignon.scad' did not match any file(s) known to git


metric/screw: add more asserts

metric/screw: add more asserts

include <system.scad>
include <shapes.scad>
include <transforms.scad>
include <misc.scad>

include <metric-thread-data.scad>
include <metric-hexnut-data.scad>
include <metric-knurlnut-data.scad>

use <scad-utils/transformations.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(thread) = lookup(ThreadSize, thread);
function get_screw_head_d(thread) = 2 * lookup(ThreadSize, thread);

module screw(nut, thread, h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=[0,0,1], align=[0,0,0])
{
    nut_thread = get(MHexNutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != undef, "screw: No nut or thread given");
    /*assert(nut!=undef && thread!=undef && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(nut);
    nut_h = lookup(MHexNutThickness,nut)*tolerance;
    threadsize = lookup(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=[0,0,1], align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head(thread=thread_, orient=[0,0,1], align=[0,0,1]);
            }

            screw_thread(thread=thread_, h=h+.1, tolerance=tolerance, orient=[0,0,1], align=[0,0,0]);

            if(with_nut && nut != undef)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut(nut=nut, tolerance=tolerance, orient=[0,0,1], align=[0,0,-1]);
            }
        }
    }
}

module screw_cut(nut, thread, h=10, tolerance=1.05, head_embed=false, with_nut=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=[0,0,1], align=[0,0,0])
{
    nut_thread = get(MHexNutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != undef, "screw_cut: No nut or thread given");
    /*assert(nut!=undef && thread!=undef && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(MHexNutThickness,nut)*tolerance;
    head_h = get_screw_head_h(thread);
    threadsize = get(ThreadSize, thread_);
    assert(threadsize != undef, "screw_cut: threadsize is undef!");

    s = threadsize*tolerance;
    total_h = h;
    assert(s != undef, "screw_cut: s is undef!");
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=[0,0,1], align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head_cut(thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=[0,0,1]);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=[0,0,1], align=[0,0,0]);

            if(with_nut && nut != undef)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, align=[0,0,-1]);
            }
        }
    }

    if($show_vit)
    {
        %screw(nut=nut, thread=thread, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_thread: thread is undef");
    // TODO render thread
    threadsize = lookup(ThreadSize, thread);
    cylindera(d=threadsize, h=h, orient=orient, align=align);
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_thread_cut: thread is undef");
    threadsize = lookup(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(thread, tolerance=1.00, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_head: thread is undef");

    threadsize = lookup(ThreadSize, thread);
    head_h = get_screw_head_h(thread);
    head_d = get_screw_head_d(thread);
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    difference()
    {
        cylindera(d=head_d, h = fallback(override_h,head_h));
        translate([0,0,head_h/2])
        cylindera(d=threadsize, h = head_d/2);
    }
}

module screw_head_cut(thread, tolerance=1.05, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_head_cut: thread is undef");

    threadsize = lookup(ThreadSize, thread);
    head_h = get_screw_head_h(thread);
    head_d = get_screw_head_d(thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    translate([0,0,-head_h/2])
    cylindera(d=head_d, h=fallback(override_h, head_h), align=[0,0,1]);
}

module screw_nut(nut, tolerance=1.00, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    assert(nut!=undef, "screw_nut: nut is undef");

    thickness = lookup(MHexNutThickness,nut)*tolerance;
    d = lookup(MHexNutWidthMin, nut)*tolerance;
    facets = lookup(MHexNutFacets, nut);
    cylindera($fn=lookup(MHexNutFacets, nut), d=d, h=fallback(override_h, thickness), orient=orient, align=align);
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, orient=[0,0,1], align=[0,0,0])
{
    assert(nut!=undef, "screw_nut_cut: nut is undef");

    thickness = lookup(MHexNutThickness,nut)*tolerance;
    d = lookup(MHexNutWidthMin, nut)*tolerance;
    size_align(size=[d,d,thickness], orient=orient, align=align)
    {
        translate([0,0,thickness/2])
        {
            screw_nut(nut, tolerance, orient=[0,0,1], align=[0,0,-1]);
            translate([0,0,-thickness+.01])
            {
                cylindera(d=d*1.1, h=h, orient=[0,0,1], align=[0,0,-1]);
            }
        }
    }
}

module nut_trap_cut(nut, thread, trap_offset=10, screw_l=10*mm, screw_l_extra=2*mm, trap_h=10, trap_axis=[0,-1,0], orient=[0,0,1], align=[0,0,0])
{
    nut_thread = get(MHexNutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != undef, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=undef && thread!=undef && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = lookup(ThreadSize, thread_);
    head_h = get_screw_head_h(thread);
    nut_h = lookup(MHexNutThickness,nut)+.5*mm;

    nut_width_min = lookup(MHexNutWidthMin, nut)+.2*mm;
    nut_width_max = lookup(MHexNutWidthMax, nut)+.2*mm;
    s = nut_width_min;
    total_h = nut_h;

    size_align(size=[s, s, total_h], orient=orient, orient_ref=[0,0,1], align=align)
    {
        translate([0,0,nut_h/2-.1])
        screw_thread_cut(thread=thread_, tolerance=1.1, h=screw_l, orient=[0,0,-1], align=[0,0,1]);

        if(screw_l_extra>0)
        translate([0,0,-nut_h/2+.1])
        screw_thread_cut(thread=thread_, tolerance=1.1, h=screw_l_extra+.1, orient=[0,0,1], align=[0,0,-1]);

        hull()
        {
            orient(-orient)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(orient)
                cylindera($fn=lookup(MHexNutFacets, nut), d=nut_width_max, h=nut_h, align=[0,0,0]);

                orient(orient)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=[0,0,0]);
            }
        }

        if($show_vit)
        {
            translate([0,0, screw_l])
            translate([0,0, -nut_h/2])
            %screw(nut=nut, thread=thread, tolerance=1, with_nut=false, orient=[0,0,-1], align=[0,0,-1]);

            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=[0,0,-1], align=[0,0,0]);
        }

    }
}

function get(key, dict) =
    let(x = search(key, dict))
    let(kv = dict[x[0]])
    kv[1];

if(false)
{
    nut1 = MHexNutM3;
    nut2 = MHexNutM5;
    nut3 = MKnurlInsertNutM3_3_42;
    nut4 = MKnurlInsertNutM3_5_42;

    /*translate([0,20,0])*/
    /*translate([0,20,0])*/
    /*for(axis1=[0:len(AXES)-1])*/
    /*[>for(axis2=AXES)<]*/
    /*translate(20*AXES[axis1])*/
    /*{*/
        /*ta = v_rotate(90, AXES[axis1])*AXES[axis1];*/
        /*echo(AXES[axis1], ta);*/
        /*nut_trap_cut(nut=nut1, trap_axis=ta, orient=AXES[axis1]);*/
    /*}*/

    /*stack(d=30, axis=[1,0,0])*/
    /*{*/
        /*stack(d=20, axis=[0,1,0])*/
        /*{*/
            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[1,0,0]);*/
            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[-1,0,0]);*/
        /*}*/

        /*stack(d=20, axis=[1,0,0])*/
        /*{*/
            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[0,1,0]);*/

            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[0,-1,0]);*/
        /*}*/
    /*}*/

    box_w = 100;
    box_h = 10;
    box_d = 10;
    o = 10;

    $show_vit = true;
    $fs= 0.5;
    $fa = 4;

    difference()
    {
        cubea([box_w,box_d/2,box_h], align=[1,1,1]);

        test_cuts();
    }
    /*difference()*/
    /*{*/
        /*cubea([box_w,box_d/2,box_h], align=[1,-1,1]);*/

        /*test_cuts();*/
    /*}*/

}

module test_cuts()
{
    union()
    {
        offset = 0;
        /*offset = -5;*/
        translate([0,offset,0])
        translate([5,0,0])
        stack(dist=10,axis=[1,0,0])
        {
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=[0,0,1], align=[0,0,1]);

            screw_cut(nut=nut1, h=box_h*mm, nut_offset=0*mm, head_embed=true, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut2, h=box_h*mm, nut_offset=3*mm, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);

            /*translate([0,5,0])*/
            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=[0,-1,0], orient=[0,0,-1], align=[0,0,0]);

            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=[0,1,0], orient=[0,0,1], align=[0,0,0]);

            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_l_extra=2*mm, trap_axis=[0,0,1], orient=[0,1,0], align=[0,0,0]);

            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_l_extra=2*mm, trap_axis=[0,0,-1], orient=[0,1,0], align=[0,0,0]);
        }
    }
}

include <system.scad>
include <shapes.scad>
include <transforms.scad>
include <misc.scad>

include <metric-thread-data.scad>
include <metric-hexnut-data.scad>
include <metric-knurlnut-data.scad>

use <scad-utils/transformations.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(thread) = lookup(ThreadSize, thread);
function get_screw_head_d(thread) = 2 * lookup(ThreadSize, thread);

module screw(nut, thread, h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=[0,0,1], align=[0,0,0])
{
    nut_thread = get(MHexNutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != undef, "screw: No nut or thread given");
    /*assert(nut!=undef && thread!=undef && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(nut);
    nut_h = lookup(MHexNutThickness,nut)*tolerance;
    threadsize = lookup(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=[0,0,1], align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head(thread=thread_, orient=[0,0,1], align=[0,0,1]);
            }

            screw_thread(thread=thread_, h=h+.1, tolerance=tolerance, orient=[0,0,1], align=[0,0,0]);

            if(with_nut && nut != undef)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut(nut=nut, tolerance=tolerance, orient=[0,0,1], align=[0,0,-1]);
            }
        }
    }
}

module screw_cut(nut, thread, h=10, tolerance=1.05, head_embed=false, with_nut=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=[0,0,1], align=[0,0,0])
{
    nut_thread = get(MHexNutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != undef, "screw_cut: No nut or thread given");
    /*assert(nut!=undef && thread!=undef && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(MHexNutThickness,nut)*tolerance;
    head_h = get_screw_head_h(thread);
    threadsize = get(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=[0,0,1], align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head_cut(thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=[0,0,1]);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=[0,0,1], align=[0,0,0]);

            if(with_nut && nut != undef)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, align=[0,0,-1]);
            }
        }
    }

    if($show_vit)
    {
        %screw(nut=nut, thread=thread, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_thread: thread is undef");
    // TODO render thread
    threadsize = lookup(ThreadSize, thread);
    cylindera(d=threadsize, h=h, orient=orient, align=align);
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_thread_cut: thread is undef");
    threadsize = lookup(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(thread, tolerance=1.00, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_head: thread is undef");

    threadsize = lookup(ThreadSize, thread);
    head_h = get_screw_head_h(thread);
    head_d = get_screw_head_d(thread);
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    difference()
    {
        cylindera(d=head_d, h = fallback(override_h,head_h));
        translate([0,0,head_h/2])
        cylindera(d=threadsize, h = head_d/2);
    }
}

module screw_head_cut(thread, tolerance=1.05, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    assert(thread!=undef, "screw_head_cut: thread is undef");

    threadsize = lookup(ThreadSize, thread);
    head_h = get_screw_head_h(thread);
    head_d = get_screw_head_d(thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    translate([0,0,-head_h/2])
    cylindera(d=head_d, h=fallback(override_h, head_h), align=[0,0,1]);
}

module screw_nut(nut, tolerance=1.00, override_h=undef, orient=[0,0,1], align=[0,0,0])
{
    assert(nut!=undef, "screw_nut: nut is undef");

    thickness = lookup(MHexNutThickness,nut)*tolerance;
    d = lookup(MHexNutWidthMin, nut)*tolerance;
    facets = lookup(MHexNutFacets, nut);
    cylindera($fn=lookup(MHexNutFacets, nut), d=d, h=fallback(override_h, thickness), orient=orient, align=align);
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, orient=[0,0,1], align=[0,0,0])
{
    assert(nut!=undef, "screw_nut_cut: nut is undef");

    thickness = lookup(MHexNutThickness,nut)*tolerance;
    d = lookup(MHexNutWidthMin, nut)*tolerance;
    size_align(size=[d,d,thickness], orient=orient, align=align)
    {
        translate([0,0,thickness/2])
        {
            screw_nut(nut, tolerance, orient=[0,0,1], align=[0,0,-1]);
            translate([0,0,-thickness+.01])
            {
                cylindera(d=d*1.1, h=h, orient=[0,0,1], align=[0,0,-1]);
            }
        }
    }
}

module nut_trap_cut(nut, thread, trap_offset=10, screw_l=10*mm, screw_l_extra=2*mm, trap_h=10, trap_axis=[0,-1,0], orient=[0,0,1], align=[0,0,0])
{
    nut_thread = get(MHexNutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != undef, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=undef && thread!=undef && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = lookup(ThreadSize, thread_);
    head_h = get_screw_head_h(thread);
    nut_h = lookup(MHexNutThickness,nut)+.5*mm;

    nut_width_min = lookup(MHexNutWidthMin, nut)+.2*mm;
    nut_width_max = lookup(MHexNutWidthMax, nut)+.2*mm;
    s = nut_width_min;
    total_h = nut_h;

    size_align(size=[s, s, total_h], orient=orient, orient_ref=[0,0,1], align=align)
    {
        translate([0,0,nut_h/2-.1])
        screw_thread_cut(thread=thread_, tolerance=1.1, h=screw_l, orient=[0,0,-1], align=[0,0,1]);

        if(screw_l_extra>0)
        translate([0,0,-nut_h/2+.1])
        screw_thread_cut(thread=thread_, tolerance=1.1, h=screw_l_extra+.1, orient=[0,0,1], align=[0,0,-1]);

        hull()
        {
            orient(-orient)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(orient)
                cylindera($fn=lookup(MHexNutFacets, nut), d=nut_width_max, h=nut_h, align=[0,0,0]);

                orient(orient)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=[0,0,0]);
            }
        }

        if($show_vit)
        {
            translate([0,0, screw_l])
            translate([0,0, -nut_h/2])
            %screw(nut=nut, thread=thread, tolerance=1, with_nut=false, orient=[0,0,-1], align=[0,0,-1]);

            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=[0,0,-1], align=[0,0,0]);
        }

    }
}

function get(key, dict) =
    let(x = search(key, dict))
    let(kv = dict[x[0]])
    kv[1];

if(false)
{
    nut1 = MHexNutM3;
    nut2 = MHexNutM5;
    nut3 = MKnurlInsertNutM3_3_42;
    nut4 = MKnurlInsertNutM3_5_42;

    /*translate([0,20,0])*/
    /*translate([0,20,0])*/
    /*for(axis1=[0:len(AXES)-1])*/
    /*[>for(axis2=AXES)<]*/
    /*translate(20*AXES[axis1])*/
    /*{*/
        /*ta = v_rotate(90, AXES[axis1])*AXES[axis1];*/
        /*echo(AXES[axis1], ta);*/
        /*nut_trap_cut(nut=nut1, trap_axis=ta, orient=AXES[axis1]);*/
    /*}*/

    /*stack(d=30, axis=[1,0,0])*/
    /*{*/
        /*stack(d=20, axis=[0,1,0])*/
        /*{*/
            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[1,0,0]);*/
            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[-1,0,0]);*/
        /*}*/

        /*stack(d=20, axis=[1,0,0])*/
        /*{*/
            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[0,1,0]);*/

            /*nut_trap_cut(nut=nut1, trap_axis=[0,0,1], orient=[0,-1,0]);*/
        /*}*/
    /*}*/

    box_w = 100;
    box_h = 10;
    box_d = 10;
    o = 10;

    $show_vit = true;
    $fs= 0.5;
    $fa = 4;

    difference()
    {
        cubea([box_w,box_d/2,box_h], align=[1,1,1]);

        test_cuts();
    }
    /*difference()*/
    /*{*/
        /*cubea([box_w,box_d/2,box_h], align=[1,-1,1]);*/

        /*test_cuts();*/
    /*}*/

}

module test_cuts()
{
    union()
    {
        offset = 0;
        /*offset = -5;*/
        translate([0,offset,0])
        translate([5,0,0])
        stack(dist=10,axis=[1,0,0])
        {
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=[0,0,1], align=[0,0,1]);

            screw_cut(nut=nut1, h=box_h*mm, nut_offset=0*mm, head_embed=true, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut2, h=box_h*mm, nut_offset=3*mm, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=[0,0,-1], align=[0,0,1]);

            /*translate([0,5,0])*/
            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=[0,-1,0], orient=[0,0,-1], align=[0,0,0]);

            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=[0,1,0], orient=[0,0,1], align=[0,0,0]);

            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_l_extra=2*mm, trap_axis=[0,0,1], orient=[0,1,0], align=[0,0,0]);

            translate([0,0,box_h/2])
                nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_l_extra=2*mm, trap_axis=[0,0,-1], orient=[0,1,0], align=[0,0,0]);
        }
    }
}


feat: allow to provide custom test elements by using child modules

/**
 * @license
 * MIT License
 *
 * Copyright (c) 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.
 *
 * Testing helpers for visual tests.
 *
 * @package shape/context
 * @author jsconan
 */

/**
 * Places the children at the proper test area.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param Number [index] - The index number of the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module placeVisualTest(length, width, index=0, margin=0, cols=0, center=false) {
    margin = vector2D(margin);
    x = cols ? index % cols : index;
    y = cols ? floor(index / cols) : 0;
    offset = center ? [length, width, 0] / 2 : ORIGIN_3D;

    translate(offset + [(length + margin.x) * x, (width + margin.y) * y, 0]) {
        children();
    }
}

/**
 * Renders a test element. If no children is given, a cube is rendered.
 * @param String|Vector [c] - The color of the element.
 * @param Number|Vector [size] - The size of the element. It only applies if no children is given.
 * @param Boolean [center] - Whether or not the element is centered. It only applies if no children is given.
 */
module testElement(c="red", size=1, center=false) {
    color(c) {
        if ($children)  {
            children();
        } else {
            cube(size=size, center=center);
        }
    }
}

/**
 * Colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedColor(alpha) {
    %color(TESTBED_COLOR, alpha) {
        children();
    }
}

/**
 * Extrudes and colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedExtrude(alpha) {
    testbedColor(alpha) {
        linear_extrude(height=TESTBED_THICKNESS, convexity=10, center=true) {
            children();
        }
    }
}

/**
 * Renders a test area, moving the tested shapes in it.
 * @param Number index - The index number of the test.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module visualTest(index, length, width, title="test", margin=1, cols=0, center=false) {
    title = str(title, " ", index);

    placeVisualTest(
        length = length,
        width = width,
        index = index,
        margin = margin,
        cols = cols,
        center = center
    ) {
        // test area
        testbedExtrude(.1) {
            square([length, width], center=center);
        }
        translate(center ? ORIGIN_3D : [length, width, 0] / 2) {
            testbedExtrude(1) {
                text(title, size=min(length, width) / len(title), font="Liberation Sans", valign="center", halign="center");
            }
        }

        // tested shapes
        children();
    }
}

/**
 * Renders a test area for each child module, moving the tested shapes in it.
 * A single test can be selected by its index thanks to the global variable `selectTest`.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not center the whole suite.
 * @param Boolean [centerEach] - Whether or not center each test area.
 */
module visualTestSuite(length, width, title="test", margin=1, cols=0, center=false, centerEach=false) {
    margin = vector2D(margin);
    count = $children;
    selectTest = is_undef(selectTest) ? undef : min(max(0, selectTest), count - 1);
    start = is_undef(selectTest) ? 0 : selectTest;
    end = is_undef(selectTest) ? count - 1 : selectTest;
    lines = cols ? ceil(count / cols) : 1;
    offset = center ? -([margin.x * (cols - 1), margin.y * (lines - 1), 0] + [length * cols, width * lines, 0]) / 2 : ORIGIN_3D;

    translate(offset) {
        for (i = [start : end]) {
            visualTest(
                index = i,
                length = length,
                width = width,
                title = title,
                margin = margin,
                cols = cols,
                center = centerEach
            ) {
                children(i);
            }
        }
    }
}

// Select a single test to show
selectTest = undef;

/**
 * @license
 * MIT License
 *
 * Copyright (c) 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.
 *
 * Testing helpers for visual tests.
 *
 * @package shape/context
 * @author jsconan
 */

/**
 * Places the children at the proper test area.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param Number [index] - The index number of the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module placeVisualTest(length, width, index=0, margin=0, cols=0, center=false) {
    margin = vector2D(margin);
    x = cols ? index % cols : index;
    y = cols ? floor(index / cols) : 0;
    offset = center ? [length, width, 0] / 2 : ORIGIN_3D;

    translate(offset + [(length + margin.x) * x, (width + margin.y) * y, 0]) {
        children();
    }
}

/**
 * Renders a test element.
 * @param String|Vector [c] - The color of the element.
 * @param Number|Vector [size] - The size of the element.
 * @param Boolean [center] - Whether or not the element is centered.
 */
module testElement(c="red", size=1, center=false) {
    color(c) {
        cube(size=size, center=center);
    }
}

/**
 * Colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedColor(alpha) {
    %color(TESTBED_COLOR, alpha) {
        children();
    }
}

/**
 * Extrudes and colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedExtrude(alpha) {
    testbedColor(alpha) {
        linear_extrude(height=TESTBED_THICKNESS, convexity=10, center=true) {
            children();
        }
    }
}

/**
 * Renders a test area, moving the tested shapes in it.
 * @param Number index - The index number of the test.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module visualTest(index, length, width, title="test", margin=1, cols=0, center=false) {
    title = str(title, " ", index);

    placeVisualTest(
        length = length,
        width = width,
        index = index,
        margin = margin,
        cols = cols,
        center = center
    ) {
        // test area
        testbedExtrude(.1) {
            square([length, width], center=center);
        }
        translate(center ? ORIGIN_3D : [length, width, 0] / 2) {
            testbedExtrude(1) {
                text(title, size=min(length, width) / len(title), font="Liberation Sans", valign="center", halign="center");
            }
        }

        // tested shapes
        children();
    }
}

/**
 * Renders a test area for each child module, moving the tested shapes in it.
 * A single test can be selected by its index thanks to the global variable `selectTest`.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not center the whole suite.
 * @param Boolean [centerEach] - Whether or not center each test area.
 */
module visualTestSuite(length, width, title="test", margin=1, cols=0, center=false, centerEach=false) {
    margin = vector2D(margin);
    count = $children;
    selectTest = is_undef(selectTest) ? undef : min(max(0, selectTest), count - 1);
    start = is_undef(selectTest) ? 0 : selectTest;
    end = is_undef(selectTest) ? count - 1 : selectTest;
    lines = cols ? ceil(count / cols) : 1;
    offset = center ? -([margin.x * (cols - 1), margin.y * (lines - 1), 0] + [length * cols, width * lines, 0]) / 2 : ORIGIN_3D;

    translate(offset) {
        for (i = [start : end]) {
            visualTest(
                index = i,
                length = length,
                width = width,
                title = title,
                margin = margin,
                cols = cols,
                center = centerEach
            ) {
                children(i);
            }
        }
    }
}

// Select a single test to show
selectTest = undef;