bigger button holes

bigger button holes

tol = 0.5;
screen_tol = 0.8;
pcb_x = 130;
pcb_y = 110;
pcb_z = 4;
bolt_d = 3.6;
shift_bolt = 5;
board_w = 1;
board_h = 1;

// box thickness
th = 2;

shift_x = 4;
shift_y = th + 1;

box_x = pcb_x + shift_x + 4;
box_y = pcb_y + shift_y + th;

pcb_width = 2;
pcb_up = 9;

box_z = th + pcb_up + pcb_width - 1;

socket_h = 15;
socket_w = 10;
socket_n = 5;
socket_int = 20;
start_socket_x = 20 + shift_x;
eps = 1e-1;

usb_type_b_w = 12;
usb_type_b_h = 11;
start_type_b_y = shift_y + 81.5;
//start_type_b_z = th + pcb_z + tol;

usb_type_a_w = 14.4;
usb_type_a_h = 7.8;
start_type_a_y = shift_y + 46;
//start_type_a_z = th + pcb_z + tol;

// programmator
micro_x = 10;
micro_y = 21.1;
micro_start_x = shift_x + 4.75;
micro_start_y = shift_y + pcb_y - 23.3 - micro_y;

contrast_start_x = shift_x + 38.4;
contrast_start_y = shift_y + 25.7;
volume_start_x = shift_x + pcb_x - 39.2;
volume_start_y = contrast_start_y;
pot_d = 7;
under_pcb = 13;
fn = 10;
gaika_h = 4;
gaika_w = 5.5;
stoiki_r = 4;
wide_stoiki_r = stoiki_r + 2;

translate([0,0,-50]) {

difference() {
  union() {
    difference() {
      union() {
        cube([box_x,box_y, box_z-board_h], false);
        translate([th - board_w, th - board_w, 0]) {
          cube([box_x - 2*(th - board_w), box_y - 2*(th -board_w), box_z], false);
        }
      }
      translate([th, th, th]) {
        cube([box_x-2*th+eps, box_y-2*th+eps, box_z-th+eps], false);
      }
    }
    // under_pcb, stoiki
    for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
      for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
//        translate([dx,dy,pcb_up/2+th-eps]) {
//          cube(size=[under_pcb,under_pcb,pcb_up], center=true);
//        }
        translate([dx,dy,th-eps]) {
          cylinder($fn=6, r=stoiki_r, h=pcb_up);
          cylinder($fn=6, r=wide_stoiki_r, h=gaika_h);
        }
      }
    }
    translate([box_x/2, box_y/2, th-eps]) {
      cylinder($fn=6, r=stoiki_r, h=pcb_up);
    }
  }

  // bolts
  for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
    for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
      translate([dx,dy,-eps]) {
        cylinder($fn=fn, r=bolt_d/2, h=th+pcb_up+2*eps);
        // gaika
        cylinder($fn=6, r=gaika_w / 2 / cos(30) + 0.05, h=gaika_h);
      }
    }
  }

//    // usb_in1 type b
//    translate([box_x-th-eps, start_type_b_y-tol, start_type_b_z]) {
//        cube(size=[th+2*eps, usb_type_b_w+2*tol, box_z]);
//    }

//    // usb_out1 type a
//    translate([-eps, start_type_a_y - tol, start_type_a_z]) {
//        cube(size=[th+2*eps, usb_type_a_w + 2*tol, box_z]);
//    }

    // programmator
    translate([micro_start_x-tol,micro_start_y-tol, -eps]) {
      cube(size=[micro_x+2*tol, micro_y+2*tol, th+2*eps]);
    }

    // contrast
    translate([contrast_start_x, contrast_start_y, -eps]) {
      cylinder($fn=fn, r=pot_d/2, h=th+2*eps);
    }

    // volume
//    translate([volume_start_x, volume_start_y, -eps]) {
//      cylinder($fn=20, r=pot_d/2, h=th+2*eps);
//    }
}
}

top_box_z = 15 + th;
stoiki_h = top_box_z - th;
wide_stoiki_h = 4;
pcb_room = 1;
screen_start_x = shift_x + 29;
screen_start_y = shift_y + 38.9;
screen_length_x = 71.2;
screen_length_y = 24.2;
screen_hold_x = 40;
screen_hold_y = 4;
screen_hold_start_z = 12.5;
screen_hold_y_shift = 2;
screen_hold_up_x = 15;
screen_hold_up_x_shift = 5;

master_button_y = shift_y + 10;
master_button_x = shift_x + 43;
master_button_int = 21.8;
master_button_r = 5;
top_bolt_h = 1.5;
top_bolt_r = 3.5;
speaker_shift = 18;
speaker_r = 15;
speaker_h = 15;
speaker_hole_d = 10;
speaker_hole_int = 3;
speaker_hole_w = 1.5;
speaker_shift = 18;

!difference() {
  union() {
    // board
    translate([0, 0, -board_h]) {
	difference() {
	    cube([box_x, box_y, board_h + eps], false);
	    translate([board_w, board_w, -eps]) {
		cube([box_x - 2*board_w, box_y - 2*board_w, board_h + 3*eps], false);
	    }
	}
    }
    difference() {
      cube([box_x,box_y,top_box_z], false);
      translate([th, th, -eps]) {
        cube([box_x-2*th+eps, box_y-2*th+eps, top_box_z-th+eps], false);
      }
    }
    // stoiki
    for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
      for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
        translate([dx,dy,pcb_room]) {
          cylinder($fn=6, r=stoiki_r, h=top_box_z-th+eps);
        }
        translate([dx,dy,top_box_z-th-wide_stoiki_h+eps]) {
          cylinder($fn=6, r=wide_stoiki_r, h=wide_stoiki_h+th-2*eps);
        }
      }
    }
    // screen_hold
    translate([screen_start_x + (screen_length_x - screen_hold_x)/2, screen_start_y - screen_hold_y_shift - screen_hold_y, screen_hold_start_z]) {
	cube([screen_hold_x, screen_hold_y, top_box_z - screen_hold_start_z - th + eps]);
    }
    translate([screen_start_x + screen_length_x - screen_hold_up_x - screen_hold_up_x_shift, screen_start_y + screen_length_y + screen_hold_y_shift, screen_hold_start_z]) {
	cube([screen_hold_up_x, screen_hold_y, top_box_z - screen_hold_start_z - th + eps]);
    }
  }
  // - speaker na stoikah
  translate([shift_x + pcb_x - 18, shift_y + 18, 0]) {
    cylinder(r=speaker_r, h = 15, $fn=20);
  }
  // speaker_hole
  for (dy = [shift_y + speaker_shift - speaker_hole_int - speaker_hole_w*3/2,
             shift_y + speaker_shift - speaker_hole_w/2,
             shift_y + speaker_shift + speaker_hole_int + speaker_hole_w/2]) {
    translate([shift_x + pcb_x - speaker_shift - speaker_hole_d, dy, top_box_z-th-eps]) {
      cube(size=[speaker_hole_d, speaker_hole_w, th + 2*eps]);
    }
  }

  // bolts
  for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
    for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
      translate([dx,dy,-eps]) {
        cylinder($fn=fn, r=bolt_d/2, h=top_box_z+2*eps);
      }
      translate([dx, dy, top_box_z - top_bolt_h]) {
        //cylinder($fn=6, r=gaika_w / 2 / cos(30) + 0.05, h=wide_stoiki_h+eps);
        cylinder($fn=6, r=top_bolt_r, h=top_bolt_h + eps);
      }
    }
  }

  // screen
  translate([screen_start_x - screen_tol, screen_start_y - screen_tol, top_box_z - th - eps]) {
      cube([screen_length_x + 2*screen_tol, screen_length_y + 2*screen_tol, th + 2*eps]);
  }
  // sockets
  for (i = [0:1:socket_n-1]) {
      translate([start_socket_x+i*socket_int - tol, box_y-th-eps, -eps - board_h]) {
        cube(size=[socket_w + 2*tol, th+2*eps, socket_h + board_h]);
      }
  }
  // type_b
  translate([box_x - th - eps, start_type_b_y - tol, -tol - board_h]) {
      cube([th + 2*eps, usb_type_b_w + 2*tol, usb_type_b_h + 2*tol + board_h]);
  }
  // type_a
  translate([-eps, start_type_a_y - tol, -eps - board_h]) {
      cube([th + 2*eps, usb_type_a_w + 2*tol, usb_type_a_h + 2*eps + board_h]);
  }
  // master buttons
  for (i = [0:1:2]) {
    translate([master_button_x + master_button_int*i, master_button_y, top_box_z - th - eps]) {
      cylinder($fn=fn, r=master_button_r + tol, h=th+2*eps);
    }
  }
}

//// bolts
//for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
//  for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
//    difference() {
//      translate([dx,dy,pcb_up/2+th]) {
//        cube(size=[a,a,pcb_up], center=true);
//      }
//      translate([dx,dy,-eps]) {
//        cylinder($fn=20, r=bolt_d/2, h=th+pcb_up+2*eps);
//      }
//    }
//  }
//}

//leg_r = t;
//shift = t/2+leg_r;
//for (dx = [shift, a-shift])
//    for (dy = [shift, b-shift]) {
//        translate([dx, dy, 0]) {
//            cylinder(h=c, r=leg_r, $fn=40);
//        }
//    }

tol = 0.5;
screen_tol = 0.8;
pcb_x = 130;
pcb_y = 110;
pcb_z = 4;
bolt_d = 3.6;
shift_bolt = 5;
board_w = 1;
board_h = 1;

// box thickness
th = 2;

shift_x = 4;
shift_y = th + 1;

box_x = pcb_x + shift_x + 4;
box_y = pcb_y + shift_y + th;

pcb_width = 2;
pcb_up = 9;

box_z = th + pcb_up + pcb_width - 1;

socket_h = 15;
socket_w = 10;
socket_n = 5;
socket_int = 20;
start_socket_x = 20 + shift_x;
eps = 1e-1;

usb_type_b_w = 12;
usb_type_b_h = 11;
start_type_b_y = shift_y + 81.5;
//start_type_b_z = th + pcb_z + tol;

usb_type_a_w = 14.4;
usb_type_a_h = 7.8;
start_type_a_y = shift_y + 46;
//start_type_a_z = th + pcb_z + tol;

// programmator
micro_x = 10;
micro_y = 21.1;
micro_start_x = shift_x + 4.75;
micro_start_y = shift_y + pcb_y - 23.3 - micro_y;

contrast_start_x = shift_x + 38.4;
contrast_start_y = shift_y + 25.7;
volume_start_x = shift_x + pcb_x - 39.2;
volume_start_y = contrast_start_y;
pot_d = 7;
under_pcb = 13;
fn = 10;
gaika_h = 4;
gaika_w = 5.5;
stoiki_r = 4;
wide_stoiki_r = stoiki_r + 2;

translate([0,0,-50]) {

difference() {
  union() {
    difference() {
      union() {
        cube([box_x,box_y, box_z-board_h], false);
        translate([th - board_w, th - board_w, 0]) {
          cube([box_x - 2*(th - board_w), box_y - 2*(th -board_w), box_z], false);
        }
      }
      translate([th, th, th]) {
        cube([box_x-2*th+eps, box_y-2*th+eps, box_z-th+eps], false);
      }
    }
    // under_pcb, stoiki
    for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
      for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
//        translate([dx,dy,pcb_up/2+th-eps]) {
//          cube(size=[under_pcb,under_pcb,pcb_up], center=true);
//        }
        translate([dx,dy,th-eps]) {
          cylinder($fn=6, r=stoiki_r, h=pcb_up);
          cylinder($fn=6, r=wide_stoiki_r, h=gaika_h);
        }
      }
    }
    translate([box_x/2, box_y/2, th-eps]) {
      cylinder($fn=6, r=stoiki_r, h=pcb_up);
    }
  }

  // bolts
  for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
    for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
      translate([dx,dy,-eps]) {
        cylinder($fn=fn, r=bolt_d/2, h=th+pcb_up+2*eps);
        // gaika
        cylinder($fn=6, r=gaika_w / 2 / cos(30) + 0.05, h=gaika_h);
      }
    }
  }

