Updated motor ribbon cable positions

Updated motor ribbon cable positions

/*
	Assembly: WheelAssembly
	Combined motor and wheel assembly

	Local Frame:
		Places the motor default connector at the origin - i.e. use the motor default connector for attaching into a parent assembly

*/

// Connectors
Wheel_Con_Default				= [ [0, 0 ,0], [0, 0, 1], 90, 0, 0 ];

module WheelAssembly( ) {

    assembly("assemblies/Wheel.scad", "Drive Wheel", str("WheelAssembly()")) {

        if (DebugCoordinateFrames) frame();

        // debug connectors?
        if (DebugConnectors) {

        }

        // Vitamins
        logo_motor();

        // ribbon Cable :)
        if ($rightSide) {
            ribbonCable(
                cables=5,
                cableRadius = 0.6,
                points= [
                    [2.5, -23, 7],
                    [10, -60, 10],
                    [70, -15, 20],
                    [26, -8, 25]
                ],
                vectors = [
                    [-1, 0 ,0],
                    [-0.5, 0.5, 1],
                    [-0.5, 0, 1],
                    [0, 0, 1]
                ],
                colors = [
                    "blue",
                    "orange",
                    "red",
                    "pink",
                    "yellow"
                ],
                debug=false
            );

        } else {
            ribbonCable(
                cables=5,
                cableRadius = 0.6,
                points= [
                    [-2.5, -23, 7],
                    [10, -60, 10],
                    [70, -20, 40],
                    [26, -8, 18]
                ],
                vectors = [
                    [1, 0 ,0],
                    [0.5, 0.5, -1],
                    [-0.5, 0.5, -1],
                    [0, 0,-1]
                ],
                colors = [
                    "blue",
                    "orange",
                    "red",
                    "pink",
                    "yellow"
                ],
                debug=false
            );
        }

        // STL
        step(1,  "Push the wheel onto the motor shaft \n**Optional:** add a rubber band to the wheel for extra grip.") {
            view(t=[0, -3, 5], r=[349,102,178], d=500);

            attach(Wheel_Con_Default, [[0,0,2],[0,0,-1],0,0,0], ExplodeSpacing=20)
                Wheel_STL();
        }
	}
}

/*
	Assembly: WheelAssembly
	Combined motor and wheel assembly

	Local Frame:
		Places the motor default connector at the origin - i.e. use the motor default connector for attaching into a parent assembly

*/

// Connectors
Wheel_Con_Default				= [ [0, 0 ,0], [0, 0, 1], 90, 0, 0 ];

module WheelAssembly( ) {

    assembly("assemblies/Wheel.scad", "Drive Wheel", str("WheelAssembly()")) {

        if (DebugCoordinateFrames) frame();

        // debug connectors?
        if (DebugConnectors) {

        }

        // Vitamins
        logo_motor();

        // ribbon Cable :)
        if ($rightSide) {
            ribbonCable(
                cables=5,
                cableRadius = 0.6,
                points= [
                    [2.5, -23, 7],
                    [10, -60, 10],
                    [20, -30,15],
                    [26,0, 13]
                ],
                vectors = [
                    [-1, 0 ,0],
                    [-0.5, 0.5, 1],
                    [-0.5, 0.5, 0],
                    [0, 0,1]
                ],
                colors = [
                    "blue",
                    "orange",
                    "red",
                    "pink",
                    "yellow"
                ],
                debug=false
            );

        } else {
            ribbonCable(
                cables=5,
                cableRadius = 0.6,
                points= [
                    [-2.5, -23, 7],
                    [10, -60, 10],
                    [20, -30,25],
                    [26,0, 30]
                ],
                vectors = [
                    [1, 0 ,0],
                    [0.5, 0.5, -1],
                    [-0.5, 0.5, -1],
                    [0, 0,-1]
                ],
                colors = [
                    "blue",
                    "orange",
                    "red",
                    "pink",
                    "yellow"
                ],
                debug=false
            );
        }

        // STL
        step(1,  "Push the wheel onto the motor shaft \n**Optional:** add a rubber band to wheel for extra grip.") {
            view(t=[0, -3, 5], r=[349,102,178], d=500);

            attach(Wheel_Con_Default, [[0,0,2],[0,0,-1],0,0,0], ExplodeSpacing=20)
                Wheel_STL();
        }
	}
}


added gear.scad as a seperate file from gear library

include <Parametric_Involute__Bevel_and_Spur_Gears/parametric_involute_gear_v5.0.scad>

$fn=100;
mm = 1;
inch = 25.4 * mm;

ACRYLIC_THICKNESS = 6 * mm;
ACRYLIC_TOL = .5 * mm;
PITCH = 360 * mm;
N_TEETH = 6;

PITCH_R = N_TEETH * PITCH / 360.;
PITCH_D = 2 * PITCH_R;
PRESSURE_ANGLE = 28;
PITCH_DIAMETRIAL = N_TEETH / PITCH_D;

// Addendum: Radial distance from pitch circle to outside circle.
ADDENDUM = 1/PITCH_DIAMETRIAL;

//Outer Circle
OUTER_RADIUS = PITCH_R+ADDENDUM;

RIM_THICKNESS = 1 * mm;
EXTRA_RIM_R = 3 * mm;

difference(){
  union(){
    gear (number_of_teeth=6,
	  circular_pitch = PITCH,
	  gear_thickness = ACRYLIC_THICKNESS + ACRYLIC_TOL,
	  rim_thickness = ACRYLIC_THICKNESS + ACRYLIC_TOL,
	  hub_thickness = ACRYLIC_THICKNESS + ACRYLIC_TOL,
	  bore_diameter=0. * mm,
	  circles=4,
	  pressure_angle=28
	  );
    translate([0, 0, -1*mm])cylinder(r=OUTER_RADIUS + EXTRA_RIM_R, h=RIM_THICKNESS);
    translate([0, 0, 6.5*mm])cylinder(r=OUTER_RADIUS, h=RIM_THICKNESS);
  }
  translate([0, 0, -1])cylinder(r=3mm, h=ACRYLIC_THICKNESS + ACRYLIC_TOL + 2);
}

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


Sandbox for MicroSwitch

Sandbox for MicroSwitch

/*

	Playground for vitamins/MicroSwitch.scad

*/

include <../config/config.scad>

include <../vitamins/MicroSwitch.scad>

microswitch();

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


whole element is now completed

whole element is now completed

use<m3d/fn.scad>
use<m3d/rounded_cube.scad>

