The diagonal lines were added.

The diagonal lines were added.

use <../../../basics/rounded-edges/doughnuts/doughnuts.scad>

masakasMask();

module masakasMask()
{
	rightEdge_x = 53;

	topRadius = rightEdge_x + 20;

	difference()
	{
		masakasMask_solid(topRadius, rightEdge_x);

		masakasMask_cutouts(topRadius, rightEdge_x);
	}
}

// support modules follow

module masakasMask_bottom(rightEdge_x)
{
	masakasMask_cheekFlap();

	xTranslate = rightEdge_x * 2;
	zTranslate = 1;

	// the mirrored cheek flap
	translate([xTranslate, 0, zTranslate])
	rotate([0,180,0])
	masakasMask_cheekFlap();

}

module masakasMask_cheekFlap()
{
	minkowski()
	{
		linear_extrude(height=1, scale=[1,1], slices=20, twist=0)
		polygon(points=
		[
			[0,0],[15,-15],[30,-5],

			[35, 20],

			// inner cheek area
			[38, 23],

			// nose bridge
			[48, 25], [53, 27],

			// right edge
			[53, 40],

			// outer cheek
			[25, 28]

		]);

	    sphere(r=1, $fn=10);
	}
}

module masakasMask_cutouts(topRadius, rightEdge_x)
{
	masakasMask_EyeCutouts(rightEdge_x);

	// bottom flattener
	translate([0,0, -1.7])
	cube([400, 400, 2], center= true);
}

module masakasMask_EyeCutouts(rightEdge_x)
{
	xTranslateOffset = 22;

	xTranslate = rightEdge_x - xTranslateOffset;
	yTranslate = 34;

	translate([xTranslate, yTranslate, -15])
	masakasMask_LeftEyeCoutout();

	// right eye mirrored from left
	rightEye_xTranslate = rightEdge_x + xTranslateOffset;
	translate([rightEye_xTranslate, yTranslate, 20])
	rotate([0,180,0])
	masakasMask_LeftEyeCoutout();
}

module masakasMask_LeftEyeCoutout(rightEdge_x)
{
	minkowski()
	{
		linear_extrude(height=50, scale=[1,1], slices=20, twist=0)
		polygon(points=
		[
			// below the first direction is the side, second direction is the orientation on that side

			// top left
			[0,0],

			// left bottom
			[4,-6],

			// bottom right
			[11,-6],

			// diagnal top
			[14, -4],

			// right top
			[15, 1]

		]);

		cylinder(r=1, $fn=30);
	}
}

module masakasMask_solid(topRadius, rightEdge_x)
{
	union()
	{
		translate([rightEdge_x, 51, 6])
		rotate([-14, 0, 0])
		masakasMask_sunEmblem();

		masakasMask_top(radius = topRadius,
						rightEdge_x = rightEdge_x);

		masakasMask_bottom(rightEdge_x = rightEdge_x);
	}
}

module masakasMask_sunEmblem()
{
	innerRadius = 6;
	minkowskiSphereRadius = 2;
	outerRadius = 7;

	roundDoughnut(height = 1,
				  innerRadius = innerRadius,
		  		  minkowskiSphereRadius = minkowskiSphereRadius,
			      outerRadius = outerRadius);

    ringRadius = outerRadius + minkowskiSphereRadius;

	difference()
	{
		translate([0, 0, 1])
		masakasMask_sunEmblemLines(ringRadius = ringRadius);

		cylinder(r = innerRadius,
				 h = 10,
				 center = true);
	}
}

module masakasMask_sunEmblemSingleLine(height, radius)
{
	color("green")
	rotate([0,90,0])
	cylinder(h = height,
			 r = radius);
}

module masakasMask_sunEmblemLines()
{
	masakasMask_sunEmblemCrossLines(radius = 0.5,
									yTranslate = 1.5);

	masakasMask_sunEmblemDiagnalLines();
}

module masakasMask_sunEmblemCrossLines(radius,
									   yTranslate)
{
//TODO where is this used?
	ringRadius = 7;

	height = 35;

	xTranslate = -(height / 2.0 ) ;

	translate([xTranslate, yTranslate, 0])
	masakasMask_sunEmblemSingleLine(height = height,
							  radius = radius);

    translate([xTranslate, -yTranslate, 0])
    masakasMask_sunEmblemSingleLine(height = height,
	  						  radius = radius);

    vertical_xTranslate = -yTranslate;
	vertical_yTranslate = xTranslate;

	translate([vertical_xTranslate, vertical_yTranslate, 0])
    rotate([0, 0, 90])
    masakasMask_sunEmblemSingleLine(height = height,
  						  	  radius = radius);

    translate([-vertical_xTranslate, vertical_yTranslate, 0])
    rotate([0, 0, 90])
    masakasMask_sunEmblemSingleLine(height = height,
						  	  radius = radius);
}

module masakasMask_sunEmblemDiagnalLines()
{
	rotate([0, 0, 45])
	masakasMask_sunEmblemCrossLines(radius = 1.5,
									yTranslate = 3);
}

module masakasMask_top(radius, rightEdge_x)
{
	cube_zLength = 10;

	xTranslate = rightEdge_x;
	yTranslate = 29;
	zTranslate = -radius + cube_zLength - 0.450;

	translate([xTranslate, yTranslate, zTranslate])

	difference()
	{
		sphere(r = radius, $fn = 60);

		// xy plane cutout
		translate([0, 0, -10])
		cube([400, 400, 145], center=true);

		// xz plane cutout
		color("green")
		translate([0, -radius/2.0, -10])
		cube([190, radius, 190], center=true);
	}
}

use <../../../basics/rounded-edges/doughnuts/doughnuts.scad>

masakasMask();

module masakasMask()
{
	rightEdge_x = 53;