//    // usb_in1 type b
//    translate([box_x-th-eps, start_type_b_y-tol, start_type_b_z]) {
//        cube(size=[th+2*eps, usb_type_b_w+2*tol, box_z]);
//    }

//    // usb_out1 type a
//    translate([-eps, start_type_a_y - tol, start_type_a_z]) {
//        cube(size=[th+2*eps, usb_type_a_w + 2*tol, box_z]);
//    }

    // programmator
    translate([micro_start_x-tol,micro_start_y-tol, -eps]) {
      cube(size=[micro_x+2*tol, micro_y+2*tol, th+2*eps]);
    }

    // contrast
    translate([contrast_start_x, contrast_start_y, -eps]) {
      cylinder($fn=fn, r=pot_d/2, h=th+2*eps);
    }

    // volume
//    translate([volume_start_x, volume_start_y, -eps]) {
//      cylinder($fn=20, r=pot_d/2, h=th+2*eps);
//    }
}
}

top_box_z = 15 + th;
stoiki_h = top_box_z - th;
wide_stoiki_h = 4;
pcb_room = 1;
screen_start_x = shift_x + 29;
screen_start_y = shift_y + 38.9;
screen_length_x = 71.2;
screen_length_y = 24.2;
screen_hold_x = 40;
screen_hold_y = 4;
screen_hold_start_z = 12.5;
screen_hold_y_shift = 2;
screen_hold_up_x = 15;
screen_hold_up_x_shift = 5;

master_button_y = shift_y + 10;
master_button_x = shift_x + 43;
master_button_int = 21.8;
master_button_r = 3;
top_bolt_h = 1.5;
top_bolt_r = 3.5;
speaker_shift = 18;
speaker_r = 15;
speaker_h = 15;
speaker_hole_d = 10;
speaker_hole_int = 3;
speaker_hole_w = 1.5;
speaker_shift = 18;

!difference() {
  union() {
    // board
    translate([0, 0, -board_h]) {
	difference() {
	    cube([box_x, box_y, board_h + eps], false);
	    translate([board_w, board_w, -eps]) {
		cube([box_x - 2*board_w, box_y - 2*board_w, board_h + 3*eps], false);
	    }
	}
    }
    difference() {
      cube([box_x,box_y,top_box_z], false);
      translate([th, th, -eps]) {
        cube([box_x-2*th+eps, box_y-2*th+eps, top_box_z-th+eps], false);
      }
    }
    // stoiki
    for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
      for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
        translate([dx,dy,pcb_room]) {
          cylinder($fn=6, r=stoiki_r, h=top_box_z-th+eps);
        }
        translate([dx,dy,top_box_z-th-wide_stoiki_h+eps]) {
          cylinder($fn=6, r=wide_stoiki_r, h=wide_stoiki_h+th-2*eps);
        }
      }
    }
    // screen_hold
    translate([screen_start_x + (screen_length_x - screen_hold_x)/2, screen_start_y - screen_hold_y_shift - screen_hold_y, screen_hold_start_z]) {
	cube([screen_hold_x, screen_hold_y, top_box_z - screen_hold_start_z - th + eps]);
    }
    translate([screen_start_x + screen_length_x - screen_hold_up_x - screen_hold_up_x_shift, screen_start_y + screen_length_y + screen_hold_y_shift, screen_hold_start_z]) {
	cube([screen_hold_up_x, screen_hold_y, top_box_z - screen_hold_start_z - th + eps]);
    }
  }
  // - speaker na stoikah
  translate([shift_x + pcb_x - 18, shift_y + 18, 0]) {
    cylinder(r=speaker_r, h = 15, $fn=20);
  }
  // speaker_hole
  for (dy = [shift_y + speaker_shift - speaker_hole_int - speaker_hole_w*3/2,
             shift_y + speaker_shift - speaker_hole_w/2,
             shift_y + speaker_shift + speaker_hole_int + speaker_hole_w/2]) {
    translate([shift_x + pcb_x - speaker_shift - speaker_hole_d, dy, top_box_z-th-eps]) {
      cube(size=[speaker_hole_d, speaker_hole_w, th + 2*eps]);
    }
  }

  // bolts
  for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
    for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
      translate([dx,dy,-eps]) {
        cylinder($fn=fn, r=bolt_d/2, h=top_box_z+2*eps);
      }
      translate([dx, dy, top_box_z - top_bolt_h]) {
        //cylinder($fn=6, r=gaika_w / 2 / cos(30) + 0.05, h=wide_stoiki_h+eps);
        cylinder($fn=6, r=top_bolt_r, h=top_bolt_h + eps);
      }
    }
  }

  // screen
  translate([screen_start_x - screen_tol, screen_start_y - screen_tol, top_box_z - th - eps]) {
      cube([screen_length_x + 2*screen_tol, screen_length_y + 2*screen_tol, th + 2*eps]);
  }
  // sockets
  for (i = [0:1:socket_n-1]) {
      translate([start_socket_x+i*socket_int - tol, box_y-th-eps, -eps - board_h]) {
        cube(size=[socket_w + 2*tol, th+2*eps, socket_h + board_h]);
      }
  }
  // type_b
  translate([box_x - th - eps, start_type_b_y - tol, -tol - board_h]) {
      cube([th + 2*eps, usb_type_b_w + 2*tol, usb_type_b_h + 2*tol + board_h]);
  }
  // type_a
  translate([-eps, start_type_a_y - tol, -eps - board_h]) {
      cube([th + 2*eps, usb_type_a_w + 2*tol, usb_type_a_h + 2*eps + board_h]);
  }
  // master buttons
  for (i = [0:1:2]) {
    translate([master_button_x + master_button_int*i, master_button_y, top_box_z - th - eps]) {
      cylinder($fn=fn, r=master_button_r + tol, h=th+2*eps);
    }
  }
}

//// bolts
//for (dx = [shift_x+shift_bolt, shift_x+pcb_x-shift_bolt]) {
//  for (dy = [shift_y+shift_bolt, shift_y+pcb_y-shift_bolt]) {
//    difference() {
//      translate([dx,dy,pcb_up/2+th]) {
//        cube(size=[a,a,pcb_up], center=true);
//      }
//      translate([dx,dy,-eps]) {
//        cylinder($fn=20, r=bolt_d/2, h=th+pcb_up+2*eps);
//      }
//    }
//  }
//}

//leg_r = t;
//shift = t/2+leg_r;
//for (dx = [shift, a-shift])
//    for (dy = [shift, b-shift]) {
//        translate([dx, dy, 0]) {
//            cylinder(h=c, r=leg_r, $fn=40);
//        }
//    }


doc: typo in doc

doc: typo in doc

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

/**
 * Part of the camelSCAD library.
 *
 * Miscellaneous utils.
 *
 * @package core/util
 * @author jsconan
 */

/**
 * Builds a position value:
 * -1: there is something before
 *  0: there is nothing anywhere
 *  1: there is something after
 *  2: there is something before and after
 *
 * @param Boolean [before] - If there is something before.
 * @param Boolean [after] - If there is something after.
 * @returns Number
 */
function position(before, after) =
    before && after ? 2
   :after ? 1
   :before ? -1
   :0
;

/**
 * Checks a position value and tells if there is something before.
 * -1: there is something before
 *  0: there is nothing anywhere
 *  1: there is something after
 *  2: there is something before and after
 *
 * @param Number position - The position value.
 * @returns Boolean
 */
function before(position) =
    let( position = float(position) )
    position < 0 || position > 1
;

/**
 * Checks a position value and tells if there is something after.
 * -1: there is something before
 *  0: there is nothing anywhere
 *  1: there is something after
 *  2: there is something before and after
 *
 * @param Number position - The position value.
 * @returns Boolean
 */
function after(position) = float(position) > 0;

/**
 * Gets a numeric representation of a cardinal point.
 * West and East are related to X-coordinate (resp. -1 and 1, or 2 if both).
 * South and North are related to Y-coordinate (resp. -1 and 1, or 2 if both).
 *
 * A cardinal expression is a string that contains cardinal point literals:
 * - "n": North
 * - "e": East
 * - "s": South
 * - "w": West
 *
 * Combinations are allowed, for instance
 * - "ne" means North East, and will be translated to [1, 1]
 * - "nwe" means North West East, and will be translated to [2, 1]
 * - "nsw" means North South West, and will be translated to [-1, 2]
 * - "nswe" means North South West East, and will be translated to [2, 2]
 *
 * @param Vector|String p - A vector or a cardinal expression.
 * @returns Vector - A 2D vector that contains position values for horizontal and vertical axis.
 */
function cardinal(p) =
    is_num(p) || isVector(p) ?
        let( p = vector2D(p) )
        [ position(before(p.x), after(p.x)),
          position(before(p.y), after(p.y)) ]
    :
        let( p = p && p != true ? str(p) : "" )
        [ position(min(len(search("wW", p)), 1), min(len(search("eE", p)), 1)),
          position(min(len(search("sS", p)), 1), min(len(search("nN", p)), 1)) ]
;

/**
 * Computes a size value, and its offset, of an object to be used as a negative for a `difference()` operation.
 * An alignment value will be added according to the parameter `direction` to counter the wall alignment issue.
 *
 * @param Number [value] - The value of the size to adjust. If the value is negative, the offset will be adjusted
 *                         to allow top to bottom position (below the origin).
 * @param Number|String [direction] - Tells on what sides adjust the size to make sure the difference won't
 *                                    produce dummy walls.
 * @param Boolean [center] - Whether or not center the position on the axis.
 * @returns Vector - A 2D vector that contains the adjusted size and its offset.
 */
function align(value, direction, center) =
    let(
        direction = cardinal(uor(direction, 2)).y,
        value = divisor(value),
        absv = abs(value),
        adjust = direction ? (direction == 2 ? ALIGN2 : ALIGN) : 0,
        offset = center ? (direction == 2 || direction == 0 ? 0 : direction * ALIGN / 2)
                        : (direction == 2 || direction == -1 ? -ALIGN : 0)
    )
    [
        absv + adjust,
        offset + (value < 0 && !center ? value : 0)
    ]
;

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

/**
 * Part of the camelSCAD library.
 *
 * Miscellaneous utils.
 *
 * @package core/util
 * @author jsconan
 */

/**
 * Builds a position value:
 * -1: there is something before
 *  0: there is nothing anywhere
 *  1: there is something after
 *  2: there is something before and after
 *
 * @param Boolean [before] - If there is something before.
 * @param Boolean [after] - If there is something after.
 * @returns Number
 */
function position(before, after) =
    before && after ? 2
   :after ? 1
   :before ? -1
   :0
;

/**
 * Checks a position value and tells if there is something before.
 * -1: there is something before
 *  0: there is nothing anywhere
 *  1: there is something after
 *  2: there is something before and after
 *
 * @param Number position - The position value.
 * @returns Boolean
 */
function before(position) =
    let( position = float(position) )
    position < 0 || position > 1
;

/**
 * Checks a position value and tells if there is something after.
 * -1: there is something before
 *  0: there is nothing anywhere
 *  1: there is something after
 *  2: there is something before and after
 *
 * @param Number position - The position value.
 * @returns Boolean
 */
function after(position) = float(position) > 0;

