Add support bar only for length greater than 1

/*
* font3d
* (C) Copyright 2014 Cameron Lai
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 3.0 which accompanies this distribution, and is available at
* https://www.gnu.org/licenses/lgpl-3.0.txt
*
* font3d is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/

include<dimension.scad>
include<basic.scad>

font3d("AB", "basic");

module font3d(inputString, fontType)
{
	numChar = len(inputString);
	// Loop around characters
	for (i=[0:numChar-1])
	{
		// Pick corresponding font
		translate([i*xdim,0,0])
		if (fontType=="basic") basic(inputString[i]);
		else
		{
		   echo("Font type not recognized");
		}
	}
	// Add support bar
	if (numChar>1) support_bar(len(inputString));
}

/*
* font3d
* (C) Copyright 2014 Cameron Lai
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 3.0 which accompanies this distribution, and is available at
* https://www.gnu.org/licenses/lgpl-3.0.txt
*
* font3d is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/

include<dimension.scad>
include<basic.scad>

module font3d(inputString, fontType)
{
	// Loop around characters
	for (i=[0:len(inputString)-1])
	{
		// Pick corresponding font
		translate([i*xdim,0,0])
		if (fontType=="basic") basic(inputString[i]);
		else
		{
		   echo("Font type not recognized");
		}
	}
	// Add support bar
	support_bar(len(inputString));
}


feat: rework the visual test operators, move the title to the top-left corder, center the tested elements, and display the axis

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

/**
 * Part of the camelSCAD library.
 *
 * Testing helpers for visual tests.
 *
 * @package shape/context
 * @author jsconan
 */

/**
 * Renders a test element. If no children is given, a cube with an arrow on top is rendered.
 * @param String|Vector [c] - The color of the element.
 * @param Number|Vector [angle] - The angle of the element.
 * @param Number|Vector [size] - The size of the element. It only applies if no children is given.
 * @param Boolean [center] - Whether or not the element is centered. It only applies if no children is given.
 */
module testElement(c="red", angle=0, size=1, center=false) {
    color(c) {
        rotate(angle) {
            if ($children)  {
                children();
            } else let(
                size = vector3D(size),
                half = size / 2,
                quarter = size / 4,
                offset = center ? -half : vector3D()
            ) {
                translate(offset) {
                    linear_extrude(height=size.z, convexity=10) {
                        polygon(
                            points = path([
                                ["P", half.x, size.y],
                                ["L", half.x, -half.y],
                                ["H", -quarter.x],
                                ["V", -half.y],
                                ["H", -half.x],
                                ["V", half.y],
                                ["H", -quarter.x],
                            ]),
                            convexity = 10
                        );
                    }
                    translateZ(quarter.z) {
                        cube(size=[size.x, size.y, half.z]);
                    }
                }
            }
        }
    }
}

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

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

/**
 * Renders a test area, moving the tested shapes in it.
 * A testbed is shown, unless the global variable `TESTBED_SHOW` is set to `false`.
 * @param Number index - The index number of the test.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 */
module visualTest(index, length, width, title="test", margin=1, cols=0) {
    title = str(title, " ", index);
    fontSize = min(length, width) / len(title);
    margin = vector2D(margin);
    height = (length + width) / 2;
    x = cols ? index % cols : index;
    y = cols ? floor(index / cols) : 0;
    offset = [length, width, 0] / 2;
    testPosition = [(length + margin.x) * x, (width + margin.y) * y, 0] + offset;
    titlePosition = [length * .1, width * .9, 0] - offset;

    translate(testPosition) {
        // test area
        if (TESTBED_SHOW) {
            // Plate
            testbedExtrude(.1) {
                square([length, width], center=true);
            }
            // Z-axis
            rotateY(90) {
                testbedExtrude(.5) {
                    square([height, TESTBED_THICKNESS], center=true);
                }
            }
            testbedExtrude(.5) {
                // X-axis
                square([length, TESTBED_THICKNESS], center=true);
                // Y-axis
                square([TESTBED_THICKNESS, width], center=true);
            }
            // Title
            translate(titlePosition) {
                testbedExtrude(1) {
                    text(title, size=fontSize, font="Liberation Sans", valign="top", halign="left");
                }
            }
        }

        // tested elements
        children();
    }
}

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

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

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

/**
 * Part of the camelSCAD library.
 *
 * Testing helpers for visual tests.
 *
 * @package shape/context
 * @author jsconan
 */

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

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

/**
 * Renders a test element. If no children is given, a cube with an arrow on top is rendered.
 * @param String|Vector [c] - The color of the element.
 * @param Number|Vector [angle] - The angle of the element.
 * @param Number|Vector [size] - The size of the element. It only applies if no children is given.
 * @param Boolean [center] - Whether or not the element is centered. It only applies if no children is given.
 */
module testElement(c="red", angle=0, size=1, center=false) {
    color(c) {
        rotate(angle) {
            if ($children)  {
                children();
            } else let(
                size = vector3D(size),
                half = size / 2,
                quarter = size / 4,
                offset = center ? -half : vector3D()
            ) {
                translate(offset) {
                    linear_extrude(height=size.z, convexity=10) {
                        polygon(
                            points = path([
                                ["P", half.x, size.y],
                                ["L", half.x, -half.y],
                                ["H", -quarter.x],
                                ["V", -half.y],
                                ["H", -half.x],
                                ["V", half.y],
                                ["H", -quarter.x],
                            ]),
                            convexity = 10
                        );
                    }
                    translateZ(quarter.z) {
                        cube(size=[size.x, size.y, half.z]);
                    }
                }
            }
        }
    }
}

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

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

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

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

        // tested shapes
        children();
    }
}

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

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


base wall element, with smooth edges

base wall element, with smooth edges

/*
 * Brown Orla-B brush holder (wall-attached).
 */
$fn=10;

module wall(tr, size)
{
  translate(tr)
    hull()
      difference()
      {
        for(t=[ [-1,-1,0], [-1,+1,0], [+1,-1,0], [+1,+1,0] ])
        {
          assign( off = [size[0]/2*t[0], size[1]/2*t[1], 0*t[2]] )
            translate(off)
              sphere(size[2]);
        };
        translate([0,0,-size[2]]) cube(2*size, center=true);
      }
}

wall([0,0,0], [36,50,2]);

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


shapes: use is_list (not old is_array)

shapes: use is_list (not old is_array)

/*use <../scad-utils/transformations.scad>*/
include <system.scad>
include <units.scad>
use <misc.scad>
use <transforms.scad>

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

module rcubea(size=[10,10,10], round_r=1, align=N, extra_size=N, extra_align=N, orient=Z, orient_ref=Z, roll=0, extra_roll, extra_roll_orient)
{
    assert(size.x > round_r);
    assert(size.y > round_r);
    assert(size.z > round_r);

    size_align(size=size, extra_size=extra_size, align=align, extra_align=extra_align, orient=orient, orient_ref=orient_ref, roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    rcube(size=size+extra_size, round_r=round_r);
}

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

module pyramid(size=[10,10,10], orient, align)
{
    let(s=size)
    size_align(size=s, align=align, orient=orient)
    polyhedron(
        points=[
            // the four points at base
            [s.x/2,s.y/2,-s.z/2],[s.x/2,-s.y/2,-s.z/2],[-s.x/2,-s.y/2,-s.z/2],[-s.x/2,s.y/2,-s.z/2],
            // the apex point
            [0,0,s.z/2]
        ],
        faces=[
            // each triangle side
            [0,1,4],[1,2,4],[2,3,4],[3,0,4],
            // two triangles for square base
            [1,0,3],[2,1,3]
        ]
        );
}

module spherea(r, d, align=N, extra_r, extra_d, extra_align, orient=Z, orient_ref=Z, roll=0, extra_roll, extra_roll_orient)
{
    d_ = v_fallback(d, [r*2]);
    assert(d_ != U);

    extra_d_ = v_fallback(extra_d, [extra_r*2, 0]);

    size=[d_,d_,d_];
    extra_size=[extra_d_,extra_d_,extra_d_];
    size_align(size=size, extra_size=extra_size, align=align, extra_align=extra_align, orient=orient, orient_ref=orient_ref, roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    sphere(d=d_+extra_d_);
}

module cylindera(
        h=10,
        r=U,
        r1=U,
        r2=U,
        d=U,
        d1=U,
        d2=U,
        align=N,
        orient=Z,
        extra_h=0,
        extra_r=U,
        extra_d=U,
        extra_align=N,
        round_r=0,
        debug=false
        )
{
    assert(h>0);

    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, [extra_d/2, 0]);