	topRadius = rightEdge_x + 20;

	difference()
	{
		masakasMask_solid(topRadius, rightEdge_x);

		masakasMask_cutouts(topRadius, rightEdge_x);
	}
}

// support modules follow

module masakasMask_bottom(rightEdge_x)
{
	masakasMask_cheekFlap();

	xTranslate = rightEdge_x * 2;
	zTranslate = 1;

	// the mirrored cheek flap
	translate([xTranslate, 0, zTranslate])
	rotate([0,180,0])
	masakasMask_cheekFlap();

}

module masakasMask_cheekFlap()
{
	minkowski()
	{
		linear_extrude(height=1, scale=[1,1], slices=20, twist=0)
		polygon(points=
		[
			[0,0],[15,-15],[30,-5],

			[35, 20],

			// inner cheek area
			[38, 23],

			// nose bridge
			[48, 25], [53, 27],

			// right edge
			[53, 40],

			// outer cheek
			[25, 28]

		]);

	    sphere(r=1, $fn=10);
	}
}

module masakasMask_cutouts(topRadius, rightEdge_x)
{
	masakasMask_EyeCutouts(rightEdge_x);

	// bottom flattener
	translate([0,0, -1.7])
	cube([400, 400, 2], center= true);
}

module masakasMask_EyeCutouts(rightEdge_x)
{
	xTranslateOffset = 22;

	xTranslate = rightEdge_x - xTranslateOffset;
	yTranslate = 34;

	translate([xTranslate, yTranslate, -15])
	masakasMask_LeftEyeCoutout();

	// right eye mirrored from left
	rightEye_xTranslate = rightEdge_x + xTranslateOffset;
	translate([rightEye_xTranslate, yTranslate, 20])
	rotate([0,180,0])
	masakasMask_LeftEyeCoutout();
}

module masakasMask_LeftEyeCoutout(rightEdge_x)
{
	minkowski()
	{
		linear_extrude(height=50, scale=[1,1], slices=20, twist=0)
		polygon(points=
		[
			// below the first direction is the side, second direction is the orientation on that side

			// top left
			[0,0],

			// left bottom
			[4,-6],

			// bottom right
			[11,-6],

			// diagnal top
			[14, -4],

			// right top
			[15, 1]

		]);

		cylinder(r=1, $fn=30);
	}
}

module masakasMask_solid(topRadius, rightEdge_x)
{
	union()
	{
		translate([rightEdge_x, 51, 8])
		rotate([-10, 0, 0])
		masakasMask_sunEmblem();

		masakasMask_top(radius = topRadius,
						rightEdge_x = rightEdge_x);

		masakasMask_bottom(rightEdge_x = rightEdge_x);
	}
}

module masakasMask_sunEmblem()
{
	innerRadius = 6;
	minkowskiSphereRadius = 2;
	outerRadius = 7;

	roundDoughnut(height = 1,
				  innerRadius = innerRadius,
		  		  minkowskiSphereRadius = minkowskiSphereRadius,
			      outerRadius = outerRadius);

    ringRadius = outerRadius + minkowskiSphereRadius;

	difference()
	{
		masakasMask_sunEmblemLines(ringRadius = ringRadius);

		cylinder(r = innerRadius,
				 h = 10,
				 center = true);
	}
}

module masakasMask_sunEmblemLine(height, radius)
{
	color("green")
	rotate([0,90,0])
	cylinder(h = height,
			 r = radius);
}

module masakasMask_sunEmblemLines()
{
	masakasMask_sunEmblemCrossLines();

	masakasMask_sunEmblemDiagnalLines();
}

module masakasMask_sunEmblemCrossLines()
{
ringRadius = 7;

	height = 35;
	radius = 1;

	xTranslate = -(height / 2.0 ) ;
	yTranslate = 3;

	translate([xTranslate, yTranslate, 0])
	masakasMask_sunEmblemLine(height = height,
							  radius = radius);

    translate([xTranslate, -yTranslate, 0])
    masakasMask_sunEmblemLine(height = height,
	  						  radius = radius);

    vertical_xTranslate = -yTranslate;
	vertical_yTranslate = xTranslate;

	translate([vertical_xTranslate, vertical_yTranslate, 0])
    rotate([0, 0, 90])
    masakasMask_sunEmblemLine(height = height,
  						  	  radius = radius);

    translate([-vertical_xTranslate, vertical_yTranslate, 0])
    rotate([0, 0, 90])
    masakasMask_sunEmblemLine(height = height,
						  	  radius = radius);
}

module masakasMask_sunEmblemDiagnalLines()
{

}

module masakasMask_top(radius, rightEdge_x)
{
	cube_zLength = 10;

	xTranslate = rightEdge_x;
	yTranslate = 29;
	zTranslate = -radius + cube_zLength - 0.450;

	translate([xTranslate, yTranslate, zTranslate])

	difference()
	{
		sphere(r = radius, $fn = 60);

		// xy plane cutout
		translate([0, 0, -10])
		cube([400, 400, 145], center=true);

		// xz plane cutout
		color("green")
		translate([0, -radius/2.0, -10])
		cube([190, radius, 190], center=true);
	}
}


Update belt clamp with known-working version

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

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

gThick = 8;
gRibHeight = 6;
gTopRoundoverRadius = 4;
gSideRoundoverRadius = 4;
gMainWidth = 16;
gMainHeight = 66;

gTopWidth = 30;
gRibWidth = 3.4;
gRibLength = 50;
gHoleSpacing = 20;
gBeltWide = 14; // 1/4" belt + slop
gBeltPitch = 2.032; // MXL in mm
gBeltThick = 1.5;
e = 0.02;

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

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

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

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

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

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

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

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