/**
 * Gets a numeric representation of a cardinal point.
 * West and Eath are related to X-coordinate (resp. -1 and 1, or 2 if both).
 * South and North are related to Y-coordinate (resp. -1 and 1, or 2 if both).
 *
 * A cardinal expression is a string that contains cardinal point literals:
 * - "n": North
 * - "e": East
 * - "s": South
 * - "w": West
 *
 * Combinations are allowed, for instance
 * - "ne" means North East, and will be translated to [1, 1]
 * - "nwe" means North Weast East, and will be translated to [2, 1]
 * - "nsw" means North South Weast, and will be translated to [-1, 2]
 * - "nswe" means North South Weast East, and will be translated to [2, 2]
 *
 * @param Vector|String p - A vector or a cardinal expression.
 * @returns Vector - A 2D vector that contains position values for horizontal and vertical axis.
 */
function cardinal(p) =
    is_num(p) || isVector(p) ?
        let( p = vector2D(p) )
        [ position(before(p.x), after(p.x)),
          position(before(p.y), after(p.y)) ]
    :
        let( p = p && p != true ? str(p) : "" )
        [ position(min(len(search("wW", p)), 1), min(len(search("eE", p)), 1)),
          position(min(len(search("sS", p)), 1), min(len(search("nN", p)), 1)) ]
;

/**
 * Computes a size value, and its offset, of an object to be used as a negative for a `difference()` operation.
 * An alignment value will be added according to the parameter `direction` to counter the wall alignment issue.
 *
 * @param Number [value] - The value of the size to adjust. If the value is negative, the offset will be adjusted
 *                         to allow top to bottom position (below the origin).
 * @param Number|String [direction] - Tells on what sides adjust the size to make sure the difference won't
 *                                    produce dummy walls.
 * @param Boolean [center] - Whether or not center the position on the axis.
 * @returns Vector - A 2D vector that contains the adjusted size and its offset.
 */
function align(value, direction, center) =
    let(
        direction = cardinal(uor(direction, 2)).y,
        value = divisor(value),
        absv = abs(value),
        adjust = direction ? (direction == 2 ? ALIGN2 : ALIGN) : 0,
        offset = center ? (direction == 2 || direction == 0 ? 0 : direction * ALIGN / 2)
                        : (direction == 2 || direction == -1 ? -ALIGN : 0)
    )
    [
        absv + adjust,
        offset + (value < 0 && !center ? value : 0)
    ]
;


increased plate length + removed positioning hacks

// TODO: add names engravings

module rounded_surface_(size, r)
{
  assert(len(size)==2);
  hull()
    for(dx=[r, size[0]-r])
      for(dy=[r, size[1]-r])
        translate([dx, dy, r])
          sphere(r=r, $fn=r*20);
}

module rounded_half_surface_(size, r)
{
  assert(len(size)==2);
  translate([0, 0, -r])
    difference()
    {
      rounded_surface_(size, r);
      cube([size[0], size[1], r]);
    }
}

// element for holding a single brush
module holder()
{
  translate([-4/2, 0, 0])
    cube([4, 7, 17]);
  translate([0, 0, 17-2])
  {
    rotate([-90, 0, 0])
      cylinder(r=(8-1.5)/2, h=7+2+7, $fn=50);
    translate([0, 7, 0])
      rotate([-90,0,0])
        cylinder(r=(3-1)/2, h=14+2, $fn=30);
  }
}

module whole_holder(names)
{
  assert( len(names) > 0 );
  r=5;
  spacing=20;
  total_len=2*(r+6) + spacing*(len(names)-1) + 4;

  // plate
  translate([0, r+2, -r])
    rotate([90, 0, 0])
      difference()
      {
        rounded_surface_([total_len, 40], r/2+1);
        cube([total_len, r, r*2]);
      }
  // back wall
  rounded_half_surface_([total_len, 60], r);
  // holders + names
  translate([r+6+4/2, 25, r])
  {
    for(i=[0:len(names)-1])
      translate(i*[spacing, 0, 0])
      {
        holder();
        #translate([0, 25, 0])
          linear_extrude(3*0.2)
            text(names[i], size=4, halign="center");
      }
  }
}

whole_holder(["John", "Jane", "Jackob"]);

// TODO: add names engravings

module rounded_surface_(size, r)
{
  assert(len(size)==2);
  hull()
    for(dx=[r, size[0]-r])
      for(dy=[r, size[1]-r])
        translate([dx, dy, r])
          sphere(r=r, $fn=r*5); // TODO
}

module rounded_half_surface_(size, r)
{
  assert(len(size)==2);
  translate([0, 0, -r])
    difference()
    {
      rounded_surface_(size, r);
      cube([size[0], size[1], r]);
    }
}

// element for holding a single brush
module holder()
{
  translate([-4/2, 0, 0])
    cube([4, 7, 17]);
  translate([0, 0, 17-2])
  {
    rotate([-90, 0, 0])
      cylinder(r=(8-1.5)/2, h=7+2+7, $fn=50);
    translate([0, 7, 0])
      rotate([-90,0,0])
        cylinder(r=(3-1)/2, h=14+2, $fn=30);
  }
}

module whole_holder(names)
{
  assert( len(names) > 0 );
  r=5;
  spacing=20;
  total_len=2*(r+6) + spacing*(len(names)-1) + 4;

  // plate
  translate([0, r+2, -r])
    rotate([90, 0, 0])
      difference()
      {
        rounded_surface_([total_len, 35], r/2+1);
        cube([total_len, r, r*2]);
      }
  // back wall
  rounded_half_surface_([total_len, 60], r);
  // holders + names
  translate([r+6+4/2, 25, r-1])
  {
    for(i=[0:len(names)-1])
      translate(i*[spacing, 0, 0])
      {
        holder();
        translate([0, 20, 0.9])
          linear_extrude(3*0.2)
            text(names[i], size=4, halign="center");
      }
  }
}

whole_holder(["John", "Jane", "Jackob"]);


minor refinements

minor refinements

include <../config/config.scad>

DebugCoordinateFrames = true;
DebugConnectors = true;

sw = 2 * 0.5;  // approx 2 perimeters

function sqr(a) = a*a;

function circumference(radius) = 2 * PI * radius;
function arcLength(radius, theta) = PI * radius * theta / 180;
function chordLength(radius, theta) = radius * sqrt(2 - 2*cos(theta));
function segmentHeight(radius, theta) = radius - sqrt(sqr(radius) - sqr(chordLength(radius, theta))/4);
function segmentDistance(radius,theta) = radius - segmentHeight(radius,theta);

// 2D
// in XY plane
// chord begins at intersection of x+
// extends towards y+
// theta should be <= 180 degrees
module circularSegment(radius, theta) {
	intersection() {
		circle(radius);

		// chord hull
		polygon(
		[
			[radius, 0],
			[2*radius * cos(theta*0.25), 2*radius * sin(theta*0.1)],
			[2*radius * cos(theta*0.5), 2*radius * sin(theta*0.5)],
			[2*radius * cos(theta*0.75), 2*radius * sin(theta*0.9)],
			[radius * cos(theta), radius * sin(theta)]
		], 5);
	}
}

module domeSupportSegment(radius=100, inset=0, thickness=1, supportAngle=45) {
	insetAng = asin(inset/radius);
	theta = 180 - 2*supportAngle - 2*insetAng;

	sh = segmentHeight(radius,theta);

	if (sh > 2)
		rotate([0,0,90 - theta - insetAng])
		translate([0,0,-thickness/2])
		linear_extrude(thickness)
		circularSegment(radius,theta);
}

module shell() {

	or = BaseDiameter/2 + Shell_NotchTol + sw;

	sr = or - sw + eta;

	supportAngle = 50;
	bridgeDist = 6;

	numRibs1 = round(circumference(sr * cos(supportAngle)) / bridgeDist);
	numRibs = 4 * (floor(numRibs1 / 4) + 1);

	$fn=64;

	// shell with hole for LED
	//render()
		difference() {
			union() {
				// curved shell with centre hole for pen
				rotate_extrude()
					difference() {
						// outer shell
						donutSector2D(
							or=or,
							ir=BaseDiameter/2 + Shell_NotchTol,
							a=90
						);

						// clearance for pen
						square([PenHoleDiameter/2, BaseDiameter]);

					}

				// large support ribs
				for (i=[0:numRibs/4])
					rotate([0,0,i*360/(numRibs/4)])
					rotate([90,0,0])
					domeSupportSegment(sr, PenHoleDiameter/2, perim, supportAngle);

				// medium support ribs
				for (i=[0:numRibs/4])
					rotate([0,0,i*360/(numRibs/4) + 360/(numRibs/2)])
					rotate([90,0,0])
					domeSupportSegment(sr, PenHoleDiameter/2 + 2*bridgeDist, perim, supportAngle);

				// small support ribs
				for (i=[0:numRibs/2])
					rotate([0,0,i*360/(numRibs/2) + 360/(numRibs)])
					rotate([90,0,0])
					domeSupportSegment(sr, PenHoleDiameter/2 + 4*bridgeDist, perim, supportAngle);
			}
		}
}

shell();

include <../config/config.scad>

DebugCoordinateFrames = true;
DebugConnectors = true;

sw = 2 * 0.5;  // approx 2 perimeters

function sqr(a) = a*a;

function circumference(radius) = 2 * PI * radius;
function arcLength(radius, theta) = PI * radius * theta / 180;
function chordLength(radius, theta) = radius * sqrt(2 - 2*cos(theta));
function segmentHeight(radius, theta) = radius - sqrt(sqr(radius) - sqr(chordLength(radius, theta))/4);
function segmentDistance(radius,theta) = radius - segmentHeight(radius,theta);

// 2D
// in XY plane
// chord begins at intersection of x+
// extends towards y+
// theta should be <= 180 degrees
module circularSegment(radius, theta) {
	intersection() {
		circle(radius);

		// chord hull
		polygon(
		[
			[radius, 0],
			[2*radius * cos(theta*0.25), 2*radius * sin(theta*0.1)],
			[2*radius * cos(theta*0.5), 2*radius * sin(theta*0.5)],
			[2*radius * cos(theta*0.75), 2*radius * sin(theta*0.9)],
			[radius * cos(theta), radius * sin(theta)]
		], 5);
	}
}

module domeSupportSegment(radius=100, inset=0, thickness=1, supportAngle=45) {
	insetAng = asin(inset/radius);
	theta = 180 - 2*supportAngle - 2*insetAng;

	rotate([0,0,90 - theta - insetAng])
		linear_extrude(thickness)
		circularSegment(radius,theta);
}

module shell() {

	supportAngle = 50;
	bridgeDist = 6;

	numRibs = round(circumference(BaseDiameter/2 * cos(90-supportAngle)) / bridgeDist);
	echo(numRibs);

	// shell with hole for LED
	//render()
		difference() {
			union() {
				// curved shell with centre hole for pen
				rotate_extrude()
					difference() {
						// outer shell
						donutSector2D(
							or=BaseDiameter/2 + Shell_NotchTol + sw,
							ir=BaseDiameter/2 + Shell_NotchTol,
							a=90
						);

						// clearance for pen
						square([PenHoleDiameter/2, BaseDiameter]);

					}

				// large support ribs
				for (i=[0:numRibs/4])
					rotate([0,0,i*360/(numRibs/4)])
					rotate([90,0,0])
					domeSupportSegment(BaseDiameter/2, PenHoleDiameter/2, perim, supportAngle);

				// medium support ribs
				for (i=[0:numRibs/4])
					rotate([0,0,i*360/(numRibs/4) + 360/(numRibs/2)])
					rotate([90,0,0])
					domeSupportSegment(BaseDiameter/2, PenHoleDiameter/2 + 10, perim, supportAngle);

				// small support ribs
				for (i=[0:numRibs/2])
					rotate([0,0,i*360/(numRibs/2) + 360/(numRibs)])
					rotate([90,0,0])
					domeSupportSegment(BaseDiameter/2, PenHoleDiameter/2 + 20, perim, supportAngle);
			}

			// section
			*translate([-100,-200,-100])
				cube([200,200,200]);
		}
}

shell();


pulley: minor fixes

pulley: minor fixes