eps = 0.01;
d_stick = 6; // flag's stick diameter
stick_hole_len = 40; // defines how deep stick goes into the form
d_a = 7; // bottom hole (aka: hole A)
d_b = 8; // top hole (aka: hole B)
a_b_distance = 10.5; // distance between A and B holes (edges! not the centers!)
b_top_distance = 18; // distance from top of B hole to end of bike's frame (edge! not the center!)

mount_len = d_a + a_b_distance + d_b + b_top_distance + 5;
flag_stick_len = 150;
box_size = [ d_b*2.5, mount_len + flag_stick_len, d_b*2.5 ];

module screw_holes(h, extra_d)
{
  translate([0, d_a/2+a_b_distance+d_b/2, 0])
    cylinder(d=d_b+extra_d, h=h, $fn=fn(60));
  cylinder(d=d_a+extra_d, h=h, $fn=fn(60));
}

module bike_flag_mount()
{
  difference()
  {
    rounded_cube(box_size, 3, $fn=fn(20));
    // screw mount cut
    {
      cut_size = [ box_size[0], mount_len + box_size[0]/2, box_size[2]/2];
      translate([-eps, -eps, box_size[2]/2])
        cube(cut_size + eps*[2,1,1]);
    }
    // screw holes
    translate([box_size[0]/2, box_size[0]/2, -eps])
      screw_holes(h=box_size[2]+2*eps, extra_d=0.5);
    // main flag stick hole
    translate([box_size[0]/2, box_size[1]-stick_hole_len, box_size[2]/2])
      rotate([-90, 0, 0])
        cylinder(d=d_stick+0.5, h=stick_hole_len+eps, $fn=fn(70));
    // flag screw-in hole
    translate([box_size[0]/2, box_size[1]-stick_hole_len+5, -eps])
      cylinder(d=3+0.5, h=box_size[2]+2*eps, $fn=fn(50));
  }
}

bike_flag_mount();

use<m3d/fn.scad>

d_stick = 6;
d_a = 7; // bottom hole (aka: hole A)
d_b = 8; // top hole (aka: hole B)
a_b_distance = 10.5; // distance between A and B holes (edges! not the centers!)
b_top_distance = 18; // distance from top of B hole to end of bike's frame (edge! not the center!)

module screw_holes(h, extra_d=0.5)
{
  translate([0, d_a/2+a_b_distance+d_b/2, 0])
    cylinder(d=d_b+extra_d, h=h, $fn=fn(60));
  cylinder(d=d_a+extra_d, h=h, $fn=fn(60));
}

translate([-6/2, 0, 0])
  cube([6, a_b_distance+d_a/2+d_b+b_top_distance, 0.4]);
screw_holes(0.8, extra_d=0);


lcd2004: fix compile errors/warnings

lcd2004: fix compile errors/warnings


models of 2 buttons

models of 2 buttons

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


Damn, finally working

Damn, finally working

/**
Program by Ankur Gupta
www.github.com/agupta231
Jan 2017

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.
**/

init_square_size = 40;
connector_length = 20;
connector_thickness = 0.1;
iteration_multiplier = 0.5;
iterations = 8;

echo(version());

translation_vectors = [[1, 1], [-1, -1], [-1, 1], [1, -1]];
rotation_vectors = [[1, 1, -1], [1, 1, -1], [1, 1, 1], [1, 1, 1]];

function ev_mult(vec1, vec2) =
    len(vec1) != len(vec2) ? false : [for(i = [0:len(vec1) - 1]) vec1[i] * vec2[i]];

module translated_cube(translation_vector, starting_pos, size) {
    translate(translation_vector + starting_pos) {
      square(size, center = true);
    }
}

module connector(vector_keys, connector_size, square_size, starting_pos, current_iteration) {
    if(current_iteration < iterations) {
        for(i = vector_keys) {
            corner = ev_mult(translation_vectors[i], [square_size / 2, square_size / 2]) + starting_pos;
            connector_correction = ev_mult(translation_vectors[i], [connector_size / (2 * sqrt(2)), connector_size / (2 * sqrt(2))]);
            next_square_center = corner + ev_mult(translation_vectors[i], [connector_size / sqrt(2), connector_size / sqrt(2)]);

            translate(corner + connector_correction) {
                rotate(ev_mult(rotation_vectors[i], [0, 45, 45])) {
                    #square([connector_thickness, connector_size], center = true);
                }
            }

            if(i == 0) {
                pattern(next_square_center, "bottom_left", current_iteration + 1);
            }
            else if(i == 1) {
                pattern(next_square_center, "top_right", current_iteration + 1);
            }
            else if(i == 2) {
                pattern(next_square_center, "bottom_right", current_iteration + 1);
            }
            else if(i == 3){
                pattern(next_square_center, "top_left", current_iteration + 1);
            }
        }
    }
}

module pattern(starting_pos, seed_corner, current_iteration) {
    translation = false;
    translations = false;
    rotations = false;
    current_size = init_square_size * pow(iteration_multiplier, current_iteration - 1);
    current_connector_size = connector_length * pow(iteration_multiplier, current_iteration - 1);