union() {
  Upright();
  translate([0,-gMainHeight/2-12,0]) rotate([0,0,180]) Top();
}
//union() {
//  for(i=[1:4]) {
//    translate([0,-15*i,0]) Top();
//  }
//}

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

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

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

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

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

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

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

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

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

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

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

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


Protect sinusoid() and cosinusoid() when the length is null

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2017 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.
 *
 * Lines handling.
 *
 * @package core/line
 * @author jsconan
 */

/**
 * Computes the coordinates of a 2D arc by rotating a vector along the provided angle.
 *
 * @param Number|Vector r - The number or the vector that defines the radius of the arc (can be ellipse).
 * @param Number [a] - The angle to cover.
 * @param Vector [o] - The coordinates of the center of the arc.
 * @param Number [a1] - The start angle of the arc.
 * @param Number [a2] - The end angle of the arc.
 * @returns Vector[]
 */
function arc(r, a=DEGREES, o, a1, a2) =
    let(
        r = vector2D(r),
        o = vector2D(o),
        a1 = deg(a1),
        a2 = a2 != undef ? deg(a2) : a1 + deg(a)
    )
    !r[0] || !r[1] || (!a1 && !a2) ? []
   :let(
        start = min(a1, a2),
        end = max(a1, a2),
        range = end - start,
        step = astep(max(r), absdeg(range)),
        inc = sign(a2 - a1) * step
    )
    complete(
        // intermediate points
        range <= step ? []
       :[ for (a = [a1 + inc : inc : a2]) arcp(r, a) + o ],
        // the start point
        arcp(r, a1) + o,
        // the final point
        arcp(r, a2) + o
    )
;

/**
 * Computes a sinusoid wave.
 * Without angle, the line will be horizontal.
 *
 * @param Number l - The length of the curve.
 * @param Number w - The wave length.
 * @param Number h - The amplitude of the sinusoid.
 * @param Number p - The phase of the sinusoid (the start angle).
 * @param Number o - The offset of the sinusoid.
 * @param Number a - A rotation angle.
 * @returns Vector[]
 */
function sinusoid(l, w, h, p, o, a) =
    let(
        a = deg(a),
        l = float(l),
        w = divisor(w),
        h = divisor(h),
        step = l / (fragments(h) * l / w)
    )
    !l ? []
   :[
        for (x = [0 : step : l])
            let( p = sinp(x=x, w=w, h=h, p=p, o=o) )
            a ? rotp(p, a) : p
    ]
;

/**
 * Computes a cosinusoid wave.
 * Without angle, the line will be vertical.
 *
 * @param Number l - The length of the curve.
 * @param Number w - The wave length.
 * @param Number h - The amplitude of the cosinusoid.
 * @param Number p - The phase of the cosinusoid (the start angle).
 * @param Number o - The offset of the cosinusoid.
 * @param Number a - A rotation angle.
 * @returns Vector[]
 */
function cosinusoid(l, w, h, p, o, a) =
    let(
        a = deg(a),
        l = float(l),
        w = divisor(w),
        h = divisor(h),
        step = l / (fragments(h) * l / w)
    )
    !l ? []
   :[
        for (y = [0 : step : l])
            let( p = cosp(y=y, w=w, h=h, p=p, o=o) )
            a ? rotp(p, a) : p
    ]
;

/**
 * Computes a line from a path.
 *
 * A path is composed of commands, each one being a simple array starting by the
 * command name. Each command will produce points relative to the last command
 * or to the last existing point. When the line starts it will rely on the
 * existing points, if any. If no existing points is provided, the line will
 * start from the absolute origin ([0, 0]), unless the first command is a point.
 *
 * The following commands are recognized:
 * - P, Point: ["P", <x>, <y>] | ["P", <point>] - Adds a point at the given absolute coordinates.
 * - L, Line: ["L", <x>, <y>] | ["L", <point>] - Adds a line from the last point to the given relative coordinates.
 * - H, Horizontal line: ["H", <length>] - Adds an horizontal line from the last point with the given length. The sign of the length determines the direction.
 * - V, Vertical line: ["V", <length>] - Adds a vertical line from the last point with the given length. The sign of the length determines the direction.
 * - I, Intersection: ["I", <P1>, <P2>, <P3>] - Adds a line from the last point, that pass through P1 and intersects with the line defined by P2 and P3.
 * - T, Tangent: ["T", <x>, <y>, <radius>] | ["T", <point>, <radius>] - Adds a line from the last point, that ends at the tangent point with the defined circle. The sign of the radius determines the direction
 * - A, Angle: ["A", <angle>, <length>] - Adds a leaned line from the last point with the given angle and the given length. The sign of the length determines the direction.
 * - C, Circle: ["C", <radius>, <start angle>, <end angle>] - Adds a circle arc from the last point with the given radius and with the given angle.
 * - B, Bezier curve: ["B", <P1>, <P2>, <P3>] - Adds a cubic bezier curve from the last point with the given control points (up to 3, the first beeing the last existing point).
 *
 * @param Array p - The path from which build the line
 * @param Vector[] points - The existing points to start the line from.
 * @param Number i - The start index in the path (default: 0)
 * @returns Vector[]
 */