include <thing_libutils/units.scad>
include <thing_libutils/shapes.scad>

// h, full_h, inner_d, outer_d, walls, bore
pulley_2GT_20T_idler = [8.65*mm, undef, 12*mm, 18*mm, 1*mm, 5*mm];
pulley_2GT_20T       = [8.65*mm, 16*mm, 12*mm, 16*mm, 1*mm, 5*mm];

module pulley(pulley=pulley_2GT_20T, align=[0,0,0], orient = [0,0,1])
{
    is_idler = pulley[1] == undef;
    pulley_full(
            is_idler=is_idler,
            h=pulley[0],
            full_h=is_idler?0:pulley[1],
            inner_d=pulley[2],
            outer_d=pulley[3],
            walls=pulley[4],
            bore=pulley[5],
            align=align,
            orient=orient
            );
}

module pulley_full(h, inner_d, outer_d, bore, walls, is_idler=false, full_h, align=[0,0,0], orient = [0,0,1])
{
    size_align(size=[outer_d, outer_d, h], align=align, orient=orient)
    {
        difference()
        {
            union()
            {
                for(z=[-1,1])
                    translate([0,0,z*h/2])
                        fncylindera(d = outer_d, h = walls, align=[0,0,-z], orient=[0,0,1]);

                fncylindera(d = inner_d, h = h, align=[0,0,0], orient=[0,0,1]);

                if(!is_idler)
                {
                    translate([0,0,h-walls])
                        fncylindera(d = outer_d, h = full_h-h, align=[0,0,0], orient=[0,0,1]);
                }
            }
            fncylindera(d = bore, h = full_h, align=[0,0,0], orient=[0,0,1]);
        }
    }
}

/*debug = false;*/
if(debug)
{
    /*pulley(pulley_2GT_20T_idler, align=[0,0,1], orient=[0,0,1]);*/
    /*pulley(pulley_2GT_20T, align=[0,0,1], orient=[0,0,1]);*/
}

// h, full_h, inner_d, outer_d, walls, bore
pulley_2GT_20T_idler = [8.65*mm, undef, 12*mm, 18*mm, 1*mm, 5*mm];
pulley_2GT_20T       = [8.65*mm, 16*mm, 12*mm, 16*mm, 1*mm, 5*mm];

module pulley(pulley=pulley_2GT_20T, align=[0,0,0], orient = [0,0,1])
{
    is_idler = pulley[1] == undef;
    pulley_full(
            is_idler=is_idler,
            h=pulley[0],
            full_h=is_idler?0:pulley[1],
            inner_d=pulley[2],
            outer_d=pulley[3],
            walls=pulley[4],
            bore=pulley[5],
            align=align,
            orient=orient
            );
}

module pulley_full(h, inner_d, outer_d, bore, walls, is_idler=false, full_h, align=[0,0,0], orient = [0,0,1])
{
    size_align(size=[outer_d, outer_d, h], align=align, orient=orient)
    {
        difference()
        {
            union()
            {
                for(z=[-1,1])
                    translate([0,0,z*h/2])
                        fncylindera(d = outer_d, h = walls, align=[0,0,-z], orient=[0,0,1]);

                fncylindera(d = inner_d, h = h, align=[0,0,0], orient=[0,0,1]);

                if(!is_idler)
                {
                    translate([0,0,h-walls])
                        fncylindera(d = outer_d, h = full_h-h, align=[0,0,0], orient=[0,0,1]);
                }
            }
            fncylindera(d = bore, h = h+full_h, align=[0,0,0], orient=[0,0,1]);
        }
    }
}

/*pulley(pulley_2GT_20T_idler, align, orient);*/
/*pulley(pulley_2GT_20T, align=align, orient=orient);*/


fixup! shapes: add cuberounda

fixup! shapes: add cuberounda

include <system.scad>
include <units.scad>
use <misc.scad>
use <transforms.scad>

module cubea(size=[10,10,10], align=[0,0,0], extrasize=[0,0,0], extrasize_align=[0,0,0], orient=[0,0,1], roll=0, extra_roll, extra_roll_orient)
{
    size_align(size=size,extra_size=extrasize, align=align, extra_align=extrasize_align, orient=orient, orient_ref=[0,0,1], roll=roll, , extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    {
        cube(size+extrasize, center=true);
    }
}

module rcubea(size=[10,10,10], rounding_radius=1, align=[0,0,0], extrasize=[0,0,0], extrasize_align=[0,0,0], orient=[0,0,1], roll=0, extra_roll, extra_roll_orient)
{
    size_align(size=size,extra_size=extrasize, align=align, extra_align=extrasize_align, orient=orient, orient_ref=[0,0,1], roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    {
        rcube(size=size+extrasize, rounding_radius=rounding_radius);
    }
}

/*cubea(size=[10,20,30], align=[0,-1,0], orient=[0,0,1], roll=10, extra_roll=-30, extra_roll_orient=[1,0,0]);*/

module rcube(size=[20,20,20], rounding_radius=1)
{
    if($preview_mode || rounding_radius == 0)
    {
        cubea(size);
    }
    else
    {
        hull()
        for(x=[-(size[0]/2-rounding_radius),(size[0]/2-rounding_radius)])
        for(y=[-(size[1]/2-rounding_radius),(size[1]/2-rounding_radius)])
        for(z=[-(size[2]/2-rounding_radius),(size[2]/2-rounding_radius)])
        translate([x,y,z])
        sphere(r=rounding_radius);
    }
}

module cylindera(
        h=10,
        r=undef,
        r1=undef,
        r2=undef,
        d=undef,
        d1=undef,
        d2=undef,
        align=[0,0,0],
        orient=[0,0,1],
        extra_h=0,
        extra_r=undef,
        extra_d=undef,
        extra_align=[0,0,0],
        round_radius=0,
        debug=false
        )
{
    pi=3.1415926536;

    d1_ = v_fallback(d1, [r*2, r1*2]);
    d2_ = v_fallback(d2, [r*2, r2*2]);

    r1_ = v_fallback(r1, [d1_/2, d/2, r]);
    r2_ = v_fallback(r2, [d2_/2, d/2, r]);

    r_max = v_fallback(r, [max(r1_,r2_)]);

    extra_r_ = v_fallback(extra_r, [0]);

    if(debug)
    {
        echo(useDia, h, r_, r1_, r2_, extra_r_, align);
    }

    size_align(size=[r_max*2,r_max*2,h], extra_size=[extra_r_*2, extra_r_*2, extra_h], orient=orient, orient_ref=[0,0,1], align=align, extra_align=extra_align)
    {
        if(round_radius>0)
        {
            rcylinder(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, round_radius=round_radius);
        }
        else
        {
            cylinder(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, center=true);
        }
    }
}

/*translate([10,0,0])*/
/*rotate_extrude()*/
        /*translate([10-2,2,0])*/
/*$fa = 5.6;*/
/*$fs = 0.3;*/
/*circle(r = 100);*/
/*torus(10, 2, align=[0,0,0], orient=[0,0,1]);*/

module torus(radius, radial_width, align=[0,0,0], orient=[0,0,1])
{
    size_align(size=[radius*2+radial_width*2, radius*2+radial_width*2, radial_width*2], align=align, orient=orient)
    rotate_extrude()
    translate([radius, 0, 0])
    circle(radial_width);
}

module rcylinder(d, d1, d2, r, r1, r2, h=10, round_radius=2, align=[0,0,0], orient=[0,0,1])
{
    if($preview_mode || round_radius == 0 || true)
    {
        cylindera(d=d, d1=d1, d2=d2, r=r, r1=r1, r2=r2, h=h, align=align, orient=orient);
    }
    else
    {
        d1_ = v_fallback(d1, [r*2, r1*2]);
        d2_ = v_fallback(d2, [r*2, r2*2]);

        r1_ = v_fallback(r1, [d/2, d1_/2]);
        r2_ = v_fallback(r2, [d/2, d2_/2]);

        r_ = [r1_,r2_];

        size_align(align=align, orient=orient)
            hull()
            {
                for(z=[-1,1])
                    translate([0, 0, z*(-h/2)])
                    {
                        r__=z!=-1?r_[0]:r_[1];
                        torus(radius=r__-round_radius/2, radial_width=round_radius/2, align=[0,0,z]);
                    }
            }
    }
}

// positive angles go from start to end counterclockwise
// negative angles are allowed
module pie_slice_shape(r, start_angle, end_angle) {
    R = r * sqrt(2) + 1;
    a0 = (4 * start_angle + 0 * end_angle) / 4;
    a1 = (3 * start_angle + 1 * end_angle) / 4;
    a2 = (2 * start_angle + 2 * end_angle) / 4;
    a3 = (1 * start_angle + 3 * end_angle) / 4;
    a4 = (0 * start_angle + 4 * end_angle) / 4;
    intersection() {
        circle(r);
        polygon([
                [0,0],
                [R * cos(a0), R * sin(a0)],
                [R * cos(a1), R * sin(a1)],
                [R * cos(a2), R * sin(a2)],
                [R * cos(a3), R * sin(a3)],
                [R * cos(a4), R * sin(a4)],
                [0,0]
        ]);
    }
}

// positive angles go from start to end counterclockwise
// negative angles are allowed
module pie_slice(r, start_angle, end_angle, h, orient=[0,0,1], align=[0,0,0])
{
    size_align(size=[r*2, r*2, h], orient=orient, orient_ref=[0,0,1], align=align)
    linear_extrude(h)
    {
        pie_slice_shape(r, start_angle, end_angle);
    }
}

module hollow_cylinder(d=10, thickness=1, h=10, taper=false, taper_h=undef, orient=[0,0,1], align=[0,0,0])
{
    outer_d = d+thickness/2;
    inner_d = d-thickness/2;
    size_align(size=[outer_d, outer_d, h], orient=orient, align=align)
    difference()
    {
        /*hull()*/
        union()
        {
            cylindera(h=h-taper_h*2, d=outer_d, orient=[0,0,1], align=[0,0,0]);
            taper_h = taper_h == undef ? (outer_d-inner_d)/4 : taper_h;
            /*taper_h_ = pythag_leg(taper_h, outer_d-inner_d);*/
            /*echo(taper_h_);*/
            if(taper)
            {
                for(z=[-1,1])
                translate([0,0,z*(h/2-taper_h)])
                mirror([0,0,z==-1?1:0])
                difference()
                {
                    cylindera(d1=outer_d, d2=outer_d-inner_d/4, h=taper_h*2, align=[0,0,1]);
                    cylindera(d1=inner_d, d2=outer_d, h=taper_h*2+.1, align=[0,0,1]);
                }
            }
        }

        cylindera(h=h+.4, d=inner_d, orient=[0,0,1], align=[0,0,0]);
    }
}

/**
 * Standard right-angled triangle
 *
 * @param number o_len Lenght of the opposite side
 * @param number a_len Lenght of the adjacent side
 * @param number depth How wide/deep the triangle is in the 3rd dimension
 * @todo a better way ?
 */
module triangle(o_len, a_len, depth, align=[0,0,0], orient=[0,0,1])
{
    size_align(size=[a_len, depth, o_len], align=align, orient=orient)
    rotate([90,0,0])
    translate([-a_len/2, -o_len/2, -depth/2])
    linear_extrude(height=depth)
    {
        polygon(points=[[0,0],[a_len,0],[0,o_len]], paths=[[0,1,2]]);
    }
}

/**
 * Rounded Standard right-angled triangle
 *
 * @param number o_len Lenght of the opposite side
 * @param number a_len Lenght of the adjacent side
 * @param number depth How wide/deep the triangle is in the 3rd dimension
 * @todo a better way ?
 */
module rtriangle(o_len, a_len, depth, rounding_radius=2, align=[0,0,0], orient=[0,0,1])
{
    size = [a_len-rounding_radius, depth-rounding_radius, o_len-rounding_radius];
    r_x = rounding_radius;
    r_y = rounding_radius;
    r_z = -rounding_radius;