    if(debug)
    {
        echo(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=Z, align=align, extra_align=extra_align)
    {
        if(round_r>0)
        {
            rcylindera(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, round_r=round_r);
        }
        else
        {
            cylinder(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, center=true);
        }
    }
}

// polycylinder (polyhole/3d printing size compensated hole)
module pcylindera(
        h=10,
        r=U,
        d=U,
        align=N,
        orient=Z,
        extra_h=0,
        extra_r=U,
        extra_d=U,
        extra_align=N,
        round_r=0,
        debug=false
        )
{
    pi=3.1415926536;

    d_ = v_fallback(d, [r*2]);
    r__ = v_fallback(r, [d_/2, d/2, r]);
    r_ = polyhole_d(r__*2)/2;
    $fn = polyhole_n(r__*2);

    extra_r_ = v_fallback(extra_r, [extra_d/2, 0]);

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

    size_align(size=[r_*2,r_*2,h], extra_size=[extra_r_*2, extra_r_*2, extra_h], orient=orient, orient_ref=Z, align=align, extra_align=extra_align)
    {
        if(round_r>0)
        {
            rcylindera(h=h+extra_h, r=r_+extra_r_, round_r=round_r);
        }
        else
        {
            cylinder(h=h+extra_h, r=r_+extra_r_, center=true);
        }
    }
}

module torus(d, r, radius, radial_width, align=N, orient=Z)
{
    r_ = v_fallback(r,[d/2, radius, 5]);
    size_align(size=[r_*2+radial_width*2, r_*2+radial_width*2, radial_width*2], align=align, orient=orient)
    rotate_extrude()
    translate(r_*X)
    circle(radial_width);
}

module rcylindera(
        h=10,
        r=U,
        r1=U,
        r2=U,
        d=U,
        d1=U,
        d2=U,
        align=N,
        orient=Z,
        extra_h=0,
        extra_r=U,
        extra_d=U,
        extra_align=N,
        round_r=1,
        debug=false,
        poly=false
        )
{
    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_)]);

    h_ = h+extra_h;

    extra_r_ = v_fallback(extra_r, [extra_d/2, 0]);

    assert(r1_ != U);
    assert(r2_ != U);
    assert(extra_r_ != U);

    assert(h >= 2*round_r);

    if(debug)
    {
        echo(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=Z, align=align, extra_align=extra_align)
    {
        if($preview_mode || round_r == 0)
        {
            cylindera(h=h_, r1=r1_+extra_r_, r2=r2_+extra_r_);
        }
        else
        {
            r_ = [r1_+extra_r_/2,r2_+extra_r_/2];

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

function circle_profile(r=10, fn=$fn) = [for (i=[0:fn-1]) [r*sin(i*360/fn), r*cos(i*360/fn)]];

function rounded_rectangle_profile(size=[10,10],r=[1,1],fn=32) = [
let(r_ = is_list(r) ? r : [r,r])
for (index = [0:fn-1])
let(a = index/fn*360)
[cos(a)*r.x, sin(a)*r.y]
+ sign_x(index, fn) * X * (size.x/2-r.x)
+ sign_y(index, fn) * Y * (size.y/2-r.y)
];

function sign_x(i,n) =
i < n/4 || i > n-n/4  ?  1 :
i > n/4 && i < n-n/4  ? -1 :
    0;

function sign_y(i,n) =
i > 0 && i < n/2  ?  1 :
i > n/2 ? -1 :
    0;

// From Obiscad
// Draw a point in the position given by the vector p
module point(p, r=0.7, fn)
{
    translate(p)
    sphere(r=r);
}

// Draw a vector poiting to the z axis
// This is an auxiliary module for implementing the vector module
//
// Parameters:
//  l: total vector length (line + arrow)
//  l_arrow: Vector arrow length
//  mark: If true, a mark is draw in the vector head, for having
// a visual reference of the rolling angle
module vectorz(l=10, l_arrow=4, mark=true)
{
    // vector body length (not including the arrow)
    lb = l - l_arrow;

    // The vector is located at 0,0,0
    union()
    {
        // Draw the arrow
        tz(lb)
        cylindera(r1=2/2, r2=0.2, h=l_arrow);

        // Draw the mark
        if(mark)
        tz(lb)
        tx(X)
        cubea([2,0.3,l_arrow*0.8], align=X);

        // Draw the body
        cylindera(r=1/2, h=lb, align=Z);
    }

    // Draw a sphere in the vector base
    spherea(r=1/2, align=-Z);
}

// From Obiscad,
// Draw a vector
//
// There are two modes of drawing the vector
// * Mode 1: Given by a cartesian point(x,y,z). A vector from the origin
//           to the end (x,y,z) is drawn. The l parameter (length) must
//           be 0  (l=0)
// * Mode 2: Give by direction and length
//           A vector of length l pointing to the direction given by
//           v is drawn
// Parameters:
//  v: Vector cartesian coordinates
//  l: total vector length (line + arrow)
//  l_arrow: Vector arrow length
//  mark: If true, a mark is draw in the vector head, for having
//    a visual reference of the rolling angle
module vector(v, l=4, l_arrow=2, mark=false)
{
  //-- Get the vector length from the coordinates
  mod = v_mod(v);

  //-- The vector is very easy implemented by means of the orientate
  //-- operator:
  //--  orientate(v) vectorz(l=mod, l_arrow=l_arrow)
  //--  BUT... in OPENSCAD 2012.02.22 the recursion does not
  //--    not work, so that if the user use the orientate operator
  //--    on a vector, openscad will ignore it..
  //-- The solution at the moment (I hope the openscad developers
  //--  implement the recursion in the near future...)
  //--  is to repite the orientate operation in this module

  //---- SAME CALCULATIONS THAN THE ORIENTATE OPERATOR!
  //-- Calculate the rotation axis

  vref = Z;
  raxis = v_cross(vref,v);

  //-- Calculate the angle between the vectors
  ang = v_anglev(vref,v);

  raxis_=ang==180&&raxis==N?X:raxis;

  //-- orientate the vector
  //-- Draw the vector. The vector length is given either
  //--- by the mod variable (when l=0) or by l (when l!=0)
  if (l==0)
  {
      rotate(a=ang, v=raxis_)
      vectorz(l=mod, l_arrow=l_arrow, mark=mark);
  }
  else
  {
      rotate(a=ang, v=raxis_)
      vectorz(l=l, l_arrow=l_arrow, mark=mark);
  }

}

// From Obiscad,
//--------------------------------------------------------------------
//-- Draw a connector
//-- A connector is defined a 3-tuple that consist of a point
//--- (the attachment point), and axis (the attachment axis) and
//--- an angle the connected part should be rotate around the
//--  attachment axis
//--
//--- Input parameters:
//--
//--  Connector c = [p , n, ang] where:
//--
//--     p : The attachment point
//--     v : The attachment axis
//--   ang : the angle
//--------------------------------------------------------------------
module connector(c)
{
    //-- Get the three components from the connector
    p = c[0];
    v = c[1];
    ang = c[2];

    //-- Draw the attachment point
    color(v)
    point(p);

    //-- Draw the attachment axis vector (with a mark)
    translate(p)
    rotate(a=ang, v=v)
    color(v)
    vector($fn=16, v=v_unitv(v), l=6, l_arrow=2, mark=true);
}

// 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=Z, align=N)
{
    size_align(size=[r*2, r*2, h], orient=orient, orient_ref=Z, 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=U, orient=Z, align=N)
{
    outer_d = d+thickness/2;
    inner_d = d-thickness/2;
    taper_h = taper_h == U ? min(h/4, (outer_d-inner_d)/2) : max(taper_h,h/2);
    taper_ = taper && taper_h > 0;
    size_align(size=[outer_d, outer_d, h], orient=orient, align=align);
    difference()
    {
        union()
        {
            cylindera(h=h-(taper_?taper_h*2:0), d=outer_d, orient=Z, align=N);
            if(taper_)
            {
                for(z=[-1,1])
                translate([0,0,z*(h/2-taper_h)])
                mirror([0,0,z==-1?1:0])
                cylindera(d1=outer_d, d2=outer_d-inner_d/4, h=taper_h, align=Z);
            }
        }
        if(taper_)
        {
            for(z=[-1,1])
            translate([0,0,z*(h/2-taper_h)])
            mirror([0,0,z==-1?1:0])
            cylindera(d1=inner_d, d2=outer_d, h=taper_h+.1, align=Z, extra_h=.2);

            // override fn for inner cylinder cut, to ensure same fragments as taper
            // this ensures cleaner mesh
            fn = fn_from_d(d=outer_d);
            cylindera(h=h+.2+taper_h, d=inner_d, orient=Z, align=N, $fn=fn);
        }
        else
        {
            cylindera(h=h, d=inner_d, orient=Z, align=N, extra_h=.4);
        }
    }
}

/**
 * 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=N, orient=Z)
{
    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, round_r=2, align=N, orient=Z)
{
    size = [a_len-round_r, depth-round_r, o_len-round_r];
    r_x = round_r;
    r_y = round_r;
    r_z = -round_r;

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

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

    /*translate([size[0]+4,0,0])*/
    /*color(X)*/
    /*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)+round_r,(size[0]/2)-r_x])
    for(y=[-(size[1]/2)+round_r,(size[1]/2)-r_y])
    for(z=[-(size[2]/2)+round_r,(size[2]/2)-r_z])
    translate([x,y,z])
    if(x<=1 || z<=1)
    sphere(r=round_r);
}

module teardrop(r, d, h=10, truncate=1, align=N, orient=Y, 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=Z, roll=roll)
    union()
    {
        linear_extrude(height = h, center = true, convexity = r_, twist = 0)
        circle(r = r_);

        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;
    all_axes()
    translate($axis*r*2)
    color($color)
    teardrop(r=r, h=20, orient=$axis, align=$axis);
}

// test cubea
if(false)
{
    w=5*mm;
    h=2*mm;
    all_axes()
    color($color)
    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=-Z);*/
    /*triangle(15, 10, 5, align=[1,1,1], orient=Z);*/

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

/**
 * 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)
{
    stack(axis=X, dist=15)
    {
        hollow_cylinder(thickness=5, h=10, taper=false, orient=Z, align=Z);
        hollow_cylinder(thickness=5, h=10, taper=true, orient=Z, align=Z);
    }

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

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

    /*cubea([10,10,10],X);*/
    /*%cubea([10,10,10],X,[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=X, extra_d=10);*/

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

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

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

    cylindera(h=20, r=5*mm, align=Y, orient=Z, $fn=100);

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

/*cylindera(d=10, h=10, orient=Y, align=Z);*/
}

if(false)
{
    /*cubea();*/
    /*rcubea(size=[10,10,10]);*/

    is_build = true;
    $fs = is_build ? 0.5 : 2;
    $fa = is_build ? 4 : 12;

    stack(axis=X, dist=15)
    {
        cylindera(h=10,r=5);

        rcylindera(h=10,r=5);

        rcubea([10,10,10]);
    }
}

if(false)
{
    r=5*mm;
    all_axes()
    translate($axis*r*2)
    color($color)
    /*orient(axis=$axis, axis_ref=Z)*/
    /*translate(Z*10)*/
    /*rcylindera(r1=5, r2=3, h=10);*/
    {
        rcylindera(h=20, extra_d = 1*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16); /*cylindera(h=20, round_r=1, extra_r = 2*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16);*/
        cylindera(h=20, extra_d = 1*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16); /*cylindera(h=20, round_r=1, extra_r = 2*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16);*/
    }

}

if(false)
{
    all_axes()
    translate($axis*r*2)
    color($color)
    {
        orient(axis=$axis, axis_ref=Z)
        hull()
        {
            cubea(size=[5,8,7]);
            translate(5*Z)
            cubea(size=[2,2,2]);
        }
    }
}

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

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

if(false)
{
    /*$fs = 0.5;*/
    /*$fa = 4;*/

    r=5;
    hull()
    {
        stack(dist=10, axis=-Z)
        {
            rcylindera(r=r, h=r*2, orient=Y, align=Y);
            rcylindera(r=r, h=r*2, orient=Y, align=Y);
        }

        rcylindera(r=5, h=r, orient=X, align=Y+X+Z);
    }
}

/*use <../scad-utils/transformations.scad>*/
include <system.scad>
include <units.scad>
use <misc.scad>
use <transforms.scad>

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

module rcubea(size=[10,10,10], round_r=1, align=N, extra_size=N, extra_align=N, orient=Z, orient_ref=Z, roll=0, extra_roll, extra_roll_orient)
{
    assert(size.x > round_r);
    assert(size.y > round_r);
    assert(size.z > round_r);

    size_align(size=size, extra_size=extra_size, align=align, extra_align=extra_align, orient=orient, orient_ref=orient_ref, roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    rcube(size=size+extra_size, round_r=round_r);
}

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

module pyramid(size=[10,10,10], orient, align)
{
    let(s=size)
    size_align(size=s, align=align, orient=orient)
    polyhedron(
        points=[
            // the four points at base
            [s.x/2,s.y/2,-s.z/2],[s.x/2,-s.y/2,-s.z/2],[-s.x/2,-s.y/2,-s.z/2],[-s.x/2,s.y/2,-s.z/2],
            // the apex point
            [0,0,s.z/2]
        ],
        faces=[
            // each triangle side
            [0,1,4],[1,2,4],[2,3,4],[3,0,4],
            // two triangles for square base
            [1,0,3],[2,1,3]
        ]
        );
}

module spherea(r, d, align=N, extra_r, extra_d, extra_align, orient=Z, orient_ref=Z, roll=0, extra_roll, extra_roll_orient)
{
    d_ = v_fallback(d, [r*2]);
    assert(d_ != U);

    extra_d_ = v_fallback(extra_d, [extra_r*2, 0]);

    size=[d_,d_,d_];
    extra_size=[extra_d_,extra_d_,extra_d_];
    size_align(size=size, extra_size=extra_size, align=align, extra_align=extra_align, orient=orient, orient_ref=orient_ref, roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    sphere(d=d_+extra_d_);
}

module cylindera(
        h=10,
        r=U,
        r1=U,
        r2=U,
        d=U,
        d1=U,
        d2=U,
        align=N,
        orient=Z,
        extra_h=0,
        extra_r=U,
        extra_d=U,
        extra_align=N,
        round_r=0,
        debug=false
        )
{
    assert(h>0);

    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, [extra_d/2, 0]);

    if(debug)
    {
        echo(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=Z, align=align, extra_align=extra_align)
    {
        if(round_r>0)
        {
            rcylindera(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, round_r=round_r);
        }
        else
        {
            cylinder(h=h+extra_h, r1=r1_+extra_r_, r2=r2_+extra_r_, center=true);
        }
    }
}

// polycylinder (polyhole/3d printing size compensated hole)
module pcylindera(
        h=10,
        r=U,
        d=U,
        align=N,
        orient=Z,
        extra_h=0,
        extra_r=U,
        extra_d=U,
        extra_align=N,
        round_r=0,
        debug=false
        )
{
    pi=3.1415926536;

    d_ = v_fallback(d, [r*2]);
    r__ = v_fallback(r, [d_/2, d/2, r]);
    r_ = polyhole_d(r__*2)/2;
    $fn = polyhole_n(r__*2);

    extra_r_ = v_fallback(extra_r, [extra_d/2, 0]);

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

    size_align(size=[r_*2,r_*2,h], extra_size=[extra_r_*2, extra_r_*2, extra_h], orient=orient, orient_ref=Z, align=align, extra_align=extra_align)
    {
        if(round_r>0)
        {
            rcylindera(h=h+extra_h, r=r_+extra_r_, round_r=round_r);
        }
        else
        {
            cylinder(h=h+extra_h, r=r_+extra_r_, center=true);
        }
    }
}

module torus(d, r, radius, radial_width, align=N, orient=Z)
{
    r_ = v_fallback(r,[d/2, radius, 5]);
    size_align(size=[r_*2+radial_width*2, r_*2+radial_width*2, radial_width*2], align=align, orient=orient)
    rotate_extrude()
    translate(r_*X)
    circle(radial_width);
}

module rcylindera(
        h=10,
        r=U,
        r1=U,
        r2=U,
        d=U,
        d1=U,
        d2=U,
        align=N,
        orient=Z,
        extra_h=0,
        extra_r=U,
        extra_d=U,
        extra_align=N,
        round_r=1,
        debug=false,
        poly=false
        )
{
    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_)]);

    h_ = h+extra_h;

    extra_r_ = v_fallback(extra_r, [extra_d/2, 0]);

    assert(r1_ != U);
    assert(r2_ != U);
    assert(extra_r_ != U);

    assert(h >= 2*round_r);

    if(debug)
    {
        echo(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=Z, align=align, extra_align=extra_align)
    {
        if($preview_mode || round_r == 0)
        {
            cylindera(h=h_, r1=r1_+extra_r_, r2=r2_+extra_r_);
        }
        else
        {
            r_ = [r1_+extra_r_/2,r2_+extra_r_/2];

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

function circle_profile(r=10, fn=$fn) = [for (i=[0:fn-1]) [r*sin(i*360/fn), r*cos(i*360/fn)]];

function rounded_rectangle_profile(size=[10,10],r=[1,1],fn=32) = [
let(r_ = is_array(r) ? r : [r,r])
for (index = [0:fn-1])
let(a = index/fn*360)
[cos(a)*r.x, sin(a)*r.y]
+ sign_x(index, fn) * X * (size.x/2-r.x)
+ sign_y(index, fn) * Y * (size.y/2-r.y)
];

function sign_x(i,n) =
i < n/4 || i > n-n/4  ?  1 :
i > n/4 && i < n-n/4  ? -1 :
    0;

function sign_y(i,n) =
i > 0 && i < n/2  ?  1 :
i > n/2 ? -1 :
    0;

// From Obiscad
// Draw a point in the position given by the vector p
module point(p, r=0.7, fn)
{
    translate(p)
    sphere(r=r);
}

// Draw a vector poiting to the z axis
// This is an auxiliary module for implementing the vector module
//
// Parameters:
//  l: total vector length (line + arrow)
//  l_arrow: Vector arrow length
//  mark: If true, a mark is draw in the vector head, for having
// a visual reference of the rolling angle
module vectorz(l=10, l_arrow=4, mark=true)
{
    // vector body length (not including the arrow)
    lb = l - l_arrow;

    // The vector is located at 0,0,0
    union()
    {
        // Draw the arrow
        tz(lb)
        cylindera(r1=2/2, r2=0.2, h=l_arrow);

        // Draw the mark
        if(mark)
        tz(lb)
        tx(X)
        cubea([2,0.3,l_arrow*0.8], align=X);

        // Draw the body
        cylindera(r=1/2, h=lb, align=Z);
    }

    // Draw a sphere in the vector base
    spherea(r=1/2, align=-Z);
}

// From Obiscad,
// Draw a vector
//
// There are two modes of drawing the vector
// * Mode 1: Given by a cartesian point(x,y,z). A vector from the origin
//           to the end (x,y,z) is drawn. The l parameter (length) must
//           be 0  (l=0)
// * Mode 2: Give by direction and length
//           A vector of length l pointing to the direction given by
//           v is drawn
// Parameters:
//  v: Vector cartesian coordinates
//  l: total vector length (line + arrow)
//  l_arrow: Vector arrow length
//  mark: If true, a mark is draw in the vector head, for having
//    a visual reference of the rolling angle
module vector(v, l=4, l_arrow=2, mark=false)
{
  //-- Get the vector length from the coordinates
  mod = v_mod(v);

  //-- The vector is very easy implemented by means of the orientate
  //-- operator:
  //--  orientate(v) vectorz(l=mod, l_arrow=l_arrow)
  //--  BUT... in OPENSCAD 2012.02.22 the recursion does not
  //--    not work, so that if the user use the orientate operator
  //--    on a vector, openscad will ignore it..
  //-- The solution at the moment (I hope the openscad developers
  //--  implement the recursion in the near future...)
  //--  is to repite the orientate operation in this module

  //---- SAME CALCULATIONS THAN THE ORIENTATE OPERATOR!
  //-- Calculate the rotation axis

  vref = Z;
  raxis = v_cross(vref,v);

  //-- Calculate the angle between the vectors
  ang = v_anglev(vref,v);

  raxis_=ang==180&&raxis==N?X:raxis;

  //-- orientate the vector
  //-- Draw the vector. The vector length is given either
  //--- by the mod variable (when l=0) or by l (when l!=0)
  if (l==0)
  {
      rotate(a=ang, v=raxis_)
      vectorz(l=mod, l_arrow=l_arrow, mark=mark);
  }
  else
  {
      rotate(a=ang, v=raxis_)
      vectorz(l=l, l_arrow=l_arrow, mark=mark);
  }

}

// From Obiscad,
//--------------------------------------------------------------------
//-- Draw a connector
//-- A connector is defined a 3-tuple that consist of a point
//--- (the attachment point), and axis (the attachment axis) and
//--- an angle the connected part should be rotate around the
//--  attachment axis
//--
//--- Input parameters:
//--
//--  Connector c = [p , n, ang] where:
//--
//--     p : The attachment point
//--     v : The attachment axis
//--   ang : the angle
//--------------------------------------------------------------------
module connector(c)
{
    //-- Get the three components from the connector
    p = c[0];
    v = c[1];
    ang = c[2];

    //-- Draw the attachment point
    color(v)
    point(p);

    //-- Draw the attachment axis vector (with a mark)
    translate(p)
    rotate(a=ang, v=v)
    color(v)
    vector($fn=16, v=v_unitv(v), l=6, l_arrow=2, mark=true);
}

// 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=Z, align=N)
{
    size_align(size=[r*2, r*2, h], orient=orient, orient_ref=Z, 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=U, orient=Z, align=N)
{
    outer_d = d+thickness/2;
    inner_d = d-thickness/2;
    taper_h = taper_h == U ? min(h/4, (outer_d-inner_d)/2) : max(taper_h,h/2);
    taper_ = taper && taper_h > 0;
    size_align(size=[outer_d, outer_d, h], orient=orient, align=align);
    difference()
    {
        union()
        {
            cylindera(h=h-(taper_?taper_h*2:0), d=outer_d, orient=Z, align=N);
            if(taper_)
            {
                for(z=[-1,1])
                translate([0,0,z*(h/2-taper_h)])
                mirror([0,0,z==-1?1:0])
                cylindera(d1=outer_d, d2=outer_d-inner_d/4, h=taper_h, align=Z);
            }
        }
        if(taper_)
        {
            for(z=[-1,1])
            translate([0,0,z*(h/2-taper_h)])
            mirror([0,0,z==-1?1:0])
            cylindera(d1=inner_d, d2=outer_d, h=taper_h+.1, align=Z, extra_h=.2);

            // override fn for inner cylinder cut, to ensure same fragments as taper
            // this ensures cleaner mesh
            fn = fn_from_d(d=outer_d);
            cylindera(h=h+.2+taper_h, d=inner_d, orient=Z, align=N, $fn=fn);
        }
        else
        {
            cylindera(h=h, d=inner_d, orient=Z, align=N, extra_h=.4);
        }
    }
}

/**
 * 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=N, orient=Z)
{
    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, round_r=2, align=N, orient=Z)
{
    size = [a_len-round_r, depth-round_r, o_len-round_r];
    r_x = round_r;
    r_y = round_r;
    r_z = -round_r;

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

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

    /*translate([size[0]+4,0,0])*/
    /*color(X)*/
    /*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)+round_r,(size[0]/2)-r_x])
    for(y=[-(size[1]/2)+round_r,(size[1]/2)-r_y])
    for(z=[-(size[2]/2)+round_r,(size[2]/2)-r_z])
    translate([x,y,z])
    if(x<=1 || z<=1)
    sphere(r=round_r);
}

module teardrop(r, d, h=10, truncate=1, align=N, orient=Y, 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=Z, roll=roll)
    union()
    {
        linear_extrude(height = h, center = true, convexity = r_, twist = 0)
        circle(r = r_);

        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;
    all_axes()
    translate($axis*r*2)
    color($color)
    teardrop(r=r, h=20, orient=$axis, align=$axis);
}

// test cubea
if(false)
{
    w=5*mm;
    h=2*mm;
    all_axes()
    color($color)
    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=-Z);*/
    /*triangle(15, 10, 5, align=[1,1,1], orient=Z);*/

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

/**
 * 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)
{
    stack(axis=X, dist=15)
    {
        hollow_cylinder(thickness=5, h=10, taper=false, orient=Z, align=Z);
        hollow_cylinder(thickness=5, h=10, taper=true, orient=Z, align=Z);
    }

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

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

    /*cubea([10,10,10],X);*/
    /*%cubea([10,10,10],X,[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=X, extra_d=10);*/

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

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

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

    cylindera(h=20, r=5*mm, align=Y, orient=Z, $fn=100);

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

/*cylindera(d=10, h=10, orient=Y, align=Z);*/
}

if(false)
{
    /*cubea();*/
    /*rcubea(size=[10,10,10]);*/

    is_build = true;
    $fs = is_build ? 0.5 : 2;
    $fa = is_build ? 4 : 12;

    stack(axis=X, dist=15)
    {
        cylindera(h=10,r=5);

        rcylindera(h=10,r=5);

        rcubea([10,10,10]);
    }
}

if(false)
{
    r=5*mm;
    all_axes()
    translate($axis*r*2)
    color($color)
    /*orient(axis=$axis, axis_ref=Z)*/
    /*translate(Z*10)*/
    /*rcylindera(r1=5, r2=3, h=10);*/
    {
        rcylindera(h=20, extra_d = 1*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16); /*cylindera(h=20, round_r=1, extra_r = 2*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16);*/
        cylindera(h=20, extra_d = 1*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16); /*cylindera(h=20, round_r=1, extra_r = 2*mm, r1=r, r2=r/2, align=$axis, orient=$axis, extra_h=r, extra_align=-$axis, $fn=16);*/
    }

}

if(false)
{
    all_axes()
    translate($axis*r*2)
    color($color)
    {
        orient(axis=$axis, axis_ref=Z)
        hull()
        {
            cubea(size=[5,8,7]);
            translate(5*Z)
            cubea(size=[2,2,2]);
        }
    }
}

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

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

if(false)
{
    /*$fs = 0.5;*/
    /*$fa = 4;*/

    r=5;
    hull()
    {
        stack(dist=10, axis=-Z)
        {
            rcylindera(r=r, h=r*2, orient=Y, align=Y);
            rcylindera(r=r, h=r*2, orient=Y, align=Y);
        }

        rcylindera(r=5, h=r, orient=X, align=Y+X+Z);
    }
}


fixed basin support depth

fixed basin support depth

use <projector_plate.scad>;
include <projector.scad>;
use <plate.scad>;
include <profile.scad>;
use <../scad/ob_wheel.scad>
use <../scad/ob_spacer.scad>
include <gantry_cart.scad>
use <../scad/brackets.scad>
use <../scad/geom.scad>
use <support_projector_beam.scad>
include <materials.scad>
use <basin_support.scad>
use <big_screw.scad>

module printer(projector_position = 0) {
  offset_depth = 50;

  middle_sections = 2;

  height = 500;
  width = 560;
  base_depth = 440;
  display_projection = true;
  base_sections = 3;

  projector_movement = height / 2 - acer_projector_distances[projector_position] + 20;

  // projector
  translate([0, 0, -offset_depth]) {
    translate([0, projector_movement, 0]) {
      center_projector() {
        Acer_Plate(160, 120, 85, 100);
        Acer_H6510BD(display_projection);
      }
    }
  }
  beam_offset = 10;
  beam_width = width - 2 * beam_offset;

  carriage_tower_sections = 3;

  translate([0, projector_movement, -offset_depth]) {
    translate([-beam_width/2,-20 * base_sections,-10])
      rotate([90,90,90])
        profile(base_sections, beam_width);
  /*
    translate([beam_width/2,-60,-10])
      rotate([90,0,90])
        gantry_cart_big(carriage_tower_sections, offset = 1);
    translate([-beam_width/2,-60,-10])
      rotate([90,0,-90])
        gantry_cart_big(carriage_tower_sections, offset = 1);
  */
  }

  // projector towers
  translate([0, 0, -offset_depth]) {
    translate([-width/2,-height/2,-10])
      rotate([-90,0,0])
        profile(carriage_tower_sections, height);
    translate([width/2,-height/2,-10])
      rotate([-90,0,0])
        profile(carriage_tower_sections, height);
  }

  // supports for horizontal beam
  translate([0, 20, -offset_depth])
  for(d = acer_projector_distances) {
    translate([0,d-height/2+10,0])
      support_projector_beam(width-60);
  }
  //height / 2 - d

  // base
  module base(width, depth, sections = 2) {
    translate([-width/2,0,-base_depth/2])
      rotate([0,0,0])
        profile(sections, base_depth);

    translate([width/2,0,-base_depth/2])
      rotate([0,0,0])
        profile(sections, base_depth);

    lateral_beam_width = width - 20;
    translate([-lateral_beam_width/2,0,-base_depth/2+10])
      rotate([0,90,0])
        profile(sections, lateral_beam_width);

    translate([-lateral_beam_width/2,0,base_depth/2-10])
      rotate([0,90,0])
        profile(sections, lateral_beam_width);
  }

  module baser(width, depth, sections = 2) {
    translate([(- width + sections * 20) / 2 - 10, -10, -base_depth/2])
      rotate([0,0,90])
        profile(sections, base_depth);

    translate([(  width - sections * 20) / 2 + 10, -10, -base_depth/2])
      rotate([0,0,90])
        profile(sections, base_depth);

    lateral_beam_width = width - 30 * sections;
    translate([-lateral_beam_width/2, -10, -base_depth/2+20])
      rotate([90,0,90])
        profile(sections, lateral_beam_width);
    translate([-lateral_beam_width/2, -10, base_depth/2-20])
      rotate([90,0,90])
        profile(sections, lateral_beam_width);
  }

  bottom_sections = 2;
  translate([0,-(height+bottom_sections*20)/2,0])
    base(width, base_depth, bottom_sections);

  translate([0,(height+middle_sections*20)/2,0])
    baser(width, base_depth, 2);

  // support towers
  tower_pos = base_depth / 2 - 10;
  translate([-width/2,-height/2,tower_pos])
    rotate([-90,0,0])
      profile(1, height);
  translate([width/2,-height/2,tower_pos])
    rotate([-90,0,0])
      profile(1, height);

  rotate([0,180,0]) {

    offset = 16;
    plate_width = width - 20 - offset * 2;
    plate_depth = base_depth - 40 * 2;
    cut_width = 440;
    cut_offset = 7.5;
    translate([
      -plate_width/2,
      height/2 + 20,
      -plate_depth / 2 + 20 - offset])
      basin_support(plate_width, plate_depth, cut_width, cut_offset);

    translate([0, height/2 + 20 * middle_sections, base_depth / 2 + 20])
      big_screw();

    // TODO review
    screw_height = 640;
    rail_sections = 2;
    rotate([-90, 0, 0]) {
      translate([65, -base_depth / 2 - 20, -height / 2 - 20 * (bottom_sections)]) {
        profile(rail_sections, screw_height + height + 20 * (bottom_sections + middle_sections));
      }
      translate([-65, -base_depth / 2 - 20, -height / 2 - 20 * (bottom_sections)]) {
        profile(rail_sections, screw_height + height + 20 * (bottom_sections + middle_sections));
      }
    }
    translate([0,0,30]) {
      translate([40, height / 2 + 20 * middle_sections + 260, base_depth / 2 + 20])
        rotate([0, 180, 0])
          profile(4, width / 2 + rail_sections * 2 - 5);
      translate([-40, height / 2 + 20 * middle_sections + 260, base_depth / 2 + 20])
        rotate([0, 180, 0])
          profile(4, width / 2 + rail_sections * 2 - 5);
      translate([-50, height / 2 + 20 * middle_sections + 300, base_depth / 2 -60])
        plate(100, 4, 80);
      translate([-50, height / 2 + 20 * middle_sections + 216, base_depth / 2 -60])
        plate(100, 4, 80);
      translate([0, height / 2 + 20 * middle_sections + 300, base_depth / 2 - width / 2 + 30])
        rotate([90,90,0])
          profile(3, 120);

      translate([65, height / 2 + 20 * middle_sections + 300 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([65, height / 2 + 20 * middle_sections + 240 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([65, height / 2 + 20 * middle_sections + 270 - 10, base_depth / 2 + rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();

      translate([-65, height / 2 + 20 * middle_sections + 300 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([-65, height / 2 + 20 * middle_sections + 240 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([-65, height / 2 + 20 * middle_sections + 270 - 10, base_depth / 2 + rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
    }
  }
}

use <projector_plate.scad>;
include <projector.scad>;
use <plate.scad>;
include <profile.scad>;
use <../scad/ob_wheel.scad>
use <../scad/ob_spacer.scad>
include <gantry_cart.scad>
use <../scad/brackets.scad>
use <../scad/geom.scad>
use <support_projector_beam.scad>
include <materials.scad>
use <basin_support.scad>
use <big_screw.scad>

module printer(projector_position = 0) {
  offset_depth = 50;

  middle_sections = 2;

  height = 500;
  width = 560;
  base_depth = 440;
  display_projection = true;
  base_sections = 3;

  projector_movement = height / 2 - acer_projector_distances[projector_position] + 20;

  // projector
  translate([0, 0, -offset_depth]) {
    translate([0, projector_movement, 0]) {
      center_projector() {
        Acer_Plate(160, 120, 85, 100);
        Acer_H6510BD(display_projection);
      }
    }
  }
  beam_offset = 10;
  beam_width = width - 2 * beam_offset;

  carriage_tower_sections = 3;

  translate([0, projector_movement, -offset_depth]) {
    translate([-beam_width/2,-20 * base_sections,-10])
      rotate([90,90,90])
        profile(base_sections, beam_width);
  /*
    translate([beam_width/2,-60,-10])
      rotate([90,0,90])
        gantry_cart_big(carriage_tower_sections, offset = 1);
    translate([-beam_width/2,-60,-10])
      rotate([90,0,-90])
        gantry_cart_big(carriage_tower_sections, offset = 1);
  */
  }

  // projector towers
  translate([0, 0, -offset_depth]) {
    translate([-width/2,-height/2,-10])
      rotate([-90,0,0])
        profile(carriage_tower_sections, height);
    translate([width/2,-height/2,-10])
      rotate([-90,0,0])
        profile(carriage_tower_sections, height);
  }

  // supports for horizontal beam
  translate([0, 20, -offset_depth])
  for(d = acer_projector_distances) {
    translate([0,d-height/2+10,0])
      support_projector_beam(width-60);
  }
  //height / 2 - d

  // base
  module base(width, depth, sections = 2) {
    translate([-width/2,0,-base_depth/2])
      rotate([0,0,0])
        profile(sections, base_depth);

    translate([width/2,0,-base_depth/2])
      rotate([0,0,0])
        profile(sections, base_depth);

    lateral_beam_width = width - 20;
    translate([-lateral_beam_width/2,0,-base_depth/2+10])
      rotate([0,90,0])
        profile(sections, lateral_beam_width);

    translate([-lateral_beam_width/2,0,base_depth/2-10])
      rotate([0,90,0])
        profile(sections, lateral_beam_width);
  }

  module baser(width, depth, sections = 2) {
    translate([(- width + sections * 20) / 2 - 10, -10, -base_depth/2])
      rotate([0,0,90])
        profile(sections, base_depth);

    translate([(  width - sections * 20) / 2 + 10, -10, -base_depth/2])
      rotate([0,0,90])
        profile(sections, base_depth);

    lateral_beam_width = width - 30 * sections;
    translate([-lateral_beam_width/2, -10, -base_depth/2+20])
      rotate([90,0,90])
        profile(sections, lateral_beam_width);
    translate([-lateral_beam_width/2, -10, base_depth/2-20])
      rotate([90,0,90])
        profile(sections, lateral_beam_width);
  }

  bottom_sections = 2;
  translate([0,-(height+bottom_sections*20)/2,0])
    base(width, base_depth, bottom_sections);

  translate([0,(height+middle_sections*20)/2,0])
    baser(width, base_depth, 2);

  // support towers
  tower_pos = base_depth / 2 - 10;
  translate([-width/2,-height/2,tower_pos])
    rotate([-90,0,0])
      profile(1, height);
  translate([width/2,-height/2,tower_pos])
    rotate([-90,0,0])
      profile(1, height);

  rotate([0,180,0]) {

    offset = 16;
    plate_width = width - 20 - offset * 2;
    plate_depth = base_depth - 60 * 2;
    cut_width = 440;
    cut_offset = 7.5;
    translate([
      -plate_width/2,
      height/2 + 20,
      -plate_depth / 2 + 20 - offset])
      basin_support(plate_width, plate_depth, cut_width, cut_offset);

    translate([0, height/2 + 20 * middle_sections, base_depth / 2 + 20])
      big_screw();

    // TODO review
    screw_height = 640;
    rail_sections = 2;
    rotate([-90, 0, 0]) {
      translate([65, -base_depth / 2 - 20, -height / 2 - 20 * (bottom_sections)]) {
        profile(rail_sections, screw_height + height + 20 * (bottom_sections + middle_sections));
      }
      translate([-65, -base_depth / 2 - 20, -height / 2 - 20 * (bottom_sections)]) {
        profile(rail_sections, screw_height + height + 20 * (bottom_sections + middle_sections));
      }
    }
    translate([0,0,30]) {
      translate([40, height / 2 + 20 * middle_sections + 260, base_depth / 2 + 20])
        rotate([0, 180, 0])
          profile(4, width / 2 + rail_sections * 2 - 5);
      translate([-40, height / 2 + 20 * middle_sections + 260, base_depth / 2 + 20])
        rotate([0, 180, 0])
          profile(4, width / 2 + rail_sections * 2 - 5);
      translate([-50, height / 2 + 20 * middle_sections + 300, base_depth / 2 -60])
        plate(100, 4, 80);
      translate([-50, height / 2 + 20 * middle_sections + 216, base_depth / 2 -60])
        plate(100, 4, 80);
      translate([0, height / 2 + 20 * middle_sections + 300, base_depth / 2 - width / 2 + 30])
        rotate([90,90,0])
          profile(3, 120);

      translate([65, height / 2 + 20 * middle_sections + 300 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([65, height / 2 + 20 * middle_sections + 240 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([65, height / 2 + 20 * middle_sections + 270 - 10, base_depth / 2 + rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();

      translate([-65, height / 2 + 20 * middle_sections + 300 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([-65, height / 2 + 20 * middle_sections + 240 - 10, base_depth / 2 - 20 - rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
      translate([-65, height / 2 + 20 * middle_sections + 270 - 10, base_depth / 2 + rail_sections * 10])
        rotate([0,90,0])
          ob_solid_wheel();
    }
  }
}


Add a simple skeleton to quickly start new samples

/**
 * Samples using the camelSCAD library.
 *
 * A base skeleton
 *
 * @author jsconan
 * @license CC0-1.0
 */

// As we need to use some shapes, use the right entry point of the library
use <../shapes.scad>
include <../core/constants.scad>

// We will render the object using the specifications of this mode
renderMode = MODE_PROD;

// Defines the constraints of the print
printResolution = 0.2;

// Defines the constraints of the object

// Defines the dimensions of the object

// Sets the minimum facet angle and size using the defined render mode.
// Displays a build box visualization to preview the printer area.
buildBox(mode=renderMode) {

}

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


fixes after 2nd test print of hmount()

fixes after 2nd test print of hmount()

//$fs = 0.1;
//$fa = 5;
$fs = 0.25;
$fa = 8;

use <hcsr04.scad>

module wheel() {
    color("black") rotate([90, 90, 0]) difference() {
        #cylinder(d=47, h=23, center=true);
        cylinder(d=30, h=26, center=true);
    }
}

module base() {

    // 4 wheels
    translate([77.5, 60, 47/2]) wheel();
    translate([-77.5, 60, 47/2]) wheel();
    translate([77.5, -60, 47/2]) wheel();
    translate([-77.5, -60, 47/2]) wheel();

    // front axe assemby - slightly longer
    translate([77.5-5, 0, 47/2]) color("gray") cube(size=[25, 103, 26], center=true);
    // rear axe assemby
    translate([-77.5, 0, 47/2]) color("gray") cube(size=[26, 97, 26], center=true);

    color("gray") translate ([0, 0, 12]) {

        // base plate
        linear_extrude(height = 2) base_plate();
        linear_extrude(height = 7) difference() {
            base_plate();
            offset(-2) base_plate();
        }

        // shaft
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        //shaft ribs
        translate ([14, 4, 23]) cube(size=[67, 2, 4], center=false);
        translate ([14, -6, 23]) cube(size=[67, 2, 4], center=false);
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        translate ([-81, 4, 23]) cube(size=[45, 2, 6], center=false);
        translate ([-81, -6, 23]) cube(size=[45, 2, 6], center=false);
        // servo
        linear_extrude(height = 15)  polygon([
            [29,0], [79,0],
            [79,-23], [70,-23],[49,-40],[29,-40]
        ]);

        // motor
        translate ([-79+30, -16, 23/2+2]) rotate([0, 90, 0]) cylinder(d=23, h=32, center=false);

        // gear
        translate ([-79+23, 0, 34/2]) rotate([0, 90, 0]) cylinder(d=34, h=6, center=false);

        // battery
        translate ([-5/2, 30/2+7, 16/2+2]) cube(size=[84, 30, 16], center=true);

        // battery holder
        translate ([-79+23, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([79-29, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([-5/2, 20, 22]) cube(size=[110, 13, 2], center=true);

        // front shocks
        translate ([162/2, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[23,44],[18,48],[5,41],[-5,41],[-18,48],[-23,44], [-38,0],[38,0]]);

        // rear shocks
        translate ([-162/2-16, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[25,48],[20,48],[5,38],[-5,38],[-20,48],[-25,48], [-38,0],[38,0]]);
    }
}

module base_plate() {
    polygon([
        [-79,25], [-70,25],[-54,40],[49,40],[70,23], [79,23],
        [79,-23], [70,-23],[49,-40],[-54,-40],[-70,-25], [-79,-25]
    ]);
}

module platform() {

    difference() {
    color("black") translate ([0, 0, 12+32+4]) difference() {
        union() {
            #linear_extrude(height = 3) intersection() {
                difference() {
                    offset(-10) offset(30) offset(-20)
                       polygon([
                         [-200,25], [-70,25],[-44,60],[44,60],[70,23], [200,23],
                         [200,-23], [70,-23],[44,-60],[-44,-60],[-70,-25], [-200,-25]
                       ]);

                    offset(5) offset(-5) translate ([-2.5, 0, 0]) difference() {
                        polygon([
                         [-50,-35], [50,-35],
                           [50,-30], [60,-15],[60,15], [50,30],
                         [50,35], [-50,35],
                           [-50,30], [-55,20],[-55,-20], [-50,-30]
                        ])

                        square(size=[90,70], center=true);
                        translate ([-45, -35, 0]) circle(r=7.5, center=true);
                        translate ([ 45, -35, 0]) circle(r=7.5, center=true);
                        translate ([-45,  35, 0]) circle(r=7.5, center=true);
                        translate ([ 45,  35, 0]) circle(r=7.5, center=true);
                    }

                }
                square(size=[162,120], center=true);
            }

            intersection() { translate ([0, 0, 100]) cube(size=[200,200,200], center=true); union() {
            // shock tower mount points
            // rear
            #translate ([-81, -25, 0]) { hull() {
                cube(size=[4, 7, 12-3.5], center=false);
                translate ([4/2, 3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            translate ([8/2, 3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }
            #translate ([-81, 25-7, 0]) { hull() {
                cube(size=[4, 7, 12-3.5], center=false);
                translate ([4/2, 7-3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            translate ([8/2, 7-3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }
            // front
            #translate ([81-4, -46/2, 0]) hull() {
                cube(size=[4, 7, 8-3.5], center=false);
                translate ([4/2, 3.5, 8-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            #translate ([81-4, 46/2-7, 0]) hull() {
                cube(size=[4, 7, 8-3.5], center=false);
                translate ([4/2, 7-3.5, 8-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            }}
        }

        // board mount holes
        translate ([-(45-2.5)-2.5,  (35-2.5), 2.5]) screw2x6bore(); // cylinder(d=2.2, h=10, center=true);
        translate ([-(45-2.5)-2.5, -(35-2.5), 2.5]) screw2x6bore(); //cylinder(d=2.2, h=10, center=true);
        translate ([ (45-2.5)-2.5,  (35-2.5), 2.5]) screw2x6bore(); //cylinder(d=2.2, h=10, center=true);
        translate ([ (45-2.5)-2.5, -(35-2.5), 2.5]) screw2x6bore(); //cylinder(d=2.2, h=10, center=true);

        // servo wire hole
        //translate ([55, -20, 0]) cylinder(d=10, h=10, center=true);
        //translate ([55,  20, 0]) cylinder(d=10, h=10, center=true);

        // mount holes
        // front
        #translate ([81, 42.5/2-3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81,-42.5/2+3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81, 42.5/2-3/2, 8-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
        #translate ([81,-42.5/2+3/2, 8-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);

        #translate ([81-4-5/2, 42.5/2-3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=6.1, h=5, center=true);
        #translate ([81-4-5/2,-42.5/2+3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=6.1, h=5, center=true);

        // rear
        #translate ([-81, 46.5/2-3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81,-46.5/2+3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81, 46.5/2-3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
        #translate ([-81,-46.5/2+3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
    }
    arrangebottom(30) hbaseholes(center=true);
    //arrangebottom(45) hbaseholes();
    }
}

module board() {

    // base
    color("yellow") translate ([-2.5, 0, 47]) cube(size=[90, 70, 1.2], center=true);

    // teensy
    color("green") translate ([-25, 0, 47+5]) cube(size=[20, 63, 10], center=true);

    // MPU-9250
    color("red") translate ([0, 15, 47+5]) cube(size=[20, 20, 10], center=true);
}

module esc() {
    color("green") cube(size=[36, 26, 2], center=true);
    color("black") translate ([36/2, 0, 0]) rotate([0, 90, 0]) cylinder(d=8, h=16, center=false);

    // esc support box ?
    // hcolor("black") translate ([0, 0, -11]) cube(size=[30, 20, 20], center=true);
}

module vsonar() {

    // vertical sonar
    translate ([0, -10, 45]) rotate([0, 90, 0]) hcsr04();

    // mount plate
    color("gray") translate ([1.5, -10, -6]) cube(size=[3, 20, 51], center=false);
    color("gray") translate ([-8.5, -10, -6]) cube(size=[10, 20, 5], center=false);

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 22.5]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module hbaseholes(center = false)
{
    translate ([-5,    0, -15]) cylinder(d=3.2, h=15, center=false);
    translate ([-5,  9.5, -15]) cylinder(d=3.2, h=15, center=false);
    translate ([-5, -9.5, -15]) cylinder(d=3.2, h=15, center=false);
    if (!center) {
        translate ([-5,  19, -15]) cylinder(d=3.2, h=15, center=false);
        translate ([-5, -19, -15]) cylinder(d=3.2, h=15, center=false);
    }
}

module hmount() {
    // mount plate
    color ("gray") {

        // front
        difference() {
            translate ([1.5, -48/2, -6]) #hull() {
                cube(size=[3, 48, 25], center=false);
                translate ([0, 48-3, 25]) rotate([90, 0, 90]) cylinder(r=3, h=3, center=false);
                translate ([0, 3, 25]) rotate([90, 0, 90]) cylinder(r=3, h=3, center=false);
            }

            translate ([0, -45/2, 0]) rotate([90, 0, 90]) {

                // sonar
                translate([45/2-(42-16)/2,18/2+1,1.3]) cylinder(d=17, h=15);
                translate([45/2+(42-16)/2,18/2+1,1.3]) cylinder(d=17, h=15);

                //xtal(s)
                hull () {
                    translate([45/2-3.25,20-3.5,1.3])
                    {
                        translate([0,0,0]) cylinder(d=6, h=5);
                        translate([6.5,0,0]) cylinder(d=6, h=5);
                    }
                    translate([45/2-3.25, 4,1.3]) hull()
                    {
                        //translate([0,0,0]) cylinder(d=6, h=5);
                        //translate([6.5,0,0]) cylinder(d=6, h=5);
                        translate([0,-2,0]) cylinder(d=6, h=5);
                        translate([6.5,-2,0]) cylinder(d=6, h=5);
                    }
                }

                // screws
                translate([   1.0,    1.0, 4.5-0.5]) screw2x6bore();
                translate([45-1.0, 20-1.5, 4.5-0.5]) screw2x6bore();
                translate([45-1.0,    1.0, 4.5-0.5]) screw2x6bore();
                translate([   1.0, 20-1.5, 4.5-0.5]) screw2x6bore();
            }

            translate ([3, 0, 10])
                cube(size=[5, 20, 11], center=true);
        }

        //base
        difference() {
            hull() {
                translate ([-3.5, -48/2, -6]) cube(size=[5, 48, 5], center=false);
                translate ([-3.5, -48/2+5, -6]) cylinder(r=5, h=5, center=false);
                translate ([-3.5, 48/2-5, -6]) cylinder(r=5, h=5, center=false);
            }
            hbaseholes();
        }
    }
}

module screw2x6bore() {
    rotate([180, 0, 0]) {
        cylinder(d=2.2, h=6);
        translate ([0, 0, -1.3])
            cylinder(d1=2*3.6, d2=0, h=2*1.3);
    }
}

module hsonar() {

    // horizontal sonar
    translate ([0, -45/2, 0]) rotate([90, 0, 90]) hcsr04();

    hmount();

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 10]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module car()
{
    base();
    platform();
    board();

    translate ([10, -23, 35]) esc();
//    translate ([-10, 0, 40]) rotate([0, 0, 180]) esc();
}

// full model - vertical sonars
module fullv() {
    car();

    translate ([68, 0, 55]) vsonar();
    translate ([60, 25, 55]) rotate([0, 0, 30]) vsonar();
    translate ([60, -25, 55]) rotate([0, 0, -30]) vsonar();
    translate ([35, 35, 55]) rotate([0, 0, 60]) vsonar();
    translate ([35, -35, 55]) rotate([0, 0, -60]) vsonar();
}

// full model - horizontal sonars
module fullh() {
    car();

    translate ([68, 0, 55]) hsonar();
    translate ([40, 35, 55]) rotate([0, 0, 30]) hsonar();
    translate ([40, -35, 55]) rotate([0, 0, -30]) hsonar();
    translate ([-10, 35, 55]) rotate([0, 0, 60]) hsonar();
    translate ([-10, -35, 55]) rotate([0, 0, -60]) hsonar();
}

module arrangebottom(a) {
    translate ([68, 0, 57]) children();
    translate ([45, 45, 57]) rotate([0, 0, a]) children();
    translate ([45, -45, 57]) rotate([0, 0, -a]) children();

    translate ([0, 52.5, 42]) rotate([180, 0, 2*a]) children();
    translate ([0, -52.5, 42]) rotate([180, 0, -2*a]) children();
}

module fullb() {
    car();
    arrangebottom(30) hsonar();
}

//car();

//platform();

/*
intersection() {
platform();
#translate ([80, 0, 0]) cube(size=[20, 200, 400], center=true);
}*/

/*
intersection() {
platform();
#translate ([-80, 0, 0]) cube(size=[20, 200, 400], center=true);
}*/

/*
intersection() {
platform();
#translate ([45-2.5, 30, 0]) cube(size=[20, 20, 400], center=true);
}
*/

//fullb();

translate ([0, 0, 4.5]) rotate([0, 90, 0]) hmount();

//screw2x6bore();

//projection(cut=true) translate ([0, 0, -47]) platform();

//$fs = 0.1;
//$fa = 5;
$fs = 0.25;
$fa = 8;

use <hcsr04.scad>

module wheel() {
    color("black") rotate([90, 90, 0]) difference() {
        #cylinder(d=47, h=23, center=true);
        cylinder(d=30, h=26, center=true);
    }
}

module base() {

    // 4 wheels
    translate([77.5, 60, 47/2]) wheel();
    translate([-77.5, 60, 47/2]) wheel();
    translate([77.5, -60, 47/2]) wheel();
    translate([-77.5, -60, 47/2]) wheel();

    // front axe assemby - slightly longer
    translate([77.5-5, 0, 47/2]) color("gray") cube(size=[25, 103, 26], center=true);
    // rear axe assemby
    translate([-77.5, 0, 47/2]) color("gray") cube(size=[26, 97, 26], center=true);

    color("gray") translate ([0, 0, 12]) {

        // base plate
        linear_extrude(height = 2) base_plate();
        linear_extrude(height = 7) difference() {
            base_plate();
            offset(-2) base_plate();
        }

        // shaft
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        //shaft ribs
        translate ([14, 4, 23]) cube(size=[67, 2, 4], center=false);
        translate ([14, -6, 23]) cube(size=[67, 2, 4], center=false);
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        translate ([-81, 4, 23]) cube(size=[45, 2, 6], center=false);
        translate ([-81, -6, 23]) cube(size=[45, 2, 6], center=false);
        // servo
        linear_extrude(height = 15)  polygon([
            [29,0], [79,0],
            [79,-23], [70,-23],[49,-40],[29,-40]
        ]);

        // motor
        translate ([-79+30, -16, 23/2+2]) rotate([0, 90, 0]) cylinder(d=23, h=32, center=false);

        // gear
        translate ([-79+23, 0, 34/2]) rotate([0, 90, 0]) cylinder(d=34, h=6, center=false);

        // battery
        translate ([-5/2, 30/2+7, 16/2+2]) cube(size=[84, 30, 16], center=true);

        // battery holder
        translate ([-79+23, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([79-29, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([-5/2, 20, 22]) cube(size=[110, 13, 2], center=true);

        // front shocks
        translate ([162/2, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[23,44],[18,48],[5,41],[-5,41],[-18,48],[-23,44], [-38,0],[38,0]]);

        // rear shocks
        translate ([-162/2-16, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[25,48],[20,48],[5,38],[-5,38],[-20,48],[-25,48], [-38,0],[38,0]]);
    }
}

module base_plate() {
    polygon([
        [-79,25], [-70,25],[-54,40],[49,40],[70,23], [79,23],
        [79,-23], [70,-23],[49,-40],[-54,-40],[-70,-25], [-79,-25]
    ]);
}

module platform() {

    difference() {
    color("black") translate ([0, 0, 12+32+4]) difference() {
        union() {
            #linear_extrude(height = 3) intersection() {
                difference() {
                    offset(-10) offset(30) offset(-20)
                       polygon([
                         [-200,25], [-70,25],[-44,60],[44,60],[70,23], [200,23],
                         [200,-23], [70,-23],[44,-60],[-44,-60],[-70,-25], [-200,-25]
                       ]);

                    offset(5) offset(-5) translate ([-2.5, 0, 0]) difference() {
                        polygon([
                         [-50,-35], [50,-35],
                           [50,-30], [60,-15],[60,15], [50,30],
                         [50,35], [-50,35],
                           [-50,30], [-55,20],[-55,-20], [-50,-30]
                        ])

                        square(size=[90,70], center=true);
                        translate ([-45, -35, 0]) circle(r=7.5, center=true);
                        translate ([ 45, -35, 0]) circle(r=7.5, center=true);
                        translate ([-45,  35, 0]) circle(r=7.5, center=true);
                        translate ([ 45,  35, 0]) circle(r=7.5, center=true);
                    }

                }
                square(size=[162,120], center=true);
            }

            intersection() { translate ([0, 0, 100]) cube(size=[200,200,200], center=true); union() {
            // shock tower mount points
            // rear
            #translate ([-81, -25, 0]) { hull() {
                cube(size=[4, 7, 12-3.5], center=false);
                translate ([4/2, 3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            translate ([8/2, 3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }
            #translate ([-81, 25-7, 0]) { hull() {
                cube(size=[4, 7, 12-3.5], center=false);
                translate ([4/2, 7-3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            translate ([8/2, 7-3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }
            // front
            #translate ([81-4, -46/2, 0]) hull() {
                cube(size=[4, 7, 8-3.5], center=false);
                translate ([4/2, 3.5, 8-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            #translate ([81-4, 46/2-7, 0]) hull() {
                cube(size=[4, 7, 8-3.5], center=false);
                translate ([4/2, 7-3.5, 8-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
            }
            }}
        }

        // board mount holes
        translate ([-(45-2.5)-2.5,  (35-2.5), 2.5]) screw2x6bore(); // cylinder(d=2.2, h=10, center=true);
        translate ([-(45-2.5)-2.5, -(35-2.5), 2.5]) screw2x6bore(); //cylinder(d=2.2, h=10, center=true);
        translate ([ (45-2.5)-2.5,  (35-2.5), 2.5]) screw2x6bore(); //cylinder(d=2.2, h=10, center=true);
        translate ([ (45-2.5)-2.5, -(35-2.5), 2.5]) screw2x6bore(); //cylinder(d=2.2, h=10, center=true);

        // servo wire hole
        //translate ([55, -20, 0]) cylinder(d=10, h=10, center=true);
        //translate ([55,  20, 0]) cylinder(d=10, h=10, center=true);

        // mount holes
        // front
        #translate ([81, 42.5/2-3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81,-42.5/2+3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81, 42.5/2-3/2, 8-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
        #translate ([81,-42.5/2+3/2, 8-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);

        #translate ([81-4-5/2, 42.5/2-3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=6.1, h=5, center=true);
        #translate ([81-4-5/2,-42.5/2+3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=6.1, h=5, center=true);

        // rear
        #translate ([-81, 46.5/2-3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81,-46.5/2+3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81, 46.5/2-3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
        #translate ([-81,-46.5/2+3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
    }
    arrangebottom(30) hbaseholes(center=true);
    //arrangebottom(45) hbaseholes();
    }
}

module board() {

    // base
    color("yellow") translate ([-2.5, 0, 47]) cube(size=[90, 70, 1.2], center=true);

    // teensy
    color("green") translate ([-25, 0, 47+5]) cube(size=[20, 63, 10], center=true);

    // MPU-9250
    color("red") translate ([0, 15, 47+5]) cube(size=[20, 20, 10], center=true);
}

module esc() {
    color("green") cube(size=[36, 26, 2], center=true);
    color("black") translate ([36/2, 0, 0]) rotate([0, 90, 0]) cylinder(d=8, h=16, center=false);

    // esc support box ?
    // hcolor("black") translate ([0, 0, -11]) cube(size=[30, 20, 20], center=true);
}

module vsonar() {

    // vertical sonar
    translate ([0, -10, 45]) rotate([0, 90, 0]) hcsr04();

    // mount plate
    color("gray") translate ([1.5, -10, -6]) cube(size=[3, 20, 51], center=false);
    color("gray") translate ([-8.5, -10, -6]) cube(size=[10, 20, 5], center=false);

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 22.5]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module hbaseholes(center = false)
{
    translate ([-5,   0, -15]) cylinder(d=3.2, h=15, center=false);
    if (!center) {
        translate ([-5,  19, -15]) cylinder(d=3.2, h=15, center=false);
        translate ([-5, -19, -15]) cylinder(d=3.2, h=15, center=false);
    }
}

module hmount() {
    // mount plate
    color ("gray") {

        // front
        difference() {
            translate ([1.5, -48/2, -6]) #hull() {
                cube(size=[3, 48, 23], center=false);
                translate ([0, 43, 23]) rotate([90, 0, 90]) cylinder(r=5, h=3, center=false);
                translate ([0, 5, 23]) rotate([90, 0, 90]) cylinder(r=5, h=3, center=false);
            }

            translate ([0, -45/2, 0]) rotate([90, 0, 90]) {

                // sonar
                translate([16/2+1,18/2+1,1.3]) cylinder(d=17, h=15);
                translate([16/2+27+1,18/2+1,1.3]) cylinder(d=17, h=15);

                //xtal(s)
                translate([45/2-3.25,20-4,1.3]) hull()
                {
                    translate([0,0,0]) cylinder(d=6, h=5);
                    translate([6.5,0,0]) cylinder(d=6, h=5);
                }
                translate([45/2-3.25, 4,1.3]) hull()
                {
                    translate([0,0,0]) cylinder(d=6, h=5);
                    translate([6.5,0,0]) cylinder(d=6, h=5);
                    translate([0,-2,0]) cylinder(d=6, h=5);
                    translate([6.5,-2,0]) cylinder(d=6, h=5);                }

                // screws
                translate([   1.5,    1.5, 4.5-0.5]) screw2x6bore();
                translate([45-1.5, 20-1.5, 4.5-0.5]) screw2x6bore();
                translate([45-1.5,    1.5, 4.5-0.5]) screw2x6bore();
                translate([   1.5, 20-1.5, 4.5-0.5]) screw2x6bore();
            }

            translate ([3, 0, 10])
                cube(size=[5, 20, 11], center=true);
        }

        //base
        difference() {
            hull() {
                translate ([-3.5, -48/2, -6]) cube(size=[5, 48, 5], center=false);
                translate ([-3.5, -48/2+5, -6]) cylinder(r=5, h=5, center=false);
                translate ([-3.5, 48/2-5, -6]) cylinder(r=5, h=5, center=false);
            }
            hbaseholes();
        }
    }
}

module screw2x6bore() {
    rotate([180, 0, 0]) {
        cylinder(d=2.2, h=6);
        translate ([0, 0, -1.3])
            cylinder(d1=2*3.6, d2=0, h=2*1.3);
    }
}

module hsonar() {

    // horizontal sonar
    translate ([0, -45/2, 0]) rotate([90, 0, 90]) hcsr04();

    hmount();

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 10]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module car()
{
    base();
    platform();
    board();

    translate ([10, -23, 35]) esc();
//    translate ([-10, 0, 40]) rotate([0, 0, 180]) esc();
}

// full model - vertical sonars
module fullv() {
    car();

    translate ([68, 0, 55]) vsonar();
    translate ([60, 25, 55]) rotate([0, 0, 30]) vsonar();
    translate ([60, -25, 55]) rotate([0, 0, -30]) vsonar();
    translate ([35, 35, 55]) rotate([0, 0, 60]) vsonar();
    translate ([35, -35, 55]) rotate([0, 0, -60]) vsonar();
}

// full model - horizontal sonars
module fullh() {
    car();

    translate ([68, 0, 55]) hsonar();
    translate ([40, 35, 55]) rotate([0, 0, 30]) hsonar();
    translate ([40, -35, 55]) rotate([0, 0, -30]) hsonar();
    translate ([-10, 35, 55]) rotate([0, 0, 60]) hsonar();
    translate ([-10, -35, 55]) rotate([0, 0, -60]) hsonar();
}

module arrangebottom(a) {
    translate ([68, 0, 57]) children();
    translate ([45, 45, 57]) rotate([0, 0, a]) children();
    translate ([45, -45, 57]) rotate([0, 0, -a]) children();

    translate ([0, 52.5, 42]) rotate([180, 0, 2*a]) children();
    translate ([0, -52.5, 42]) rotate([180, 0, -2*a]) children();
}

module fullb() {
    car();
    arrangebottom(30) hsonar();
}

//car();

platform();

/*
intersection() {
platform();
#translate ([80, 0, 0]) cube(size=[20, 200, 400], center=true);
}*/

/*
intersection() {
platform();
#translate ([-80, 0, 0]) cube(size=[20, 200, 400], center=true);
}*/

/*
intersection() {
platform();
#translate ([45-2.5, 30, 0]) cube(size=[20, 20, 400], center=true);
}
*/

//fullb();

//translate ([0, 0, 4.5]) rotate([0, 90, 0]) hmount();

//screw2x6bore();

//projection(cut=true) translate ([0, 0, -47]) platform();