function path(p, points, i) =
    let(
        p = array(p),
        i = integer(i),
        points = array(points),
        length = len(points),
        point = vector2D(points[length - 1]),
        cur = p[i],
        cmd = cur[0],
        l = len(cur)
    )
    i >= len(p) ? points
   :let(
        values = (
            l <= 1 ? [point]
            :cmd == "P" || cmd == "p" ? [isVector(cur[1]) ? vector2D(cur[1]) : apply2D(x=cur[1], y=cur[2])]
            :cmd == "L" || cmd == "l" ? [point, point + (isVector(cur[1]) ? vector2D(cur[1]) : apply2D(x=cur[1], y=cur[2]))]
            :cmd == "H" || cmd == "h" ? [point, point + apply2D(x=cur[1])]
            :cmd == "V" || cmd == "v" ? [point, point + apply2D(y=cur[1])]
            :cmd == "I" || cmd == "i" ? [point, intersect2D(point, cur[1], cur[2], cur[3])]
            :cmd == "T" || cmd == "t" ? let(isv = isVector(cur[1])) [point, tangent2D(point, isv ? cur[1] : apply2D(x=cur[1], y=cur[2]), isv ? cur[2] : cur[3])]
            :cmd == "A" || cmd == "a" ? [point, point + arcPoint(a=cur[1], r=cur[2])]
            :cmd == "C" || cmd == "c" ? arc(r=cur[1], a1=cur[2], a2=cur[3], o=point + arcPoint(a=float(cur[2]) + STRAIGHT, r=cur[1]))
            :cmd == "B" || cmd == "b" ? (
                l >= 4 ? concat([point], cubicBezierCurve(point, point + vector2D(cur[1]), point + vector2D(cur[2]), point + vector2D(cur[3])))
               :l == 3 ? concat([point], quadraticBezierCurve(point, point + vector2D(cur[1]), point + vector2D(cur[2])))
               :[point, point + vector2D(cur[1])]
            )
            :[]
        )
    )
    path(p=p, points=concat(values[0] == point ? slice(points, 0, -1) : points, values), i=i + 1)
;

/**
 * Computes the outline of a polygon. The outline can be at a particular distance.
 *
 * @param Vector[] points - The points defining the polygon to outline.
 * @param Number distance - The distance from the polygon for the outline.
 *                          A positive distance will place the outline outside,
 *                          while a negative distance will place it inside.
 * @returns Vector[]
 */
function outline(points, distance) =
    let(
        points = array(points),
        l = len(points),
        prev = l - 1,
        next = 1
    )
    l >= 3 ? [
        for (i = [0 : 1 : l - 1])
            vertexOutline2D(
                a=points[(i + prev) % l],
                v=points[i],
                b=points[(i + next) % l],
                distance=distance
            )
    ] : points
;

/**
 * Adds a value to each point of a line.
 *
 * @param Vector[] points - The points defining the line.
 * @param Number|Vector value - The value to add to the line.
 * @returns Vector[]
 */
function lineAdd(points, value) =
    let(
        l = max(
            float(len(value)),
            float(len(points[0]))
        ),
        value = vector3D(value)
    )
    l > 2 ? [ for (p = points) vector3D(p) + value ]
          : [ for (p = points) vector2D(p) + value ]
;

/**
 * Builds a vector of faces to be used in a polyhedron. The function accepts the
 * number of points defining one main face of the polyhedron, then returns the
 * faces vector that contains the indices of each face enclosing the solid. This
 * only apply on simple polyhedron where two opposite faces share the same
 * number of points.
 *
 * @param Number length - The number of points for one main face of the polyhedron
 * @returns Vector[]
 */
function simplePolyhedronFaces(length) =
    let(
        length = integer(length)
    )
    length < 3 ? []
   :let(
       r1 = 0,
       r2 = length - 1,
       r3 = length,
       r4 = length * 2 - 1
   )
    concat([
        range(r1, r2),
        range(r4, r3)
    ], [
        for (i = [r1 : r2]) [
            r3 + i,
            r3 + (i + 1) % length,
            (r1 + i + 1) % length,
            r1 + i
        ]
    ])
;

/**
 * Composes a list of points to be used in a simple polyhedron.
 *
 * @param Vector[] [bottom] - The list of points for the bottom face.
 * @param Vector[] [top] - The list of points for the top face.
 * @param Vector[] [points] - The list of points for a main face.
 * @param Vector [distane] - The distance between two main faces.
 * @param Number [x] - The distance between two main faces on the X-axis.
 * @param Number [y] - The distance between two main faces on the Y-axis.
 * @param Number [z] - The distance between two main faces on the Z-axis.
 * @returns Vector[]
 */
function simplePolyhedronPoints(bottom, top, points, distance, x, y, z) =
    let(
       distance = apply3D(v=distance, x=x, y=y, z=z)
    )
    top && bottom ? concat(
        [ for (p = bottom) vector3D(p) ],
        [ for (p = top) vector3D(p) + distance ]
    )
   :let(
       points = [ for (p = (points ? points : (top ? top : bottom))) vector3D(p) ]
   )
   concat(
       points,
       [ for (p = points) p + distance ]
   )
;

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2017 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.
 *
 * Lines handling.
 *
 * @package core/line
 * @author jsconan
 */

/**
 * Computes the coordinates of a 2D arc by rotating a vector along the provided angle.
 *
 * @param Number|Vector r - The number or the vector that defines the radius of the arc (can be ellipse).
 * @param Number [a] - The angle to cover.
 * @param Vector [o] - The coordinates of the center of the arc.
 * @param Number [a1] - The start angle of the arc.
 * @param Number [a2] - The end angle of the arc.
 * @returns Vector[]
 */
function arc(r, a=DEGREES, o, a1, a2) =
    let(
        r = vector2D(r),
        o = vector2D(o),
        a1 = deg(a1),
        a2 = a2 != undef ? deg(a2) : a1 + deg(a)
    )
    !r[0] || !r[1] || (!a1 && !a2) ? []
   :let(
        start = min(a1, a2),
        end = max(a1, a2),
        range = end - start,
        step = astep(max(r), absdeg(range)),
        inc = sign(a2 - a1) * step
    )
    complete(
        // intermediate points
        range <= step ? []
       :[ for (a = [a1 + inc : inc : a2]) arcp(r, a) + o ],
        // the start point
        arcp(r, a1) + o,
        // the final point
        arcp(r, a2) + o
    )