    hyp = pythag_hyp(o_len,a_len);
    a_z = atan(a_len/o_len);

    translate([0,0,size[2]+2])
    color([1,0,0])
    rotate([90,90,0])
    pie_slice(r=2, start_angle=0, end_angle=a_z, h=depth);

    /*translate([size[0]+4,0,0])*/
    /*color([1,0,0])*/
    /*rotate([90,90,0])*/
    /*pie_slice(r=2, start_angle=90, end_angle=90+a_z, h=depth);*/

    size_align(size=size, align=align, orient=orient)
    /*hull()*/
    for(x=[-(size[0]/2)+rounding_radius,(size[0]/2)-r_x])
    for(y=[-(size[1]/2)+rounding_radius,(size[1]/2)-r_y])
    for(z=[-(size[2]/2)+rounding_radius,(size[2]/2)-r_z])
    translate([x,y,z])
    if(x<=1 || z<=1)
    sphere(r=rounding_radius);
}

module teardrop(r, d, h=10, truncate=1, align=[0,0,0], orient=[0,1,0], roll=0)
{
    r_= v_fallback(r, [d/2]);

    sx1 = r_ * sin(-45);
    sx2 = r_ * -sin(-45);
    sy = r_ * -cos(-45);
    ex = 0;
    ey = (sin(-135) + cos(-135)) * r_;

    dx= ex-sx1;
    dy = ey-sy;

    eys = lerp(-r_,ey,1-truncate);

    dys = eys-sy;
    ex1 = sy+dys*dx/dy;
    ex2 = -ex1;

    size_align(size=[d,d,h], align=align, orient=orient, orient_ref=[0,0,1], roll=roll)
    union()
    {
        linear_extrude(height = h, center = true, convexity = r_, twist = 0)
        circle(r = r_, center = true);

        linear_extrude(height = h, center = true, convexity = r_, twist = 0)
        polygon(points = [
                [sy, sx1],
                [sy, sx2],
                [eys, ex2],
                [eys, ex1]],
                paths = [[0, 1, 2, 3]]);
    }
}

// test teardrop
if(false)
{
    r=5*mm;
    for(axis=concat(AXES,-AXES))
    translate(axis*r*2)
    {
        c= v_abs(axis*.3 + v_clamp(v_sign(axis),0,1)*.7);
        color(c)
        teardrop(r=r, h=20, orient=axis, align=axis);
    }
}

// test cubea
if(false)
{
    w=5*mm;
    h=2*mm;
    for(axis=concat(AXES,-AXES))
    /*translate(axis)*/
    {
        c= v_abs(axis*.3 + v_clamp(v_sign(axis),0,1)*.7);
        color(c)
        cubea([w,w,h], orient=axis, align=axis*3);
    }
}
/**
/*debug();*/

module debug()
{
    $fs = 0.1;
    $fa = 1;

    /*triangle_a(45, 5, 5);*/
    /*triangle(15, 10, 5, align=[1,1,1], orient=[0,0,-1]);*/
    /*triangle(15, 10, 5, align=[1,1,1], orient=[0,0,1]);*/

    triangle(10, 30, 5, align=[1,1,1], orient=[0,0,1]);
    translate([0,-10,0])
    rtriangle(10, 30, 5, align=[1,1,1], orient=[0,0,1]);
}

/**
 * Standard right-angled triangle (tangent version)
 *
 * @param number angle of adjacent to hypotenuse (ie tangent)
 * @param number a_len Lenght of the adjacent side
 * @param number depth How wide/deep the triangle is in the 3rd dimension
 */
module triangle_a(tan_angle, a_len, depth)
{
    linear_extrude(height=depth)
    {
        polygon(points=[[0,0],[a_len,0],[0,tan(tan_angle) * a_len]], paths=[[0,1,2]]);
    }
}

// Tests:
module test_triangles()
{
    // Generate a bunch of triangles by sizes
    for (i = [1:10])
    {
        translate([i*7, -30, i*7])
        {
            triangle(i*5, sqrt(i*5+pow(i,2)), 5);
        }
    }

    // Generate a bunch of triangles by angle
    for (i = [1:85/5])
    {
        translate([i*7, 22, i*7])
        {
            triangle_a(i*5, 10, 5);
        }
    }
}

if(false)
{
    /*hollow_cylinder(thickness=5, h=10, taper=true, orient=[0,0,1], align=[0,0,1]);*/
    /*cylindera(d=10, h=10, orient=[0,0,1], align=[0,0,1]);*/
    /*translate([10,0,0])*/
    /*{*/
    /*hollow_cylinder(d=10, thickness=4, h=10, taper=true, taper_h=.5, orient=[0,0,1], align=[0,0,1]);*/
    /*cylindera(d=8, h=10, orient=[0,0,1], align=[0,0,1]);*/
    /*}*/

    /*%pie_slice(r=10, start_angle=-30, end_angle=270, h=10);*/

    /*cubea([10,10,10],[1,0,0]);*/
    /*%cubea([10,10,10],[1,0,0],[5,5,5],[1,1,1]);*/

    /*%cubea([10,10,10],[-1,0,1]);*/
    /*%cubea([10,10,10],[-1,0,1],[1,1,1],[-1,0,1]);*/
    /*%cylindera(h=10, r=10/2, align=[-1,1,1], extra_r=5/2, extra_h=2, extra_align=[-1,1,1], $fn=100);*/

    /*%cylindera(h=10, d=10, align=[-1,1,1], extra_d=5, extra_h=2, extra_align=[-1,1,1], $fn=100);*/
    /*%cylindera(h=10, d=10, align=[1,1,1], extra_d=15, extra_h=2, extra_align=[1,1,1], $fn=100);*/

    /*%cylindera(h=10, d=10, align=[1,-1,1], extra_d=5, extra_h=2, extra_align=[-1,-1,1], $fn=100);*/
    /*%cylindera(h=10, d=10, align=[1,1,1], extra_d=5, extra_h=2, extra_align=[1,1,1], $fn=100);*/

    /*%cylindera(h=10, d=10, align=[1,0,0], extra_d=10);*/

    /*hull_pairwise()*/
    /*sphere(30/2);*/

    /*translate([30,0,0])*/
    /*rcubea(s=[35,35,35], rounding_radius=10);*/
    /*rcubea(s=[35,35,35], align=[0,-1,0]);*/

    /*translate([60,0,0])*/
    /*sphere(30/2);*/

    cylindera(h=20, r=5*mm, align=[0,1,0], orient=[0,0,1], $fn=100);

/*size_align(size=[5,5,20], orient=[0,1,0], orient_ref=[0,0,1])*/
/*size_align(size=[5,5,20], orient=[0,1,0], orient_ref=[0,1,0], align=[0,1,0])*/
/*size_align(size=[5,20,5], orient=[0,0,1], orient_ref=[0,1,0], align=[0,0,0])*/
/*rotate([90,0,0])*/
/*cylinder(d=5, h=20, center=true);*/

/*cylindera(d=10, h=10, orient=[0,1,0], align=[0,0,1]);*/
}

if(false)
{
    r=5*mm;
    for(axis=concat(AXES,-AXES))
    translate(axis*r*2)
    {
        c= v_abs(axis*.3 + v_clamp(v_sign(axis),0,1)*.7);
        color(c)
        /*cylindera(h=20, r=r, align=axis, orient=axis, extra_h=r, extra_align=-axis, $fn=100);*/
        cylindera(h=20, r1=r, r2=r/2, align=axis, orient=axis, extra_h=r, extra_align=-axis, $fn=16, round_radius=2);
    }
}

if(false)
{
    stack(dist=50, axis=[0,1,0])
    {
        stack(dist=10, axis=[0,0,-1])
        {
            /*cylindera(orient=[0,0,-1], align=[0,0,-1]);*/
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,0,1], align=[0,0,-1]);
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,0,-1], align=[0,0,1]);
            /*pie_slice(r=30, h=5, start_angle=-90, end_angle=180, align=[0,0,-1]);*/
            /*pie_slice(r=30, h=5, start_angle=90, end_angle=270, align=[0,0,-1]);*/
            /*pie_slice(r=30, h=5, start_angle=0, end_angle=270, align=[0,0,-1]);*/
        }

        stack(dist=10, axis=[0,1,0])
        {
            /*cylindera(orient=[0,1,0], align=[0,1,0]);*/
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,-1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=-90, end_angle=180, orient=[0,1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=90, end_angle=270, orient=[0,1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=0, end_angle=270, orient=[0,1,0], align=[0,1,0]);
        }
    }
}

include <system.scad>
include <units.scad>
use <misc.scad>
use <transforms.scad>