    if(seed_corner == "center") {
        center_pos = [0,0] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "bottom_left") {
        center_pos = [current_size / 2, current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "bottom_right") {
        center_pos = [-current_size / 2, current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 2], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "top_left") {
        center_pos = [current_size / 2, -current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "top_right") {
        center_pos = [-current_size / 2, -current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([1, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
}

pattern([0, 0], "center", 1);

/**
Program by Ankur Gupta
www.github.com/agupta231
Jan 2017

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.
**/

init_square_size = 40;
connector_length = 30;
connector_thickness = 0.1;
iteration_multiplier = 0.5;
iterations = 3;

echo(version());

translation_vectors = [[1, 1], [-1, -1], [-1, 1], [1, -1]];
rotation_vectors = [[1, 1, -1], [1, 1, -1], [1, 1, 1], [1, 1, 1]];

function ev_mult(vec1, vec2) =
    len(vec1) != len(vec2) ? false : [for(i = [0:len(vec1) - 1]) vec1[i] * vec2[i]];

module translated_cube(translation_vector, starting_pos, size) {
    translate(translation_vector + starting_pos) {
      square(size, center = true);
    }
}

module connector(vector_keys, connector_size, square_size, starting_pos, current_iteration) {
    if(current_iteration < iterations) {
        for(i = vector_keys) {
            corner = ev_mult(translation_vectors[i], [square_size / 2, square_size / 2]) + starting_pos;
            connector_correction = ev_mult(translation_vectors[i], [connector_size / (2 * sqrt(2)), connector_size / (2 * sqrt(2))]);
            next_square_center = corner + ev_mult(translation_vectors[i], [connector_size / sqrt(2), connector_size / sqrt(2)]);

            translate(corner + connector_correction) {
                rotate(ev_mult(rotation_vectors[i], [0, 45, 45])) {
                    #square([connector_thickness, connector_size], center = true);
                }
            }

            if(i == 0) {
                pattern(next_square_center, "bottom_left", current_iteration + 1);
            }
            else if(i == 1) {
                pattern(next_square_center, "top_right", current_iteration + 1);
            }
            else if(i == 2) {
                pattern(next_square_center, "bottom_right", current_iteration + 1);
            }
            else if(i == 3){
                pattern(next_square_center, "top_left", current_iteration + 1);
            }
        }
    }
}

module pattern(starting_pos, seed_corner, current_iteration) {
    translation = false;
    translations = false;
    rotations = false;
    current_size = init_square_size * pow(iteration_multiplier, current_iteration - 1);
    current_connector_size = connector_length * pow(iteration_multiplier, current_iteration - 1);

    if(seed_corner == "center") {
        translated_cube([0,0], starting_pos, current_size);
        connector([0, 1, 2, 3], current_connector_size, current_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "bottom_left") {
        translated_cube([current_size / 2, current_size / 2], starting_pos, current_size);
        connector([0, 2, 3], current_connector_size, current_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "bottom_right") {
        translated_cube([-current_size / 2, current_size / 2], starting_pos, current_size);
        connector([0, 1, 2], current_connector_size, current_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "top_left") {
        translated_cube([current_size / 2, -current_size / 2], starting_pos, current_size);
        connector([0, 1, 3], current_connector_size, current_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "top_right") {
        translated_cube([-current_size / 2, -current_size / 2], starting_pos, current_size);
        connector([1, 2, 3], current_connector_size, current_size, starting_pos, current_iteration);
    }
}

pattern([0, 0], "center", 1);


moineau: increase rotor clearence to reduce tension


initial commit

initial commit

module LetterBlock(letter, size=30) {
    difference() {
        translate([0,0,size/4])
          cube([size,size,size/2], center=true);
        translate([0,0,size/6])
            linear_extrude(height=size, convexity=3)
                text(letter,
                     size=size*22/30,
                     font="Tahoma",
                     halign="center",
                     valign="center");
    }
}

size=20;
for (i=[0:25])
  translate([(i%5)*(size+3),floor(i/5)*-(size+1),0])
    LetterBlock(chr(i+65), size);

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


add a protection to avoid to break potentiometer connection

width = 15;
beam_size = 10;
beam_thickness = 5;
thickness = 3;
bridge_length = 32;
potentiometer_width = 16.1;
total_length = bridge_length+2*width+2*thickness;

module bottom_beam(){
difference(){
union(){
		difference(){
		union(){
			cube(size = [total_length, beam_size, beam_thickness], center=true);

			translate([total_length/2 - thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);

			translate([total_length/2 - thickness/2-thickness-width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2+thickness+width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
		}

	//Remove space to save weight?
		translate([bridge_length/2+width/2,0,0]) cube(size = [width, beam_size-2*thickness, 2*beam_thickness], center=true);
		translate([-bridge_length/2-width/2,0,0]) cube(size = [width, beam_size-2*thickness, 2*beam_thickness], center=true);
	}
		minkowski(){
			cube(size = [potentiometer_width, potentiometer_width, beam_thickness/2], center=true);
			cylinder(r= thickness, h=beam_thickness/2,center=true);
}
	}
	cube(size = [potentiometer_width, potentiometer_width, beam_thickness*2], center=true);
}

}
module top_beam(){
difference(){
	union(){
			difference(){
		union(){
			cube(size = [total_length, beam_size, beam_thickness], center=true);

			translate([total_length/2 - thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);

			translate([total_length/2 - thickness/2-thickness-width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2+thickness+width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
		}
	}
		cylinder(r= 5+thickness, h= beam_thickness, center=true);
	}
	cylinder(r= 5, h= 2* beam_thickness, center=true);
}
}

projection() difference(){
	union()
	{
		bottom_beam();
			translate([0,15,0]) minkowski(){
			cube(size = [potentiometer_width, potentiometer_width, beam_thickness/2], center=true);
			cylinder(r= thickness, h=beam_thickness/2,center=true);
}
	}
	translate([0 , potentiometer_width/2+4],0) cube(size = [potentiometer_width, potentiometer_width, beam_thickness*2], center=true);
}
projection() translate([0, 38, beam_thickness+1]) bottom_beam();

projection()  translate([0, 59, 2*beam_thickness+2])top_beam();

translate([0, 0, 2* beam_thickness+2]) difference(){
 	cylinder(r=3+thickness,h=beam_thickness, center=true);
	cylinder(r=3, h=2*beam_thickness, center=true);
}

width = 15;
beam_size = 10;
beam_thickness = 5;
thickness = 3;
bridge_length = 32;
potentiometer_width = 16.1;
total_length = bridge_length+2*width+2*thickness;

module bottom_beam(){
difference(){
union(){
		difference(){
		union(){
			cube(size = [total_length, beam_size, beam_thickness], center=true);

			translate([total_length/2 - thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);

			translate([total_length/2 - thickness/2-thickness-width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2+thickness+width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
		}

	//Remove space to save weight?
		translate([bridge_length/2+width/2,0,0]) cube(size = [width, beam_size-2*thickness, 2*beam_thickness], center=true);
		translate([-bridge_length/2-width/2,0,0]) cube(size = [width, beam_size-2*thickness, 2*beam_thickness], center=true);
	}
		minkowski(){
			cube(size = [potentiometer_width, potentiometer_width, beam_thickness/2], center=true);
			cylinder(r= thickness, h=beam_thickness/2,center=true);
}
	}
	cube(size = [potentiometer_width, potentiometer_width, beam_thickness*2], center=true);
}

}
module top_beam(){
difference(){
	union(){
			difference(){
		union(){
			cube(size = [total_length, beam_size, beam_thickness], center=true);

			translate([total_length/2 - thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);

			translate([total_length/2 - thickness/2-thickness-width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
			translate([-total_length/2 + thickness/2+thickness+width,0,0]) cube(size = [thickness, beam_size+2*thickness, beam_thickness], center=true);
		}
	}
		cylinder(r= 5+thickness, h= beam_thickness, center=true);
	}
	cylinder(r= 5, h= 2* beam_thickness, center=true);
}
}

projection() difference(){
	bottom_beam();
	translate([0 , potentiometer_width/2],0) cube(size = [potentiometer_width, potentiometer_width, beam_thickness*2], center=true);
}
projection() translate([0, 23, beam_thickness+1]) bottom_beam();

projection()  translate([0, 44, 2*beam_thickness+2])top_beam();

translate([0, 0, 2* beam_thickness+2]) difference(){
 	cylinder(r=3+thickness,h=beam_thickness, center=true);
	cylinder(r=3, h=2*beam_thickness, center=true);
}


Moving the test file to a separate directory to make it clear it's not part of the final project.

nbr_lasers = 36;
laser_spacing = 5;

for(x = [0 : sqrt(nbr_lasers)]) {
    for(y = [0 : sqrt(nbr_lasers)]) {
        translate([x * laser_spacing, y * laser_spacing])
            circle(2);
    }
}

error: pathspec 'lasercut_parts/test/laserbox_test_plate.scad' did not match any file(s) known to git


Enlarge belt clamp holes slightly, narrow ribs so overall width remains the same.

// This printed part is one possible way to anchor Shapeoko belts.
// See http://www.shapeoko.com/wiki/index.php/Belt_Anchors for other ideas.
// Motivated by http://www.thingiverse.com/thing:33881 not being strong enough
// when I initially printed it.

// Upright: printed, but not stress tested, looks sturdy
// Top: completely untested

gThick = 8;
gRibHeight = 6;
gTopRoundoverRadius = 4;
gSideRoundoverRadius = 4;
gMainWidth = 16;
gTopWidth = 30;
gRibWidth = 3.4;
gHoleSpacing = 20;
gBeltWide = 14; // 1/4" belt + slop
gBeltPitch = 2.032; // MXL in mm
gBeltThick = 1.5;
e = 0.02;

// screw_clearance_diameter, socket_head_clearance_diameter, nut outside diameter
// TODO untested numbers, these should move to a common include.
M3 = [3.5, 6.4, 6.4];
M5 = [5.5, 9.4, 9.4];
M8 = [8, 13, 13];

module InsetHole(x) {
  d = x[0];
  socket_head = x[1];
  union() {
    translate([0,0,-50]) cylinder(r=d/2, h=100);
    cylinder(r=socket_head/2, h=10);
  }
}

module NutHole(x) {
  d = x[0];
  nut_size = x[2];
  union() {
    translate([0,0,1]) cylinder(r=d/2, h=50);
    cylinder(r=nut_size/2, $fn=6, h=6);
  }
}

module Rib(t=4) {
  translate([-t/2,0,-0.1])
    rotate([90,0,90])
      linear_extrude(height=t)
        polygon(points=[[0,0],[40,0],[0,gRibHeight]],paths=[[0,1,2]]);
}

module Roundover(r) {
  translate([0.1,0.1,-50])
    difference() {
      cube([r,r,100]);
      translate([0,0,-1]) cylinder(r=r,h=102,$fn=20);
    }
}

module Upright() {
  difference() {
    union() {
      // body, for now (until beziers work)
      translate([0,-25,(gThick+gRibHeight)/2]) cube([gTopWidth,10,gThick+gRibHeight], center=true);
      translate([0,0,gThick/2]) cube([gMainWidth,60,gThick], center=true);
      // ribs
      translate([(gMainWidth-gRibWidth)/2,-20,gThick]) Rib(t=gRibWidth);
      translate([-(gMainWidth-gRibWidth)/2,-20,gThick]) Rib(t=gRibWidth);
    }
    // Chamfer top edges
    // TODO make these translation numbers constants as well, and fix Roundover's
    // origin.
    translate([gTopWidth/2-gTopRoundoverRadius,0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,0])
        Roundover(r=gTopRoundoverRadius);
    translate([-(gTopWidth/2-gTopRoundoverRadius),0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,180])
        Roundover(r=gTopRoundoverRadius);
    // Chamfer edges near bottom mounting hole
    translate([gMainWidth/2-gSideRoundoverRadius,26,0])
      Roundover(r=gSideRoundoverRadius);
    translate([-(gMainWidth/2-gSideRoundoverRadius),26,0])
      rotate([0,0,90])
        Roundover(r=gSideRoundoverRadius);
    // M5 mounting holes
    translate([0,0,gThick-2]) InsetHole(M5);
    translate([0,20,gThick-2]) InsetHole(M5);
    // M3 nut traps (20mm spacing, for compat with old part)
    translate([-gHoleSpacing/2,-16,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
    translate([gHoleSpacing/2,-16,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
  }
}

module Belt() {
  translate([0,0,gBeltThick/2]) cube([gBeltWide,30,gBeltThick], center=true);
  for(i = [0:30/gBeltPitch]) {
    translate([0,i * gBeltPitch,-gBeltThick/2+e]) cube([gBeltWide,gBeltPitch / 2,gBeltThick], center=true);
  }
}

module Top() {
  rotate([-90,0,0]) difference() {
    translate([0,-5,(gThick+gRibHeight)/2]) cube([gTopWidth,10,gThick+gRibHeight], center=true);
    // holes
    translate([-gHoleSpacing/2,20,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
    translate([gHoleSpacing/2,20,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
    // chamfer
    translate([gTopWidth/2-gTopRoundoverRadius,0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,0])
        Roundover(r=gTopRoundoverRadius);
    translate([-(gTopWidth/2-gTopRoundoverRadius),0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,180])
        Roundover(r=gTopRoundoverRadius);
    // TODO remove 12 as magic number
    translate([0,-12+(1.6*gBeltThick),0]) rotate([90,0,0]) Belt();
  }
}

union() {
  Upright();
  translate([0,-50,0]) Top();
}

// This printed part is one possible way to anchor Shapeoko belts.
// See http://www.shapeoko.com/wiki/index.php/Belt_Anchors for other ideas.
// Motivated by http://www.thingiverse.com/thing:33881 not being strong enough
// when I initially printed it.

// Upright: printed, but not stress tested, looks sturdy
// Top: completely untested

gThick = 8;
gRibHeight = 6;
gTopRoundoverRadius = 4;
gSideRoundoverRadius = 4;
gMainWidth = 16;
gTopWidth = 30;
gRibWidth = 3.5;
gHoleSpacing = 20;
gBeltWide = 14; // 1/4" belt + slop
gBeltPitch = 2.032; // MXL in mm
gBeltThick = 1.5;
e = 0.02;

// screw_clearance_diameter, socket_head_clearance_diameter, nut outside diameter
// TODO untested numbers, these should move to a common include.
M3 = [3.5, 6, 6];
M5 = [5.5, 9, 9];
M8 = [8, 13, 13];

module InsetHole(x) {
  d = x[0];
  socket_head = x[1];
  union() {
    translate([0,0,-50]) cylinder(r=d/2, h=100);
    cylinder(r=socket_head/2, h=10);
  }
}

module NutHole(x) {
  d = x[0];
  nut_size = x[2];
  union() {
    translate([0,0,1]) cylinder(r=d/2, h=50);
    cylinder(r=nut_size/2, $fn=6, h=6);
  }
}

module Rib(t=4) {
  translate([-t/2,0,-0.1])
    rotate([90,0,90])
      linear_extrude(height=t)
        polygon(points=[[0,0],[40,0],[0,gRibHeight]],paths=[[0,1,2]]);
}

module Roundover(r) {
  translate([0.1,0.1,-50])
    difference() {
      cube([r,r,100]);
      translate([0,0,-1]) cylinder(r=r,h=102,$fn=20);
    }
}

module Upright() {
  difference() {
    union() {
      // body, for now (until beziers work)
      translate([0,-25,(gThick+gRibHeight)/2]) cube([gTopWidth,10,gThick+gRibHeight], center=true);
      translate([0,0,gThick/2]) cube([gMainWidth,60,gThick], center=true);
      // ribs
      translate([(gMainWidth-gRibWidth)/2,-20,gThick]) Rib(t=gRibWidth);
      translate([-(gMainWidth-gRibWidth)/2,-20,gThick]) Rib(t=gRibWidth);
    }
    // Chamfer top edges
    // TODO make these translation numbers constants as well, and fix Roundover's
    // origin.
    translate([gTopWidth/2-gTopRoundoverRadius,0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,0])
        Roundover(r=gTopRoundoverRadius);
    translate([-(gTopWidth/2-gTopRoundoverRadius),0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,180])
        Roundover(r=gTopRoundoverRadius);
    // Chamfer edges near bottom mounting hole
    translate([gMainWidth/2-gSideRoundoverRadius,26,0])
      Roundover(r=gSideRoundoverRadius);
    translate([-(gMainWidth/2-gSideRoundoverRadius),26,0])
      rotate([0,0,90])
        Roundover(r=gSideRoundoverRadius);
    // M5 mounting holes
    translate([0,0,gThick-2]) InsetHole(M5);
    translate([0,20,gThick-2]) InsetHole(M5);
    // M3 nut traps (20mm spacing, for compat with old part)
    translate([-gHoleSpacing/2,-16,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
    translate([gHoleSpacing/2,-16,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
  }
}

module Belt() {
  translate([0,0,gBeltThick/2]) cube([gBeltWide,30,gBeltThick], center=true);
  for(i = [0:30/gBeltPitch]) {
    translate([0,i * gBeltPitch,-gBeltThick/2+e]) cube([gBeltWide,gBeltPitch / 2,gBeltThick], center=true);
  }
}

module Top() {
  rotate([-90,0,0]) difference() {
    translate([0,-5,(gThick+gRibHeight)/2]) cube([gTopWidth,10,gThick+gRibHeight], center=true);
    // holes
    translate([-gHoleSpacing/2,20,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
    translate([gHoleSpacing/2,20,(gThick+gRibHeight)/2]) rotate([90,-90,0]) NutHole(M3);
    // chamfer
    translate([gTopWidth/2-gTopRoundoverRadius,0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,0])
        Roundover(r=gTopRoundoverRadius);
    translate([-(gTopWidth/2-gTopRoundoverRadius),0,gThick+gRibHeight-gTopRoundoverRadius])
      rotate([90,0,180])
        Roundover(r=gTopRoundoverRadius);
    // TODO remove 12 as magic number
    translate([0,-12+(1.6*gBeltThick),0]) rotate([90,0,0]) Belt();
  }
}

union() {
  Upright();
  translate([0,-50,0]) Top();
}


removed most of the elements. while it was roughy the copy of the original part, it turned out that most of it is not really needed, and may even make mounting more difficult. the minimal working part is just a drive connection, screwd to the brush. that's it. :)

difference()
{
  // main screw mount - top part
  {
    int_cube_size=9.5;
    translate([-int_cube_size/2, -int_cube_size/2, 0])
      cube([int_cube_size, int_cube_size, 3]);
  }
  // screw hole
  screw_box_size=7+0.5;
  translate([-screw_box_size/2, -screw_box_size/2, 0.5])
    cube([screw_box_size, screw_box_size, 3-0.5]);
  cylinder(r=4/2, h=12, $fs=1);
}

difference()
{
  union()
  {
    // main cylinder
    difference()
    {
      cylinder(r=24/2, h=2.5);
      cylinder(r=(16+1.5)/2, h=1.5);
    }

    // 3 side blockers
    for(angle = [0, 120, 240])
      rotate([0, 0, angle])
        translate([-2/2, -1/2, 0] + [16/2-1/2, 0, 0])
          cube([2.5, 1, 2]);

    // main screw mount - bottom part
    intersection()
    {
      int_cube_size=7.2;
      rotate([0, 0, 0])
        translate([-int_cube_size/2, -int_cube_size/2, 0])
          cube([int_cube_size, int_cube_size, 2]);
      rotate([0, 0, 45])
        translate([-int_cube_size/2, -int_cube_size/2, 0])
          cube([int_cube_size, int_cube_size, 2]);
    }

    // main screw mount - top part
    {
      int_cube_size=9.5;
      translate([-int_cube_size/2, -int_cube_size/2, 0] + [0, 0, 2.5])
        cube([int_cube_size, int_cube_size, 3]);
    }
  }
  // screw hole
  screw_box_size=7+0.5;
  translate([-screw_box_size/2, -screw_box_size/2, 2.5+0.5])
    cube([screw_box_size, screw_box_size, 3-0.5]);
  cylinder(r=4/2, h=12, $fs=1);
}


fix typo in the baseplate scad file

fix typo in the baseplate scad file

bar_depth = 3;

pi_height = 56;
pi_width = 85;
pi_standoff_radius = 4;

ledstrip_depth = 4;
ledstrip_height = 17;
ledstrip_length = 1000;

$fa = 1;
$fs = 0.1;

module ledstrip () {
    color("green") cube([ledstrip_length, ledstrip_height, ledstrip_depth]);
}

module pi() {
    color("green") cube([pi_width, pi_height, 2]);
}

module device_support(bar_height) {
    radius = bar_height / 2;
    device_support_width = pi_width + 2 * radius;
    device_support_height = pi_height + 2 * radius;
    inner_plate_width = device_support_width - 2 * radius;
    inner_plate_height = device_support_height - 2 * radius;

    translate([radius, radius + inner_plate_height, 0])
        cylinder(h = bar_depth, r = radius);
    translate([radius + inner_plate_width, radius + inner_plate_height, 0])
        cylinder(h = bar_depth, r = radius);
    translate([0, -bar_height / 2, 0])
        cube([device_support_width, device_support_height, bar_depth]);
    translate([radius, device_support_height - radius, 0])
        cube([inner_plate_width, radius, bar_depth]);

    translate([device_support_width, 0, 0]) {
        difference() {
            cube([radius, radius, 1]);
            translate([radius, radius, 0])
                cylinder(h = bar_depth, r = radius);
        }
    }
}

module screw_holes(bar_end_radius, bar_length, bar_height) {
    translate([bar_end_radius, bar_end_radius, - bar_depth / 2])
        cylinder(h = bar_depth * 2, r = 1.5);

    translate([bar_length - bar_end_radius, bar_end_radius, - bar_depth / 2])
        cylinder(h = bar_depth * 2, r = 1.5);
}

module pi_holes(bar_height) {
    pi_padding = bar_height / 2;
    translate([pi_padding + pi_standoff_radius, bar_height + pi_padding + pi_height + 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }
    translate([pi_padding + pi_width - 10, bar_height + pi_padding + pi_height + 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }
    translate([pi_padding + pi_standoff_radius, bar_height + pi_padding - 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }
    translate([pi_padding + 47, bar_height + pi_padding - 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }

}

module bar(length, height) {
    radius = height / 2;
    difference() {
        union() {
            translate([radius, radius, 0])
                cylinder(h = bar_depth, r = radius);
            translate([radius, 0, 0])
                cube([length - 2*radius, height, bar_depth]);
            translate([length - radius, radius, 0])
                cylinder(h = bar_depth, r = radius);
        }
        screw_holes();
    }
}

module top_bar(length, height) {
    difference() {
        bar(length, height);
        screw_holes(height / 2, length);
    }
}

module bottom_bar_and_support(bar_length, bar_height) {
    difference() {
        union() {
            translate([0,bar_height,0])
                device_support(bar_height);
            bar(bar_length, bar_height);
        }
        screw_holes(bar_height / 2, bar_length);
        pi_holes(bar_height);
    }
}

module 3dmodel(bar_height, bar_length) {
    translate([bar_height / 2, bar_height + bar_height / 2 , bar_depth])
        pi();
    translate([bar_height / 2 + 15, bar_height / 2 - 17 / 2, bar_depth])
        ledstrip();
    bottom_bar_and_support(bar_length, bar_height);
    translate([0, 0, bar_depth + ledstrip_depth]) {
        top_bar(bar_length, bar_height);
    }
}

module cutout(bar_height, bar_length) {
    projection() {
        translate([0, bar_height + 10, 0])
            bottom_bar_and_support(bar_length, bar_height);
        top_bar(bar_length, bar_height);
    }
}

/*
3dmodel(30, 1060);
translate([5, 5, 10])
    3dmodel(20, 1050);
*/

cutout(30, 1060);
translate([1040, 220, 0])
    rotate([0, 0, 180])
        cutout(20, 1040);

bar_depth = 3;

pi_height = 56;
pi_width = 85;
pi_standoff_radius = 4;

ledstrip_depth = 4;
ledstrip_height = 17;
ledstrip_length = 1000;

$fa = 1;
$fs = 0.1;

module ledstrip () {
    color("green") cube([ledstrip_length, ledstrip_height, ledstrip_depth]);
}

module pi() {
    color("green") cube([pi_width, pi_height, 2]);
}

module device_support(bar_height) {
    radius = bar_height / 2;
    device_support_width = pi_width + 2 * radius;
    device_support_height = pi_height + 2 * radius;
    inner_plate_width = device_support_width - 2 * radius;
    inner_plate_height = device_support_height - 2 * radius;

    translate([radius, radius + inner_plate_height, 0])
        cylinder(h = bar_depth, r = radius);
    translate([radius + inner_plate_width, radius + inner_plate_height, 0])
        cylinder(h = bar_depth, r = radius);
    translate([0, -bar_height / 2, 0])
        cube([device_support_width, device_support_height, bar_depth]);
    translate([radius, device_support_height - radius, 0])
        cube([inner_plate_width, radius, bar_depth]);

    translate([device_support_width, 0, 0]) {
        difference() {
            cube([radius, radius, 1]);
            translate([radius, radius, 0])
                cylinder(h = bar_depth, r = radius);
        }
    }
}

module screw_holes(bar_end_radius, bar_length, bar_height) {
    translate([bar_end_radius, bar_end_radius, - bar_depth / 2])
        cylinder(h = bar_depth * 2, r = 1.5);

    translate([bar_length - bar_end_radius, bar_end_radius, - bar_depth / 2])
        cylinder(h = bar_depth * 2, r = 1.5);
}

module pi_holes(bar_height) {
    pi_padding = bar_height / 2;
    translate([pi_padding + pi_standoff_radius, bar_height + pi_padding + pi_height + 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }
    translate([pi_padding + pi_width - 10, bar_height + pi_padding + pi_height + 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }
    translate([pi_padding + pi_standoff_radius, bar_height + pi_padding - 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }
    translate([pi_padding + 47, bar_height + pi_padding - 0.5, - bar_depth / 2]) {
        cylinder(h = bar_depth * 2, r = 2);
    }

}

module bar(length, height) {
    radius = height / 2;
    difference() {
        union() {
            translate([radius, radius, 0])
                cylinder(h = bar_depth, r = radius);
            translate([radius, 0, 0])
                cube([length - 2*radius, height, bar_depth]);
            translate([length - radius, radius, 0])
                cylinder(h = bar_depth, r = radius);
        }
        screw_holes();
    }
}

module top_bar(length, height) {
    difference() {
        bar(length, height);
        screw_holes(height / 2, length);
    }
}

module bottom_bar_and_support(bar_length, bar_height) {
    difference() {
        union() {
            translate([0,bar_height,0])
                device_support(bar_height);
            bar(bar_length, bar_height);
        }
        screw_holes(bar_height / 2, bar_length);
        pi_holes(bar_height);
    }
}

module 3dmodel(bar_height, bar_length) {
    translate([bar_height / 2, bar_height + bar_height / 2 , bar_depth])
        pi();
    translate([bar_height / 2 + 15, bar_height / 2 - 17 / 2, bar_depth])
        ledstrip();
    bottom_bar_and_support(bar_length, bar_height);
    translate([0, 0, bar_depth + ledstrip_depth]) {
        top_bar(bar_length, bar_height);
    }
}

module cutout(bar_height, bar_length) {
    projection() {
        translate([0, bar_height + 10, 0])
            bottom_bar_and_support(bar_length, bar_height);
        top_bar(bar_length, bar_height);
    }
}

/*
3dmodel(30, 1060);
translate([5, 5, 10])
    3dmodel(20, 1050);
*/

cutout(30, 1060);
translate([bar_length, 220, 0])
    rotate([0, 0, 180])
        cutout(20, 1040);


misc/lerp: fix tests

misc/lerp: fix tests

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

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

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

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


Completed - 1st rev

Completed - 1st rev

This is the version as originally envisioned. Now to print out a small version and test it out...

// Cushion Grabber - see README.md for details.
// Author: Joseph M. Joy

function range(stop, start=0,  step=1) = [start: step: stop-1];

// Coords of an isoceles right triangle
function ir_tri(base) = [[0,0], [0, base], [base, 0]];

// Extruded version
module rt_prism(base, height)  {
    linear_extrude(height) polygon(ir_tri(base));
}

// A row of extruded triangles
module prism_rows(base, height, n) {
    for (i = range(n)) {
         translate([2*base*i, 0, 0]) rt_prism(base, height);
    }
}

// Dome - used for differencing
module dome(width) {
    r = 10;
    ceiling_height = 3; // inner height;
    outer_height = r; // height of top of dome from above
    scale([1, ceiling_height/r, 1])
        difference() {
            translate([-r, -r, -r]) cube([width+2*r, 3*outer_height, width+2*r]);
            translate([r, -r, r]) minkowski() {
                cube([width-2*r, r, width-2*r]);
                sphere(r);
            }
        }
}

// Final grabber structure
module cushion_grabber(width) {
    tri_height = 2;
    base_height = 1;
    n = width / (2*tri_height); // each row is 2*tri_height, including space
    translate([0, width, 0]) rotate([90, 0, 0])
    difference() {
        union() {
            cube([width, base_height, width]);
            translate([0, base_height, 0]) prism_rows(tri_height, width, n);
        }
        dome(width);
    }
}

cushion_grabber(50);
//dome(100);

// Cushion Grabber - see README.md for details.
// Author: Joseph M. Joy

function range(stop, start=0,  step=1) = [start: step: stop-1];

// Coords of an isoceles right triangle
function ir_tri(base) = [[0,0], [0, base], [base, 0]];

// Extruded version
module rt_prism(base, height)  {
    linear_extrude(height) polygon(ir_tri(base));
}

// A row of them
module prism_rows(base, height, n) {
    for (i = range(n)) {
         translate([2*base*i, 0, 0]) rt_prism(base, height);
    }
}

//

prism_rows(10, 40, 10);


added tolerance

added tolerance

inch    = 25.4;
width   = inch * 9;
height  = inch * 6;
glass_thickness = inch / 5 + 0.2;
wall_thickness = inch / 8;
cut_depth = 1;
tank_depth = 40;
support_height = 2;
wall_height = tank_depth + support_height + glass_thickness;

module glass(w,d,h) {
  color([1,1,1,0.35])
  cube([w,d,h], center=true);
}

module side0(width, height, support_height, wall_thickness, cut_depth, glass_thickness) {
  gap_width = width + 2;
  difference() {
    cube([width,height,wall_thickness], center=true);
    translate([
      -1,
      height / 2 - support_height - glass_thickness / 2,
      wall_thickness-cut_depth
    ])
      cube([gap_width,glass_thickness,wall_thickness], center=true);
  }
}

module side1(width, height, support_height, wall_thickness, cut_depth, glass_thickness) {
  color([1,0.1,0.0,0.85])
    side0(width, height, support_height, wall_thickness, cut_depth, glass_thickness);
}

module side2(width, height, support_height, wall_thickness, cut_depth, glass_thickness) {
  height2 = height + 2;
  color([1,0.4,0.1,0.85])
    difference() {
      side0(width, height, support_height, wall_thickness, cut_depth, glass_thickness);
      translate([width/2-wall_thickness/2+1,0,wall_thickness-cut_depth])
        cube([wall_thickness+2,height2,wall_thickness], center=true);
      translate([-width/2+wall_thickness/2-1,0,wall_thickness-cut_depth])
        cube([wall_thickness+2,height2,wall_thickness], center=true);
    }
}

module sideA() {
  side1(width + cut_depth * 0, wall_height, support_height, wall_thickness, cut_depth, glass_thickness);
}

module sideB() {
  side2(height+(wall_thickness-cut_depth)*2, wall_height, support_height, wall_thickness, cut_depth, glass_thickness);
}

inch    = 25.4;
width   = inch * 9;
height  = inch * 6;
glass_thickness = inch / 5;
wall_thickness = inch / 8;
cut_depth = 1;
tank_depth = 40;
support_height = 2;
wall_height = tank_depth + support_height + glass_thickness;

module glass(w,d,h) {
  color([1,1,1,0.35])
  cube([w,d,h], center=true);
}

module side0(width, height, support_height, wall_thickness, cut_depth, glass_thickness) {
  gap_width = width + 2;
  difference() {
    cube([width,height,wall_thickness], center=true);
    translate([
      -1,
      height / 2 - support_height - glass_thickness / 2,
      wall_thickness-cut_depth
    ])
      cube([gap_width,glass_thickness,wall_thickness], center=true);
  }
}

module side1(width, height, support_height, wall_thickness, cut_depth, glass_thickness) {
  color([1,0.1,0.0,0.85])
    side0(width, height, support_height, wall_thickness, cut_depth, glass_thickness);
}

module side2(width, height, support_height, wall_thickness, cut_depth, glass_thickness) {
  height2 = height + 2;
  color([1,0.4,0.1,0.85])
    difference() {
      side0(width, height, support_height, wall_thickness, cut_depth, glass_thickness);
      translate([width/2-wall_thickness/2+1,0,wall_thickness-cut_depth])
        cube([wall_thickness+2,height2,wall_thickness], center=true);
      translate([-width/2+wall_thickness/2-1,0,wall_thickness-cut_depth])
        cube([wall_thickness+2,height2,wall_thickness], center=true);
    }
}

module sideA() {
  side1(width + cut_depth * 0, wall_height, support_height, wall_thickness, cut_depth, glass_thickness);
}

module sideB() {
  side2(height+(wall_thickness-cut_depth)*2, wall_height, support_height, wall_thickness, cut_depth, glass_thickness);
}


Added more cable glands

Added more cable glands

module spacer1() {
  difference() {
    union() {
      cylinder(d=12,h=5,$fn=36);
      cylinder(d=7,h=6,$fn=36);
    }
    cylinder(d=4.5,h=100,center=true,$fn=36);
  }
}
module spacer2() {
  difference() {
    cylinder(d=14,h=1.7,$fn=36);
    translate([0,0,0.7]) {
      cylinder(d=9,h=10,$fn=36);
      translate([0,3,5])cube([3,5,10],true);
    }
    cylinder(d=5.5,h=100,center=true,$fn=36);
  }
}

module voltmeter_holder() {
  difference() {
    union() {
      cube([34,22,5],true);
    }
    cube([23.5,15,20],true);
    for(a=[0,1])mirror([a,0])translate([14,0]) cylinder(d=2,h=20,center=true,$fn=24);
  }
}
module fuse_holder() {
  difference() {
    union() {
      translate([-7,-7])cube([7+7+12,7+7+30+12,5]);
    }
    for(xy=[[0,0],[0,30],[12,42]]) translate(xy)
      cylinder(d=6,h=20,center=true,$fn=36);
    for(xy=[[10,0],[0,42],[12,30]]) translate(xy)
      cylinder(d=2.5,h=20,center=true,$fn=36);
  }
}

fuse_holder();

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


Getting started on box itself

Getting started on box itself

//
// This is the iotsa board itself, including the important connectors and
// holes. You can define here if you have shortened the board, which will
// not only change the board itself, but also the box we are going to create around it.
//
numberOfRowsRemoved = 0;
extraMMRemoved = 0; // 0 if nothing removed, 2.2 if any rows removed.
numberOfMMRemoved = extraMMRemoved + (numberOfRowsRemoved*2.54);

boardThickness = 1.8; // The thickness of the PCB board
regulatorAngle = 35; // 0, or higher if you've bent the regulator over backwards.

module iotsaBoard() {
    module esp12() {
        union() {
            cube([24, 16, 2]); // the esp-12 board
            translate([8, 1.5, 2]) cube([15, 13, 2.5]); // the metal cap
        }
    }
    module regulator() {
        rotate([-regulatorAngle, 0, 0])
        translate([0, 0, 5])
        union() {
            translate([0, 4.5, 0]) cube([10.2, 1.3, 15]);
            translate([0, 0, 0]) cube([10.2, 4.5, 8.5]);
        }
    }
    module powerjack() {
        difference() {
            cube([9, 15, 11]);
            translate([4.5, -1, 7]) rotate([-90, 0, 0]) cylinder(14, d=6);
        }
    }
    translate([0, 0, -1.8])
    union() {
        // The board
        difference() {
            cube([63-numberOfMMRemoved,43,1.8]);
            translate([2, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([2, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
        }
        // The components
        translate([-5, 13.5, 1.8]) esp12();
        translate([20, 15, 1.8]) regulator();
        translate([22, -3, 1.8]) powerjack();
    }
}

//
// Box parameters. We are going to compute the size from the parameters.
//
iotsaComponentHeight = 20;  // Make sure this is high enough
iotsaSolderingHeight = 3;   // How far the soldering extends below the board.
boxThickness = 3;
boxBottomThickness = boxThickness;
boxFrontThickness = boxThickness;   // This is where the power connector is
boxLeftThickness = boxThickness;
boxRightThickness = boxThickness;
boxBackThickness = boxThickness;
boxTopThickness = boxThickness;

module box() {
    innerXSize = (63-numberOfMMRemoved) + 5; // Add 5 for how far the esp12 antenna extends
    innerYSize = 43;
    innerZSize = boardThickness + iotsaComponentHeight + iotsaSolderingHeight;

    outerXSize = innerXSize + boxLeftThickness + boxRightThickness;
    outerYSize = innerYSize + boxFrontThickness + boxBackThickness;
    outerZSize = innerZSize + boxBottomThickness + boxTopThickness;

    strutThickness = 2;
    module basicBox() {
         difference() {
            cube([outerXSize, outerYSize, outerZSize-boxTopThickness]);
            translate([boxLeftThickness, boxFrontThickness, boxBottomThickness])
                cube([innerXSize, innerYSize, innerZSize+1]);
        }
    }
    module frontHoles() {
        // Holes in the front. X and Z relative to iotsa frontleft corner, Y should be 2*frontThickness.
        translate([22, 0, 0]) cube([9, 2*boxFrontThickness, 11]);
    }
    union() {
        difference() {
            translate([-5-boxLeftThickness, -boxFrontThickness, -(boardThickness+iotsaSolderingHeight+boxBottomThickness)])
                union() {
                    basicBox();
                    cube([outerXSize, boxFrontThickness+strutThickness, boxBottomThickness+iotsaSolderingHeight]);
                    translate([0, innerYSize, 0])
                        cube([outerXSize, boxFrontThickness+strutThickness, boxBottomThickness+iotsaSolderingHeight]);
                }
                translate([0, -boxFrontThickness, 0]) frontHoles();
            }
        % iotsaBoard();
    }
}
box();

//
// This is the iotsa board itself, including the important connectors and
// holes. You can define here if you have shortened the board, which will
// not only change the board itself, but also the box we are going to create around it.
//
numberOfRowsRemoved = 0;
extraMMRemoved = 0; // 0 if nothing removed, 2.2 if any rows removed.
numberOfMMRemoved = extraMMRemoved + (numberOfRowsRemoved*2.54);

module iotsaBoard() {
    module esp12() {
        union() {
            cube([24, 16, 2]); // the esp-12 board
            translate([8, 1.5, 2]) cube([15, 13, 2.5]); // the metal cap
        }
    }
    module regulator() {
        translate([0, 0, 2])
        union() {
            translate([0, 4.5, 0]) cube([10.2, 1.3, 15]);
            translate([0, 0, 0]) cube([10.2, 4.5, 8.5]);
        }
    }
    module powerjack() {
        difference() {
            cube([9, 15, 11]);
            translate([4.5, -1, 7]) rotate([-90, 0, 0]) cylinder(14, d=6);
        }
    }
    union() {
        // The board
        difference() {
            cube([63-numberOfMMRemoved,43,1.8]);
            translate([2, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([2, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
        }
        // The components
        translate([-5, 13.5, 1.8]) esp12();
        translate([20, 15, 1.8]) regulator();
        translate([22, -3, 1.8]) powerjack();
    }
}

iotsaBoard();