;

/**
 * Computes a sinusoid wave.
 * Without angle, the line will be horizontal.
 *
 * @param Number l - The length of the curve.
 * @param Number w - The wave length.
 * @param Number h - The amplitude of the sinusoid.
 * @param Number p - The phase of the sinusoid (the start angle).
 * @param Number o - The offset of the sinusoid.
 * @param Number a - A rotation angle.
 * @returns Vector[]
 */
function sinusoid(l, w, h, p, o, a) =
    let(
        a = deg(a),
        l = float(l),
        w = divisor(w),
        h = divisor(h),
        step = l / (fragments(h) * l / w)
    )
    [
        for (x = [0 : step : l])
            let( p = sinp(x=x, w=w, h=h, p=p, o=o) )
            a ? rotp(p, a) : p
    ]
;

/**
 * Computes a cosinusoid wave.
 * Without angle, the line will be vertical.
 *
 * @param Number l - The length of the curve.
 * @param Number w - The wave length.
 * @param Number h - The amplitude of the cosinusoid.
 * @param Number p - The phase of the cosinusoid (the start angle).
 * @param Number o - The offset of the cosinusoid.
 * @param Number a - A rotation angle.
 * @returns Vector[]
 */
function cosinusoid(l, w, h, p, o, a) =
    let(
        a = deg(a),
        l = float(l),
        w = divisor(w),
        h = divisor(h),
        step = l / (fragments(h) * l / w)
    )
    [
        for (y = [0 : step : l])
            let( p = cosp(y=y, w=w, h=h, p=p, o=o) )
            a ? rotp(p, a) : p
    ]
;

/**
 * Computes a line from a path.
 *
 * A path is composed of commands, each one being a simple array starting by the
 * command name. Each command will produce points relative to the last command
 * or to the last existing point. When the line starts it will rely on the
 * existing points, if any. If no existing points is provided, the line will
 * start from the absolute origin ([0, 0]), unless the first command is a point.
 *
 * The following commands are recognized:
 * - P, Point: ["P", <x>, <y>] | ["P", <point>] - Adds a point at the given absolute coordinates.
 * - L, Line: ["L", <x>, <y>] | ["L", <point>] - Adds a line from the last point to the given relative coordinates.
 * - H, Horizontal line: ["H", <length>] - Adds an horizontal line from the last point with the given length. The sign of the length determines the direction.
 * - V, Vertical line: ["V", <length>] - Adds a vertical line from the last point with the given length. The sign of the length determines the direction.
 * - I, Intersection: ["I", <P1>, <P2>, <P3>] - Adds a line from the last point, that pass through P1 and intersects with the line defined by P2 and P3.
 * - T, Tangent: ["T", <x>, <y>, <radius>] | ["T", <point>, <radius>] - Adds a line from the last point, that ends at the tangent point with the defined circle. The sign of the radius determines the direction
 * - A, Angle: ["A", <angle>, <length>] - Adds a leaned line from the last point with the given angle and the given length. The sign of the length determines the direction.
 * - C, Circle: ["C", <radius>, <start angle>, <end angle>] - Adds a circle arc from the last point with the given radius and with the given angle.
 * - B, Bezier curve: ["B", <P1>, <P2>, <P3>] - Adds a cubic bezier curve from the last point with the given control points (up to 3, the first beeing the last existing point).
 *
 * @param Array p - The path from which build the line
 * @param Vector[] points - The existing points to start the line from.
 * @param Number i - The start index in the path (default: 0)
 * @returns Vector[]
 */
function path(p, points, i) =
    let(
        p = array(p),
        i = integer(i),
        points = array(points),
        length = len(points),
        point = vector2D(points[length - 1]),
        cur = p[i],
        cmd = cur[0],
        l = len(cur)
    )
    i >= len(p) ? points
   :let(
        values = (
            l <= 1 ? [point]
            :cmd == "P" || cmd == "p" ? [isVector(cur[1]) ? vector2D(cur[1]) : apply2D(x=cur[1], y=cur[2])]
            :cmd == "L" || cmd == "l" ? [point, point + (isVector(cur[1]) ? vector2D(cur[1]) : apply2D(x=cur[1], y=cur[2]))]
            :cmd == "H" || cmd == "h" ? [point, point + apply2D(x=cur[1])]
            :cmd == "V" || cmd == "v" ? [point, point + apply2D(y=cur[1])]
            :cmd == "I" || cmd == "i" ? [point, intersect2D(point, cur[1], cur[2], cur[3])]
            :cmd == "T" || cmd == "t" ? let(isv = isVector(cur[1])) [point, tangent2D(point, isv ? cur[1] : apply2D(x=cur[1], y=cur[2]), isv ? cur[2] : cur[3])]
            :cmd == "A" || cmd == "a" ? [point, point + arcPoint(a=cur[1], r=cur[2])]
            :cmd == "C" || cmd == "c" ? arc(r=cur[1], a1=cur[2], a2=cur[3], o=point + arcPoint(a=float(cur[2]) + STRAIGHT, r=cur[1]))
            :cmd == "B" || cmd == "b" ? (
                l >= 4 ? concat([point], cubicBezierCurve(point, point + vector2D(cur[1]), point + vector2D(cur[2]), point + vector2D(cur[3])))
               :l == 3 ? concat([point], quadraticBezierCurve(point, point + vector2D(cur[1]), point + vector2D(cur[2])))
               :[point, point + vector2D(cur[1])]
            )
            :[]
        )
    )
    path(p=p, points=concat(values[0] == point ? slice(points, 0, -1) : points, values), i=i + 1)
;

/**
 * Computes the outline of a polygon. The outline can be at a particular distance.
 *
 * @param Vector[] points - The points defining the polygon to outline.
 * @param Number distance - The distance from the polygon for the outline.
 *                          A positive distance will place the outline outside,
 *                          while a negative distance will place it inside.
 * @returns Vector[]
 */