module cubea(size=[10,10,10], align=[0,0,0], extrasize=[0,0,0], extrasize_align=[0,0,0], orient=[0,0,1], roll=0, extra_roll, extra_roll_orient)
{
    size_align(size=size,extra_size=extrasize, align=align, extra_align=extrasize_align, orient=orient, orient_ref=[0,0,1], roll=roll, , extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    {
        cube(size+extrasize, center=true);
    }
}

module rcubea(size=[10,10,10], rounding_radius=1, align=[0,0,0], extrasize=[0,0,0], extrasize_align=[0,0,0], orient=[0,0,1], roll=0, extra_roll, extra_roll_orient)
{
    size_align(size=size,extra_size=extrasize, align=align, extra_align=extrasize_align, orient=orient, orient_ref=[0,0,1], roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    {
        rcube(size=size+extrasize, rounding_radius=rounding_radius);
    }
}

/*cubea(size=[10,20,30], align=[0,-1,0], orient=[0,0,1], roll=10, extra_roll=-30, extra_roll_orient=[1,0,0]);*/

module rcube(size=[20,20,20], rounding_radius=1)
{
    if($preview_mode || rounding_radius == 0 || true)
    {
        cubea(size);
    }
    else
    {
        hull()
        for(x=[-(size[0]/2-rounding_radius),(size[0]/2-rounding_radius)])
        for(y=[-(size[1]/2-rounding_radius),(size[1]/2-rounding_radius)])
        for(z=[-(size[2]/2-rounding_radius),(size[2]/2-rounding_radius)])
        translate([x,y,z])
        sphere(r=rounding_radius);
    }
}

module cylindera(
        h=10,
        r=undef,
        r1=undef,
        r2=undef,
        d=undef,
        d1=undef,
        d2=undef,
        align=[0,0,0],
        orient=[0,0,1],
        extra_h=0,
        extra_r=undef,
        extra_d=undef,
        extra_align=[0,0,0],
        round_radius=0,
        debug=false
        )
{
    pi=3.1415926536;

    d1_ = v_fallback(d1, [r*2, r1*2]);
    d2_ = v_fallback(d2, [r*2, r2*2]);

    r1_ = v_fallback(r1, [d1_/2, d/2, r]);
    r2_ = v_fallback(r2, [d2_/2, d/2, r]);

    r_max = v_fallback(r, [max(r1_,r2_)]);

    extra_r_ = v_fallback(extra_r, [0]);

    if(debug)
    {
        echo(useDia, h, r_, r1_, r2_, extra_r_, align);
    }

    size_align(size=[r_max*2,r_max*2,h], extra_size=[extra_r_*2, extra_r_*2, extra_h], orient=orient, orient_ref=[0,0,1], align=align, extra_align=extra_align)
    {
        if(round_radius>0)
        {
            rcylinder(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, round_radius=round_radius);
        }
        else
        {
            cylinder(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, center=true);
        }
    }
}

/*translate([10,0,0])*/
/*rotate_extrude()*/
        /*translate([10-2,2,0])*/
/*$fa = 5.6;*/
/*$fs = 0.3;*/
/*circle(r = 100);*/
/*torus(10, 2, align=[0,0,0], orient=[0,0,1]);*/

module torus(radius, radial_width, align=[0,0,0], orient=[0,0,1])
{
    size_align(size=[radius*2+radial_width*2, radius*2+radial_width*2, radial_width*2], align=align, orient=orient)
    rotate_extrude()
    translate([radius, 0, 0])
    circle(radial_width);
}

module rcylinder(d, d1, d2, r, r1, r2, h=10, round_radius=2, align=[0,0,0], orient=[0,0,1])
{
    if($preview_mode || round_radius == 0 || true)
    {
        cylindera(d=d, d1=d1, d2=d2, r=r, r1=r1, r2=r2, h=h, align=align, orient=orient);
    }
    else
    {
        d1_ = v_fallback(d1, [r*2, r1*2]);
        d2_ = v_fallback(d2, [r*2, r2*2]);

        r1_ = v_fallback(r1, [d/2, d1_/2]);
        r2_ = v_fallback(r2, [d/2, d2_/2]);

        r_ = [r1_,r2_];

        size_align(align=align, orient=orient)
            hull()
            {
                for(z=[-1,1])
                    translate([0, 0, z*(-h/2)])
                    {
                        r__=z!=-1?r_[0]:r_[1];
                        torus(radius=r__-round_radius/2, radial_width=round_radius/2, align=[0,0,z]);
                    }
            }
    }
}

// positive angles go from start to end counterclockwise
// negative angles are allowed
module pie_slice_shape(r, start_angle, end_angle) {
    R = r * sqrt(2) + 1;
    a0 = (4 * start_angle + 0 * end_angle) / 4;
    a1 = (3 * start_angle + 1 * end_angle) / 4;
    a2 = (2 * start_angle + 2 * end_angle) / 4;
    a3 = (1 * start_angle + 3 * end_angle) / 4;
    a4 = (0 * start_angle + 4 * end_angle) / 4;
    intersection() {
        circle(r);
        polygon([
                [0,0],
                [R * cos(a0), R * sin(a0)],
                [R * cos(a1), R * sin(a1)],
                [R * cos(a2), R * sin(a2)],
                [R * cos(a3), R * sin(a3)],
                [R * cos(a4), R * sin(a4)],
                [0,0]
        ]);
    }
}

// positive angles go from start to end counterclockwise
// negative angles are allowed
module pie_slice(r, start_angle, end_angle, h, orient=[0,0,1], align=[0,0,0])
{
    size_align(size=[r*2, r*2, h], orient=orient, orient_ref=[0,0,1], align=align)
    linear_extrude(h)
    {
        pie_slice_shape(r, start_angle, end_angle);
    }
}

module hollow_cylinder(d=10, thickness=1, h=10, taper=false, taper_h=undef, orient=[0,0,1], align=[0,0,0])
{
    outer_d = d+thickness/2;
    inner_d = d-thickness/2;
    size_align(size=[outer_d, outer_d, h], orient=orient, align=align)
    difference()
    {
        /*hull()*/
        union()
        {
            cylindera(h=h-taper_h*2, d=outer_d, orient=[0,0,1], align=[0,0,0]);
            taper_h = taper_h == undef ? (outer_d-inner_d)/4 : taper_h;
            /*taper_h_ = pythag_leg(taper_h, outer_d-inner_d);*/
            /*echo(taper_h_);*/
            if(taper)
            {
                for(z=[-1,1])
                translate([0,0,z*(h/2-taper_h)])
                mirror([0,0,z==-1?1:0])
                difference()
                {
                    cylindera(d1=outer_d, d2=outer_d-inner_d/4, h=taper_h*2, align=[0,0,1]);
                    cylindera(d1=inner_d, d2=outer_d, h=taper_h*2+.1, align=[0,0,1]);
                }
            }
        }

        cylindera(h=h+.4, d=inner_d, orient=[0,0,1], align=[0,0,0]);
    }
}

/**
 * Standard right-angled triangle
 *
 * @param number o_len Lenght of the opposite side
 * @param number a_len Lenght of the adjacent side
 * @param number depth How wide/deep the triangle is in the 3rd dimension
 * @todo a better way ?
 */
module triangle(o_len, a_len, depth, align=[0,0,0], orient=[0,0,1])
{
    size_align(size=[a_len, depth, o_len], align=align, orient=orient)
    rotate([90,0,0])
    translate([-a_len/2, -o_len/2, -depth/2])
    linear_extrude(height=depth)
    {
        polygon(points=[[0,0],[a_len,0],[0,o_len]], paths=[[0,1,2]]);
    }
}

/**
 * Rounded Standard right-angled triangle
 *
 * @param number o_len Lenght of the opposite side
 * @param number a_len Lenght of the adjacent side
 * @param number depth How wide/deep the triangle is in the 3rd dimension
 * @todo a better way ?
 */
module rtriangle(o_len, a_len, depth, rounding_radius=2, align=[0,0,0], orient=[0,0,1])
{
    size = [a_len-rounding_radius, depth-rounding_radius, o_len-rounding_radius];
    r_x = rounding_radius;
    r_y = rounding_radius;
    r_z = -rounding_radius;

    hyp = pythag_hyp(o_len,a_len);
    a_z = atan(a_len/o_len);

    translate([0,0,size[2]+2])
    color([1,0,0])
    rotate([90,90,0])
    pie_slice(r=2, start_angle=0, end_angle=a_z, h=depth);

    /*translate([size[0]+4,0,0])*/
    /*color([1,0,0])*/
    /*rotate([90,90,0])*/
    /*pie_slice(r=2, start_angle=90, end_angle=90+a_z, h=depth);*/

    size_align(size=size, align=align, orient=orient)
    /*hull()*/
    for(x=[-(size[0]/2)+rounding_radius,(size[0]/2)-r_x])
    for(y=[-(size[1]/2)+rounding_radius,(size[1]/2)-r_y])
    for(z=[-(size[2]/2)+rounding_radius,(size[2]/2)-r_z])
    translate([x,y,z])
    if(x<=1 || z<=1)
    sphere(r=rounding_radius);
}

module teardrop(r, d, h=10, truncate=1, align=[0,0,0], orient=[0,1,0], roll=0)
{
    r_= v_fallback(r, [d/2]);

    sx1 = r_ * sin(-45);
    sx2 = r_ * -sin(-45);
    sy = r_ * -cos(-45);
    ex = 0;
    ey = (sin(-135) + cos(-135)) * r_;

    dx= ex-sx1;
    dy = ey-sy;

    eys = lerp(-r_,ey,1-truncate);

    dys = eys-sy;
    ex1 = sy+dys*dx/dy;
    ex2 = -ex1;

    size_align(size=[d,d,h], align=align, orient=orient, orient_ref=[0,0,1], roll=roll)
    union()
    {
        linear_extrude(height = h, center = true, convexity = r_, twist = 0)
        circle(r = r_, center = true);

        linear_extrude(height = h, center = true, convexity = r_, twist = 0)
        polygon(points = [
                [sy, sx1],
                [sy, sx2],
                [eys, ex2],
                [eys, ex1]],
                paths = [[0, 1, 2, 3]]);
    }
}

// test teardrop
if(false)
{
    r=5*mm;
    for(axis=concat(AXES,-AXES))
    translate(axis*r*2)
    {
        c= v_abs(axis*.3 + v_clamp(v_sign(axis),0,1)*.7);
        color(c)
        teardrop(r=r, h=20, orient=axis, align=axis);
    }
}

// test cubea
if(false)
{
    w=5*mm;
    h=2*mm;
    for(axis=concat(AXES,-AXES))
    /*translate(axis)*/
    {
        c= v_abs(axis*.3 + v_clamp(v_sign(axis),0,1)*.7);
        color(c)
        cubea([w,w,h], orient=axis, align=axis*3);
    }
}
/**
/*debug();*/

module debug()
{
    $fs = 0.1;
    $fa = 1;

    /*triangle_a(45, 5, 5);*/
    /*triangle(15, 10, 5, align=[1,1,1], orient=[0,0,-1]);*/
    /*triangle(15, 10, 5, align=[1,1,1], orient=[0,0,1]);*/

    triangle(10, 30, 5, align=[1,1,1], orient=[0,0,1]);
    translate([0,-10,0])
    rtriangle(10, 30, 5, align=[1,1,1], orient=[0,0,1]);
}

/**
 * Standard right-angled triangle (tangent version)
 *
 * @param number angle of adjacent to hypotenuse (ie tangent)
 * @param number a_len Lenght of the adjacent side
 * @param number depth How wide/deep the triangle is in the 3rd dimension
 */
module triangle_a(tan_angle, a_len, depth)
{
    linear_extrude(height=depth)
    {
        polygon(points=[[0,0],[a_len,0],[0,tan(tan_angle) * a_len]], paths=[[0,1,2]]);
    }
}

// Tests:
module test_triangles()
{
    // Generate a bunch of triangles by sizes
    for (i = [1:10])
    {
        translate([i*7, -30, i*7])
        {
            triangle(i*5, sqrt(i*5+pow(i,2)), 5);
        }
    }

    // Generate a bunch of triangles by angle
    for (i = [1:85/5])
    {
        translate([i*7, 22, i*7])
        {
            triangle_a(i*5, 10, 5);
        }
    }
}

if(false)
{
    /*hollow_cylinder(thickness=5, h=10, taper=true, orient=[0,0,1], align=[0,0,1]);*/
    /*cylindera(d=10, h=10, orient=[0,0,1], align=[0,0,1]);*/
    /*translate([10,0,0])*/
    /*{*/
    /*hollow_cylinder(d=10, thickness=4, h=10, taper=true, taper_h=.5, orient=[0,0,1], align=[0,0,1]);*/
    /*cylindera(d=8, h=10, orient=[0,0,1], align=[0,0,1]);*/
    /*}*/

    /*%pie_slice(r=10, start_angle=-30, end_angle=270, h=10);*/

    /*cubea([10,10,10],[1,0,0]);*/
    /*%cubea([10,10,10],[1,0,0],[5,5,5],[1,1,1]);*/

    /*%cubea([10,10,10],[-1,0,1]);*/
    /*%cubea([10,10,10],[-1,0,1],[1,1,1],[-1,0,1]);*/
    /*%cylindera(h=10, r=10/2, align=[-1,1,1], extra_r=5/2, extra_h=2, extra_align=[-1,1,1], $fn=100);*/

    /*%cylindera(h=10, d=10, align=[-1,1,1], extra_d=5, extra_h=2, extra_align=[-1,1,1], $fn=100);*/
    /*%cylindera(h=10, d=10, align=[1,1,1], extra_d=15, extra_h=2, extra_align=[1,1,1], $fn=100);*/

    /*%cylindera(h=10, d=10, align=[1,-1,1], extra_d=5, extra_h=2, extra_align=[-1,-1,1], $fn=100);*/
    /*%cylindera(h=10, d=10, align=[1,1,1], extra_d=5, extra_h=2, extra_align=[1,1,1], $fn=100);*/

    /*%cylindera(h=10, d=10, align=[1,0,0], extra_d=10);*/

    /*hull_pairwise()*/
    /*sphere(30/2);*/

    /*translate([30,0,0])*/
    /*rcubea(s=[35,35,35], rounding_radius=10);*/
    /*rcubea(s=[35,35,35], align=[0,-1,0]);*/

    /*translate([60,0,0])*/
    /*sphere(30/2);*/

    cylindera(h=20, r=5*mm, align=[0,1,0], orient=[0,0,1], $fn=100);

/*size_align(size=[5,5,20], orient=[0,1,0], orient_ref=[0,0,1])*/
/*size_align(size=[5,5,20], orient=[0,1,0], orient_ref=[0,1,0], align=[0,1,0])*/
/*size_align(size=[5,20,5], orient=[0,0,1], orient_ref=[0,1,0], align=[0,0,0])*/
/*rotate([90,0,0])*/
/*cylinder(d=5, h=20, center=true);*/

/*cylindera(d=10, h=10, orient=[0,1,0], align=[0,0,1]);*/
}

if(false)
{
    r=5*mm;
    for(axis=concat(AXES,-AXES))
    translate(axis*r*2)
    {
        c= v_abs(axis*.3 + v_clamp(v_sign(axis),0,1)*.7);
        color(c)
        /*cylindera(h=20, r=r, align=axis, orient=axis, extra_h=r, extra_align=-axis, $fn=100);*/
        cylindera(h=20, r1=r, r2=r/2, align=axis, orient=axis, extra_h=r, extra_align=-axis, $fn=16, round_radius=2);
    }
}

if(false)
{
    stack(dist=50, axis=[0,1,0])
    {
        stack(dist=10, axis=[0,0,-1])
        {
            /*cylindera(orient=[0,0,-1], align=[0,0,-1]);*/
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,0,1], align=[0,0,-1]);
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,0,-1], align=[0,0,1]);
            /*pie_slice(r=30, h=5, start_angle=-90, end_angle=180, align=[0,0,-1]);*/
            /*pie_slice(r=30, h=5, start_angle=90, end_angle=270, align=[0,0,-1]);*/
            /*pie_slice(r=30, h=5, start_angle=0, end_angle=270, align=[0,0,-1]);*/
        }

        stack(dist=10, axis=[0,1,0])
        {
            /*cylindera(orient=[0,1,0], align=[0,1,0]);*/
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,-1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=0, end_angle=120, orient=[0,1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=-90, end_angle=180, orient=[0,1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=90, end_angle=270, orient=[0,1,0], align=[0,1,0]);
            pie_slice(r=30, h=5, start_angle=0, end_angle=270, orient=[0,1,0], align=[0,1,0]);
        }
    }
}