function outline(points, distance) =
    let(
        points = array(points),
        l = len(points),
        prev = l - 1,
        next = 1
    )
    l >= 3 ? [
        for (i = [0 : 1 : l - 1])
            vertexOutline2D(
                a=points[(i + prev) % l],
                v=points[i],
                b=points[(i + next) % l],
                distance=distance
            )
    ] : points
;

/**
 * Adds a value to each point of a line.
 *
 * @param Vector[] points - The points defining the line.
 * @param Number|Vector value - The value to add to the line.
 * @returns Vector[]
 */
function lineAdd(points, value) =
    let(
        l = max(
            float(len(value)),
            float(len(points[0]))
        ),
        value = vector3D(value)
    )
    l > 2 ? [ for (p = points) vector3D(p) + value ]
          : [ for (p = points) vector2D(p) + value ]
;

/**
 * Builds a vector of faces to be used in a polyhedron. The function accepts the
 * number of points defining one main face of the polyhedron, then returns the
 * faces vector that contains the indices of each face enclosing the solid. This
 * only apply on simple polyhedron where two opposite faces share the same
 * number of points.
 *
 * @param Number length - The number of points for one main face of the polyhedron
 * @returns Vector[]
 */
function simplePolyhedronFaces(length) =
    let(
        length = integer(length)
    )
    length < 3 ? []
   :let(
       r1 = 0,
       r2 = length - 1,
       r3 = length,
       r4 = length * 2 - 1
   )
    concat([
        range(r1, r2),
        range(r4, r3)
    ], [
        for (i = [r1 : r2]) [
            r3 + i,
            r3 + (i + 1) % length,
            (r1 + i + 1) % length,
            r1 + i
        ]
    ])
;

/**
 * Composes a list of points to be used in a simple polyhedron.
 *
 * @param Vector[] [bottom] - The list of points for the bottom face.
 * @param Vector[] [top] - The list of points for the top face.
 * @param Vector[] [points] - The list of points for a main face.
 * @param Vector [distane] - The distance between two main faces.
 * @param Number [x] - The distance between two main faces on the X-axis.
 * @param Number [y] - The distance between two main faces on the Y-axis.
 * @param Number [z] - The distance between two main faces on the Z-axis.
 * @returns Vector[]
 */
function simplePolyhedronPoints(bottom, top, points, distance, x, y, z) =
    let(
       distance = apply3D(v=distance, x=x, y=y, z=z)
    )
    top && bottom ? concat(
        [ for (p = bottom) vector3D(p) ],
        [ for (p = top) vector3D(p) + distance ]
    )
   :let(
       points = [ for (p = (points ? points : (top ? top : bottom))) vector3D(p) ]
   )
   concat(
       points,
       [ for (p = points) p + distance ]
   )
;


Reworking enclosure shell w. LED strip channel

use <pin-hinge.scad>;
width=40;
length=66;
height=17.5;
lid_height=7;
wall=1;
radius_xy=wall*4;
radius_z=wall*2;
inset=0.75;

// the base
translate([-1.2*width/2,0,height/2+wall]) {
    difference() {
        union() {
            hinged_box(width, length, height, 1);
        }
        union() {
            // aperture for the usb port
            *translate([0, -length/2, -2]) {
                charge_port(10, 5, wall);
            }
            // top strap receiver
            top_strap(width/3);
        }
    }
}

// the lid
translate([1.2*width/2,0,lid_height/2+wall]) rotate([0,180,0]) {
    difference() {
        union() {
            hinged_box(width, length, lid_height, -1);
        }
        #translate([0,12,lid_height/2]) rotate(180) button(10,36,wall*2);
    }

}

// the strap
translate([1.2*width+1.2*width/2,0,width/3/2]) {
    rotate([0,90,0]) {
        top_strap(width/3);
    }
}

module top_strap(strap_w) {
    strap_len=length+inset;
    wall=1;
    translate([0,0,inset*2]) difference() {
        union() {
            cube([strap_w, strap_len, height-wall], center=true);
            translate([0,0,-height/2+inset*2]) {
                rotate([0,0,90]) outer_ridges(inset, strap_w, [strap_len, strap_w, height]);
            }
        }
        translate([0,0,-wall/2]) cube([strap_w, strap_len-2*wall, height-wall*2], center=true);
    }
}
module outer_ridges(radius, length, bounds) {
    translate([-1*(bounds[0]/2+radius/2), 0]) {
        rotate([180,0,0]) ridge(radius, length, 1);
    }
    translate([bounds[0]/2+radius/2, 0]) {
        rotate([180,0,0]) ridge(radius, length, -1);
    }
}
module ridge(radius, length, orient=1) {
    rotate([0,0,orient<0?0:180])
    translate([-radius/2, 0, radius/2]) difference() {
        rotate([90,0,0]) cylinder(r=radius, h=length, center=true, $fn=12);
        union() {
            translate([0,0,radius/2]) cube([radius*2, length, radius], center=true);
            translate([-radius/2,0,0]) cube([radius, length, radius*2], center=true);
        }
    }
}

module hinged_oval_box(width, length, height, orient) {
    hull() {
        translate([0, -length/2+5, 2]) {
            resize(newsize=[width, width/2, height]) sphere(d=width, center=true);
        }
        translate([0, +length/2-5, 2]) {
            resize(newsize=[width*1.25, width/2, height]) sphere(d=width, center=true);
        }
    }
}
module hinged_box(width, length, height, orient) {
    hinge_radius=4;
    hinge_y_offset=length/2+radius_xy+hinge_radius/2;
    difference() {
        union() {
            difference() {
                shell(width, length, height, orient);
                translate([0, -1*hinge_y_offset, orient*(height/2)]) {
                  rotate(a=[0,90,0]) translate([0,0,0.25]) {
                      cylinder(r=hinge_radius, h=width*.66, center=true);
                  }
                }

            }
            translate([0, -1*hinge_y_offset, orient*(height/2)]) {
              rotate(a=[90,180,0]) translate([0,0,0.25]) {
                  _pin_hinge(width*.66, hinge_radius, orient>0?0:1);
              }
            }
        }
        union() {
            // the slot for the LEDs
        }
    }
}

module _pin_hinge(h, radius, is_top=0, clearance=0) {
    fingers=5; pin_radius=1.5; gap=0.75;
    total_gap=(fingers-1)*gap;
    unit_h=(h-total_gap)/fingers;
    rotate([-90,0,0]) rotate([0,90,0]) translate([0,-radius,-1*(h/2)+unit_h/2]) {
        for(i=[0:fingers-1]) {
            if (i%2>0 && is_top) {
                translate([0,0, gap*i+i*unit_h]) {
                    pin_hinge_unit(unit_h, radius, pin_radius, i);
                    translate([radius-1,radius*2,0]) #cube([radius*1.5, radius, unit_h], center=true);
                }
            }
            if (i%2==0 && !is_top) {
                translate([0,0, gap*i+i*unit_h]) {
                    pin_hinge_unit(unit_h, radius, pin_radius, i);
                    translate([-1*(radius-1),radius*2,0]) #cube([radius*1.5, radius, unit_h], center=true);
                }
            }
        }
    }
}

module shell(width, length, height, orient=1) {
    difference() {
        union() {
            //cube([2,length, 2], center=true);
            hull() {
                linear_extrude(height=height+wall, center=true) {
                    offset(radius_xy) square([width-radius_xy*2+wall*2, length], center=true);
                }
                translate([0,length/2-wall,0]) {
                    difference() {
                        resize([width, width/2, height]) cylinder(d=width, height=height, center=true);
                        translate([0,-width/4,0]) cube([width, width/2, height], center=true);
                    }
                }
            }
        }
        translate([0,0,orient*wall]) {
            translate([0,length/2-inset, orient*wall]) {
                difference() {
                    resize([width-wall, width/2-wall*4, height], auto=true) cylinder(d=width, height=height, center=true);
                    translate([0,-width/4+wall*2,0]) cube([width-wall, width/2-wall*4, height], center=true);
                }
            }
            cube([width, length, height], center=true);
        }
    }
    translate([0,length/2+wall,0]) {
        cube([width*0.75, wall*2, height], center=true);
    }
}

module radiused_cube(dims, radii, center=true) {
    // radiused box sized to *outer* dimensions
    // flatten the radii to 1/2 height
    rad_x=radii[0];
    rad_y=radii[1];
    rad_z=radii[2];
    w=dims[0]; l=dims[1]; h=dims[2];

    translate([-w/2,-l/2,-h/2]) hull() {
        for(z=[0:1]) {
            translate([rad_x,rad_y,z==0?rad_z:h-rad_z]) {
                resize(newsize=[rad_x, rad_y, rad_z]) sphere(rad_x, center=true);
            }
            translate([w-rad_x,rad_y,z==0?rad_z:h-rad_z]) {
                resize(newsize=[rad_x, rad_y, rad_z]) sphere(rad_x, center=true);
            }
            translate([rad_x,l-rad_y,z==0?rad_z:h-rad_z]) {
                resize(newsize=[rad_x, rad_y, rad_z]) sphere(rad_x, center=true);
            }
            translate([w-rad_x,l-rad_y,z==0?rad_z:h-rad_z]) {
                resize(newsize=[rad_x, rad_y, rad_z]) sphere(rad_x, center=true);
            }
        }
    }
}

module button(radius, length, depth) {
    gap=1;
    difference() {
        linear_extrude(height=depth, twist=0, center=true) {
            difference() {
                offset(r=radius_xy+gap) {
                    circle(radius, center = true);
                    translate([0, 10, 0]) square([radius, radius*2.5], center = true);
                }
                offset(r=radius_xy) {
                    circle(radius, center = true);
                    translate([0, 10, 0]) square([radius, radius*2.5], center = true);
                }
            }
        }
        translate([0,  radius*2.5, 0]) cube([radius*2, radius_xy+gap, depth], center=true);
    }
}

module hanger() {
    hull() {
        translate([w/2, 0, h]) resize([width, width*0.66, radius*4]) #sphere(radius, center=true);
        translate([w/2, 10, h]) resize([width, width*0.66, radius*4]) #sphere(radius, center=true);
    }
}

module charge_port(width, height, wall) {
    radius=height/2;
    rotate([90, 0, 0]) hull() {
        translate([-width/2+radius, 0, 0]) cylinder(r=radius, h=wall, $fn=16);
        translate([width/2-radius, 0, 0]) cylinder(r=radius, h=wall, $fn=16);
    }
}

use <pin-hinge.scad>;
width=40;
length=66;
height=17.5;
lid_height=5;
wall=1.5;
r=wall*2;
inset=0.75;

// the base
translate([-1.2*width/2,0,height/2+wall]) {
    difference() {
        union() {
            hinged_box(width, length, height, 1);
        }
        union() {
            // aperture for the usb port
            *translate([0, -length/2, -2]) {
                charge_port(10, 5, wall);
            }
            // top strap receiver
            #top_strap(width/3);
        }
    }
}

// the lid
translate([1.2*width/2,0,lid_height/2+wall]) rotate([0,180,0]) {
    difference() {
        union() {
            hinged_box(width, length, lid_height, -1);
        }
        translate([0,12,lid_height/2+wall]) rotate(180) button(10,36,wall*2);
    }

}