added negative_sq module

added negative_sq module

//Simple Objects
module ring(r=5,d=1) {
	difference() {
		circle(r=r);
		circle(r=r-d);
	}
}
//Translation Modules
module tr_xy(x,y=0) {
	if(y==0) {
		for(i=[-1,1])for(j=[-1,1])translate([x*i,x*j])children();
	} else {
		for(i=[-1,1])for(j=[-1,1])translate([x*i,y*j])children();
	}
}
module rot_x(x=0,rotation=90,off=0) {
	for(i=[0:360/rotation-1])rotate(a=[0,0,rotation*i+off])translate([x,0])children();
}
//Support for Hollowed Models
module support(comb=false) {
    intersection() {
        if(comb==false)support_raw();
        if(comb==true)support_honeycomb();
        children();
    }
}
module support_raw(x=20,y=20,d=1.1,t=0.15) {
    for(i=[-1,1])for(j=[0:d:x/2])translate([i*j,0])square([t,y],center=true);
    for(i=[-1,1])for(j=[0:d:x/2])translate([0,i*j])square([x,t],center=true);
}
module support_honeycomb(x=10,y=10,r=0.7,d=0.1,fn=6) {
    for(k=[-1,1])for(l=[0:2*(r+r*sin(30)-d):x]){
        for(i=[1,-1])for(j=[0:2*r*cos(30)-d:y]) {
            translate([k*l,i*j])comb(r,d,fn);
            translate([k*(l+r+r*sin(30)-d),i*(j+cos(30)*r-0.5*d)])comb(r,d,fn);
        }
    }
}
module honeycomb_drainholes(x=10,y=10,r=0.7,d=0.1,fn=6,o1=1,o2=1,ra=0.25) {
    for(k=[-1,1])for(l=[0:(2*(r+r*sin(30)-d))*o1:x]){
        for(i=[1,-1])for(j=[0:(2*r*cos(30)-d)*o2:y]) {
            translate([k*l,i*j])circle(r=ra);
            translate([k*(l+r+r*sin(30)-d),i*(j+cos(30)*r-0.5*d)])circle(r=ra);
        }
    }
}
module comb(r,d,fn) {
    difference() {
        circle(r=r,$fn=fn);
        circle(r=r-d,$fn=fn);
    }
}
module negativ_sq() {
    difference() {
        hull()children();
        children();
    }
}


Partial implementation of hut housing for "nut drive"

// Housings to hold hexagonal nut for a "nut drive" two nuts with
// reverse threads on a threaded rod.
use <../common/extrusions.scad>
EPSILON = 0.01;
LARGE = 100;

function poly_coords(order, r=1)  =
    let(angles=[ for (i = [0:order-1]) i*(360/order) ])
 	[ for (th=angles) [r*cos(th), r*sin(th)] ];

 module regular_polygon(order, r=1){
 	polygon(poly_coords(order, r));
 }

block_w = 30;
block_d = 30;
block_h = 10;
block_ro = 3;

module center_piece() {

    hex_dia = 20;
    hole_r = 2.05;
    hole_major_dia = block_d  - hole_r;
    hole_centers = poly_coords(4, hole_major_dia/2);
    punch_h = block_h + 2*EPSILON;

    difference() {
        oblong(block_w, block_d, block_ro, block_h);
        translate([block_w/2, block_d/2, -EPSILON]) {
            linear_extrude(height = block_h + 2*EPSILON) regular_polygon(6, hex_dia/2);
            rotate([0, 0, 45]) for (i = [0:3]) {
                translate(hole_centers[i])
                    cylinder(r=hole_r, h = punch_h);
            }
        }
    }
}

$fn = 50;
center_piece();

error: pathspec 'openscad/nut-drive/nut-housings.scad' did not match any file(s) known to git


core pipe (in and out side) is now completed

eps = 0.01;
wall = 1.3;

launch_side = 22;
launch_h = 180;

conn_phi = 25;
conn_h = 20;

module screw_mount()
{
  s = 10;
  h = 5;
  difference()
  {
    translate([-s/2, -s/2, 0])
      cube([s, s, h]);
    translate([0, 0, -eps])
      #cylinder(d=4, h=h+2*eps, $fn=50);
  }
}

module triangle_2d(side)
{
  dx = -side/2;
  dy = 0;
  translate([dx, 0, 0])
  polygon( points=[
    [0, 0],
    [side, 0],
    [cos(60)*side, sin(60)*side]
  ] );
}

module triangle_2d_int(side)
{
  offset(r=-wall)
    triangle_2d(side);
}

module pipe_connector(d, h, $fn=200)
{
  cylinder(d=d, h=h);
}

module outer_pipe()
{
  // rocket side
  translate([0, launch_h+eps, 0])
    rotate([90, 0, 0])
      linear_extrude(height=launch_h)
        triangle_2d(launch_side);
  // rubber pipe side
  dz = launch_side * sin(60);
  translate([0, -conn_phi/2, dz])
    pipe_connector(conn_phi, 20);
  // interconenction of both
  translate([-conn_phi/2, -conn_phi, 0])
    cube([conn_phi, conn_phi, dz]);
}

module inner_pipe()
{
  // rocket side
  translate([0, launch_h+eps, 0])
    rotate([90, 0, 0])
      linear_extrude(height=launch_h+2*eps)
        triangle_2d_int(launch_side);
  // rubber pipe side
  dz = launch_side * sin(60);
  translate([0, -conn_phi/2, dz])
    pipe_connector(conn_phi-2*wall, 20+eps);
  // smooth interconenction of both
  hull()
  {
    // launch side
    #rotate([90, 0, 0])
      linear_extrude(height=eps)
        triangle_2d_int(launch_side);
    // top-level connection facing side
    translate([0, -conn_phi/2, dz])
      pipe_connector(conn_phi-2*wall, 0.1, $fn=50);
    // gradual narrowing of the pipe
    denom = 10;
    translate([0, 0, dz-1.5])
      for(r=[0:5:60])
        rotate([r, 0, 0])
          translate([0, -(conn_phi-r/denom)/2, 0])
            pipe_connector(conn_phi-2*wall-r/denom, 0.1, $fn=50);
  }
}

module launcher()
{
  difference()
  {
    outer_pipe();
    inner_pipe();
  }
  // TODO screw holes :P
}

launcher();

eps = 0.01;
wall = 1.3;

launch_side = 22;
launch_h = 180;

conn_phi = 25;
conn_h = 20;

module screw_mount()
{
  s = 10;
  h = 5;
  difference()
  {
    translate([-s/2, -s/2, 0])
      cube([s, s, h]);
    translate([0, 0, -eps])
      #cylinder(d=4, h=h+2*eps, $fn=50);
  }
}

module triangle_2d(side)
{
  dx = -side/2;
  dy = 0;
  translate([dx, 0, 0])
  polygon( points=[
    [0, 0],
    [side, 0],
    [cos(60)*side, sin(60)*side]
  ] );
}

module triangle_2d_int(side)
{
  offset(r=-wall)
    triangle_2d(side);
}

module pipe_connector(d, h, $fn=200)
{
  cylinder(d=d, h=h);
}

module outer_pipe()
{
  // rocket side
  translate([0, launch_h+eps, 0])
    rotate([90, 0, 0])
      linear_extrude(height=launch_h)
        triangle_2d(launch_side);
  // rubber pipe side
  dz = launch_side * sin(60);
  translate([0, -conn_phi/2, dz])
    pipe_connector(conn_phi, 20);
  // interconenction of both
  translate([-conn_phi/2, -conn_phi, 0])
    cube([conn_phi, conn_phi, dz]);
}

module inner_pipe()
{
  // rocket side
  translate([0, launch_h+eps, 0])
    rotate([90, 0, 0])
      linear_extrude(height=launch_h+2*eps)
        triangle_2d_int(launch_side);
  // rubber pipe side
  dz = launch_side * sin(60);
  translate([0, -conn_phi/2, dz])
    pipe_connector(conn_phi-2*wall, 20+eps);
  // interconenction of both
  /*
  hull()
  {
    rotate([90, 0, 0])
      triangular_prism(launch_side, 0.1);
    translate([0, 0, dz])
      for(r=[0:5:40])
        rotate([r, 0, 0])
          translate([0, -conn_phi/2, 0])
            pipe_connector(conn_phi-r/5, 0.1, $fn=50);
  }
  */
}

module launcher()
{
  difference()
  {
//    outer_pipe();
    inner_pipe();
  }
  // TODO screw holes :P
}

launcher();


fixed dimentioning - some diameters were put in diameter instead of redius

/*
 * configuration
 */
brushes_count = 2;
$fn=200;

/*
 * modules
 */

module lower_tub()
{
  translate([0, (20+4)/2, 0])
    difference()
    {
      cylinder(h=10+3, r=20/2);
      translate([0,0,3])
        cylinder(h=10+3, r2=20/2, r1=16/2);
    }
}

module upper_ring()
{
  translate([0, (24+5)/2, 0])
    difference()
    {
      cylinder(h=16, r=(24+5)/2);
      cylinder(h=16, r=(24+0)/2);
    }
}

module back_wall()
{
  // lower part
  translate([-40/2, 0, 0])
    cube([40, 3, 20]);
  // upper part
  translate([-40/2, 0, 100-16+13])
    cube([40, 3, 20]);
  // connector
  translate([-10/2, 0, 0])
    cube([10, 3, 100]);
}

module brush_holder()
{
  lower_tub();
  translate([0,0,100])
    upper_ring();
  back_wall();
  // lower mounting
  difference()
  {
    translate([-15/2, 3, 0])
      cube([15, 4, 10+3]);
    translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
      hull()
        lower_tub();
  }
  // upper mounting
  translate([0, 0, 100])
  {
    difference()
    {
      translate([-25/2, 3, 0])
        cube([25, 10, 16]);
      translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
        hull()
          upper_ring();
    }
  }
}

/*
 * main program
 */

rotate([90, 0, 0])
{
  for(offset = [0:brushes_count-1])
  {
    translate(offset * [30, 0, 0])
      brush_holder();
  }
}

/*
 * configuration
 */
brushes_count = 2;
$fn=200;