// the strap
translate([1.2*width+1.2*width/2,0,width/3/2]) {
    rotate([0,90,0]) {
        top_strap(width/3);
    }
}

module top_strap(strap_w) {
    strap_len=length+inset;
    wall=1;
    translate([0,0,inset*2]) union() {
        union() {
            cube([strap_w, strap_len, height-wall], center=true);
            translate([0,0,-height/2+inset*2]) {
                rotate([0,0,90]) outer_ridges(inset, strap_w, [strap_len, strap_w, height]);
            }
        }
        translate([0,0,-wall/2]) #cube([strap_w, strap_len-2*wall, height-wall*2], center=true);
    }
}
module outer_ridges(radius, length, bounds) {
    translate([-1*(bounds[0]/2+radius/2), 0]) {
        rotate([180,0,0]) ridge(radius, length, 1);
    }
    translate([bounds[0]/2+radius/2, 0]) {
        rotate([180,0,0]) ridge(radius, length, -1);
    }
}
module ridge(radius, length, orient=1) {
    rotate([0,0,orient<0?0:180])
    translate([-radius/2, 0, radius/2]) difference() {
        rotate([90,0,0]) cylinder(r=radius, h=length, center=true, $fn=12);
        union() {
            translate([0,0,radius/2]) cube([radius*2, length, radius], center=true);
            translate([-radius/2,0,0]) #cube([radius, length, radius*2], center=true);
        }
    }
}

module hinged_oval_box(width, length, height, orient) {
    hull() {
        translate([0, -length/2+5, 2]) {
            resize(newsize=[width, width/2, height]) sphere(d=width, center=true);
        }
        translate([0, +length/2-5, 2]) {
            resize(newsize=[width*1.25, width/2, height]) sphere(d=width, center=true);
        }
    }
}
module hinged_box(width, length, height, orient) {
    difference() {
        union() {
            shell(width, length, height, r, orient);
            clearance=wall;
            translate([0,-1*(length/2+r+clearance),orient*(height/2+r/2)]) {
              rotate(a=[90,180,0]) translate([0,0,0.25]) {
                  _pin_hinge(width*.66, 4, orient>0?0:1);
              }
            }
        }
        union() {
            translate([0,0,orient*wall]) radiused_cube([width, length, height+r*2], r/2);
        }
    }
}

module _pin_hinge(h, radius, is_top=0) {
    fingers=5; pin_radius=1.5; gap=0.75;
    total_gap=(fingers-1)*gap;
    unit_h=(h-total_gap)/fingers;
    rotate([-90,0,0]) rotate([0,90,0]) translate([0,-radius,-1*(h/2)+unit_h/2]) {
        for(i=[0:fingers-1]) {
            if (i%2>0 && is_top) {
                translate([0,0, gap*i+i*unit_h]) pin_hinge_unit(unit_h, radius, pin_radius, i);
            }
            if (i%2==0 && !is_top) {
                translate([0,0, gap*i+i*unit_h]) pin_hinge_unit(unit_h, radius, pin_radius, i);
            }
        }
    }
}

module shell(width, length, height, r, orient=1) {
    // hollow box sized to interior dimension
    // w. thicker end walls
    difference() {
        radiused_cube([width+wall*2, length+wall*4, height+wall*2], r);
        translate([0,0,orient*r/2]) radiused_cube([width, length, height+r], r);
    }
}

module radiused_cube(dims, radius, center=true) {
    // radiused box sized to *outer* dimensions
    w=dims[0]; l=dims[1]; h=dims[2];
    offset=radius;
    translate([-w/2,-l/2,-h/2]) hull() {
        for(z=[0:1]) {
            translate([offset,offset,z==0?offset:h-offset]) sphere(radius, center=true);
            translate([w-offset,offset,z==0?offset:h-offset]) sphere(radius, center=true);
            translate([offset,l-offset,z==0?offset:h-offset]) sphere(radius, center=true);
            translate([w-offset,l-offset,z==0?offset:h-offset]) sphere(radius, center=true);
        }
    }
}

module button(radius, length, depth) {
    gap=1;
    difference() {
        linear_extrude(height=depth, twist=0, center=true) {
            difference() {
                offset(r=r+gap) {
                    circle(radius, center = true);
                    translate([0, 10, 0]) square([radius, radius*2.5], center = true);
                }
                offset(r=r) {
                    circle(radius, center = true);
                    translate([0, 10, 0]) square([radius, radius*2.5], center = true);
                }
            }
        }
        translate([0,  radius*2.5, 0]) #cube([radius*2, r+gap, depth], center=true);
    }
}

module hanger() {
    hull() {
        translate([w/2, 0, h]) resize([width, width*0.66, radius*4]) #sphere(radius, center=true);
        translate([w/2, 10, h]) resize([width, width*0.66, radius*4]) #sphere(radius, center=true);
    }
}

module charge_port(width, height, wall) {
    radius=height/2;
    rotate([90, 0, 0]) hull() {
        translate([-width/2+radius, 0, 0]) cylinder(r=radius, h=wall, $fn=16);
        translate([width/2-radius, 0, 0]) cylinder(r=radius, h=wall, $fn=16);
    }
}


joint model

joint model

module joint(groundSpacing)
{
  hch = groundSpacing + 6.5/2;  // hole center height

  difference()
  {
    // main element
    union()
    {
      cube([3, 6.5, hch]);
      translate([0, 6.5/2, hch])
        rotate([0, 90, 0])
          cylinder(r=6.5/2, h=3, $fs=0.01);
    }
    // screw hole
    translate([-1, 6.5/2, hch])
      rotate([0, 90, 0])
        cylinder(r=4.5/2, h=3+2*1, $fs=0.01);
  }
}

joint(1);

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