/*
 * modules
 */

module lower_tub()
{
  translate([0, 20+4, 0])
    difference()
    {
      cylinder(h=10+3, r=20);
      translate([0,0,3])
        cylinder(h=10+3, r2=20, r1=16);
    }
}

module upper_ring()
{
  translate([0, 24+5, 0])
    difference()
    {
      cylinder(h=16, r=24+5);
      cylinder(h=16, r=24+0);
    }
}

module back_wall()
{
  translate([0,0,0])
  {
    // lower part
    translate([-70/2, 0, 0])
      cube([70, 3, 20]);
    // upper part
    translate([-70/2, 0, 100-16+13])
      cube([70, 3, 20]);
    // connector
    translate([-20/2, 0, 0])
      cube([20, 3, 100]);
  }
}

module brush_holder()
{
  lower_tub();
  translate([0,0,100])
    upper_ring();
  back_wall();
  // lower mounting
  difference()
  {
    translate([-30/2, 3, 0])
      cube([30, 10, 10+3]);
    translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
      hull()
        lower_tub();
  }
  // upper mounting
  translate([0, 0, 100])
  {
    difference()
    {
      translate([-40/2, 3, 0])
        cube([40, 10, 16]);
      translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
        hull()
          upper_ring();
    }
  }
}

/*
 * main program
 */

rotate([90, 0, 0])
{
  for(offset = [0:brushes_count-1])
  {
    translate(offset * [60, 0, 0])
      brush_holder();
  }
}


number of brushes is now computed from array names length, that is a part of configuration

include <Write.scad>

/*
 * configuration
 */
names = ["JOHN", "JANE", "BILLY"];
brushes_count = len(names);
//$fn=200;

/*
 * modules
 */

module lower_tub()
{
  translate([0, (20+4)/2, 0])
    difference()
    {
      cylinder(h=10+3, r=(20+3)/2);
      translate([0,0,3])
        cylinder(h=10+3, r2=20/2, r1=16/2);
    }
}

module upper_ring()
{
  translate([0, (24+5)/2, 0])
    difference()
    {
      cylinder(h=10, r=(24+5)/2);
      cylinder(h=10, r=(24+0)/2);
    }
}

module back_wall()
{
  // lower part
  translate([-40/2, 0, 0])
    cube([40, 3, 20]);
  // upper part
  translate([-40/2, 0, 100-10])
    cube([40, 3, 20]);
  // connector
  translate([-10/2, 0, 0])
    cube([10, 3, 100]);
}

module brush_holder()
{
  lower_tub();
  translate([0,0,100-5])
    upper_ring();
  back_wall();
  // lower mounting
  difference()
  {
    translate([-15/2, 3, 0])
      cube([15, 4, 10+3]);
    translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
      hull()
        lower_tub();
  }
  // upper mounting
  translate([0, 0, 100-5])
  {
    difference()
    {
      translate([-25/2, 3, 0])
        cube([25, 10, 10]);
      translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
        hull()
          upper_ring();
    }
  }
}

/*
 * main program
 */

rotate([90, 0, 0])
{
  for(i = [0:brushes_count-1])
  {
    translate([i*30,4,55])
      rotate([-90, 90, 0])
        scale([2,1,1])
          write(names[i], t=2, h=8, center=true);
    translate(i * [30, 0, 0])
      brush_holder();
  }
}

include <Write.scad>

/*
 * configuration
 */
brushes_count = 2;
//$fn=200;

/*
 * modules
 */

module lower_tub()
{
  translate([0, (20+4)/2, 0])
    difference()
    {
      cylinder(h=10+3, r=(20+3)/2);
      translate([0,0,3])
        cylinder(h=10+3, r2=20/2, r1=16/2);
    }
}

module upper_ring()
{
  translate([0, (24+5)/2, 0])
    difference()
    {
      cylinder(h=10, r=(24+5)/2);
      cylinder(h=10, r=(24+0)/2);
    }
}

module back_wall()
{
  // lower part
  translate([-40/2, 0, 0])
    cube([40, 3, 20]);
  // upper part
  translate([-40/2, 0, 100-10])
    cube([40, 3, 20]);
  // connector
  translate([-10/2, 0, 0])
    cube([10, 3, 100]);
}

module brush_holder()
{
  lower_tub();
  translate([0,0,100-5])
    upper_ring();
  back_wall();
  // lower mounting
  difference()
  {
    translate([-15/2, 3, 0])
      cube([15, 4, 10+3]);
    translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
      hull()
        lower_tub();
  }
  // upper mounting
  translate([0, 0, 100-5])
  {
    difference()
    {
      translate([-25/2, 3, 0])
        cube([25, 10, 10]);
      translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
        hull()
          upper_ring();
    }
  }
}

/*
 * main program
 */
name = ["name1", "name2"];

rotate([90, 0, 0])
{
  for(i = [0:brushes_count-1])
  {
    translate([i*30,4,55])
      rotate([-90, 90, 0])
        scale([2,1,1])
          write(name[i], t=2, h=8, center=true);
    translate(i * [30, 0, 0])
      brush_holder();
  }
}


increased hole diameter for a roller

increased hole diameter for a roller

eps=0.01;
r_stick=3.5/2; // d=2.5mm + 1mm for imprecissions, etc.

module roller()
{
  difference()
  {
    cylinder(r=20/2, h=5, $fn=200);
    translate([0, 0, -eps])
      cylinder(r=r_stick, h=5+2*eps, $fn=200);
  }
  translate([20/2-5/2, 0, 5])
    cylinder(r=5/2, h=20, $fn=100);
}

module spool_body_()
{
  r_ext=(5+2*3)/2;
  r_int=r_stick+1;

  difference()
  {
    union()
    {
      cylinder(r1=r_ext, r2=r_int, h=5.5, $fn=150);
      translate([0, 0, 5.5+3])
        cylinder(r1=r_int, r2=r_ext, h=5.5, $fn=150);
      translate([0, 0, 5.5])
        cylinder(r=r_int, h=3, $fn=100);
    }
    translate([0, 0, -eps])
      cylinder(r=r_stick, h=2*5.5+3+2*eps, $fn=200);
  }
}

module hook_()
{
  r_int=1/2;
  r_ext=r_int+1;

  difference()
  {
    translate([-2, -1.5/2, 0])
      cube([2, 1.5, 3+2*3]);

    translate([-1, 0, (3+2*3)/2])
      translate([0, 1, 0])
        rotate([90, 0, 0])
          translate([0, 0, -eps])
            cylinder(r=1/2, h=2+2*eps, $fn=200);
  }
}

module spool()
{
  spool_body_();
  translate([-1.8, 0, 2.5])
    hook_();
}

roller();
translate([20, 0, 0])
  spool();

eps=0.01;
r_stick=2.8/2; // extr_width=0.4, d=2.5mm, thus 7*0.4=2.8 as a next best thing

module roller()
{
  difference()
  {
    cylinder(r=20/2, h=5, $fn=200);
    translate([0, 0, -eps])
      cylinder(r=r_stick, h=5+2*eps, $fn=200);
  }
  translate([20/2-5/2, 0, 5])
    cylinder(r=5/2, h=20, $fn=100);
}

module spool_body_()
{
  r_ext=(5+2*3)/2;
  r_int=r_stick+1;

  difference()
  {
    union()
    {
      cylinder(r1=r_ext, r2=r_int, h=5.5, $fn=150);
      translate([0, 0, 5.5+3])
        cylinder(r1=r_int, r2=r_ext, h=5.5, $fn=150);
      translate([0, 0, 5.5])
        cylinder(r=r_int, h=3, $fn=100);
    }
    translate([0, 0, -eps])
      cylinder(r=r_stick, h=2*5.5+3+2*eps, $fn=200);
  }
}

module hook_()
{
  r_int=1/2;
  r_ext=r_int+1;

  difference()
  {
    translate([-2, -1.5/2, 0])
      cube([2, 1.5, 3+2*3]);

    translate([-1, 0, (3+2*3)/2])
      translate([0, 1, 0])
        rotate([90, 0, 0])
          translate([0, 0, -eps])
            cylinder(r=1/2, h=2+2*eps, $fn=200);
  }
}

module spool()
{
  spool_body_();
  translate([-1.8, 0, 2.5])
    hook_();
}

roller();
translate([20, 0, 0])
  spool();


Added dumplings cutter

Added dumplings cutter

$fs=0.2; // default 2
$fa=3; // default 12

form_r = 25;
flange_width = 5;
flange_height = 3;
flange_notches_count = 28;
flange_notch_r = 1.5;
inner_r = form_r - flange_width;

module flange() {
    module flange_shell() {
        rotate_extrude() {
            polygon([
                [inner_r, 0],
                [inner_r, 1],
                [form_r + 1, 1],
                [form_r + 1, -flange_height/2],
                [form_r + 0.5, -flange_height],
                [form_r, -flange_height],
                [form_r, 0]
            ]);
        }
    }
    module flange_notches() {
        intersection() {
            for(i=[0:360/flange_notches_count:359]) {
                rotate([0,0,i])
                    translate([inner_r,0,0]) {
                        intersection() {
                            union() {
                                translate([flange_notch_r,0,0])
                                    sphere(r=flange_notch_r);
                                translate([flange_width/2+flange_notch_r,0,0])
                                    rotate([0,90,0])
                                        cylinder(r=flange_notch_r,h=flange_width,center=true);
                            }
                        }
                    }
            }
            translate([0,0,-4.5]) cylinder(r=form_r+0.1,h=10,center=true);
        }
    }
    flange_shell();
    flange_notches();
}
handle_inner_x0 = inner_r+0.1;
handle_outer_x0 = form_r + 1.52;
handle_inner_x1 = handle_inner_x0;
handle_outer_x1 = handle_inner_x1 + 3;
handle_y1 = 10;
handle_inner_x2 = 2;
handle_outer_x2 = handle_inner_x2 + 3;
handle_y2 = 28;
handle_inner_x3 = 3;
handle_outer_x3 = 0;
handle_inner_y3 = 30;
handle_outer_y3 = 32;
handle_outer_x4 = 300;
handle_inner_x4 = 100;
handle_y4 = 150;
handle_top = 40;
handle_bottom = 0.99;

//#translate([0,0,31]) rotate([0,-45,0])cube([1000,1,1],true);
module handle_outer_edge() {
    polygon([
        [handle_outer_x0, 2],
        [handle_outer_x1, handle_y1],
        [handle_outer_x2, handle_y2],
        [handle_outer_x3, handle_outer_y3],
        [handle_outer_x4, handle_y4],
        [-100, handle_y4],
        [-100, -100],
        [handle_outer_x0, -100]
    ]);
}
module handle_inner_edge() {
    polygon([
        [handle_inner_x0, 2],
        [handle_inner_x1, handle_y1],
        [handle_inner_x2, handle_y2],
        [handle_inner_x3, handle_inner_y3],
        [handle_inner_x4, handle_y4],
        [-100, handle_y4],
        [-100, -100],
        [handle_inner_x0, -100]
    ]);
}
//handle_inner_edge();
module handle_both_edges() {
    difference() {
        offset(r=-10) offset(delta=10)
            offset(r=25) offset(delta=-25)
                handle_outer_edge();
        offset(r=-10) offset(delta=10)
            offset(r=25) offset(delta=-25)
                handle_inner_edge();
    };
}

module handle_silhouette() {
    intersection() {
        offset(r=0.9) offset(delta=-0.9)
            intersection() {
                handle_both_edges();
                translate([0,handle_top-500]) square(1000, true);
            }
        translate([0,handle_bottom+500]) square(1000, true);
    }
}
//handle_silhouette();
module handle() {
    rotate_extrude() {
        handle_silhouette();
    }
}

module cutter() {
    handle();
    flange();
}

intersection() {
    cutter();
//    cube([1000,1,1000],true);
}

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