Removed dodgy Explode

Removed dodgy Explode

/*
	Assembly: LogoBotAssembly

	Master assembly for the LogoBot

	Local Frame:
		Centred on the origin, such that bottom edge of wheels sit on XY plane.
		Front of the robot faces towards y+

	Parameters:
		PenLift - Set to true to include a PenLift, defaults to false

	Returns:
		Complete LogoBot model

*/

// Connectors
// ----------
// These are used within this module to layout the various vitamins/sub-assemblies
// The same connectors are used to shape associated portions of the LogoBotBase_STL

LogoBot_Con_Breadboard          = [[0, 12, 0],[0,0,-1], -90,0,0];

LogoBot_Con_LeftMotorDriver     = [[-20, 16, 3],[0,0,1],180,0,0];
LogoBot_Con_RightMotorDriver    = [[20, 16, 3],[0,0,1],180,0,0];

LogoBot_Con_Caster = [ [0, -BaseDiameter/2 + 10, 0], [0,0,1], 0, 0, 0];

LogoBot_Con_PenLift = [ [-20, -5, 10], [0,1,0], 0, 0, 0];

// Assembly
// --------

function LogoBotAssembly_NumSteps() = 10 + (PenLift ? 1 : 0);

module LogoBotAssembly ( PenLift=false ) {

    Assembly("LogoBot");

	translate([0, 0, GroundClearance]) {

		// Default Design Elements
		// -----------------------

		// Base
		LogoBotBase_STL();

        step(1,
            "Connect the breadboard assembly to the underside of the base",
            "400 300 0 17 12 112 0 222 513")
		    attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170), ExplodeSpacing=-20)
		    BreadboardAssembly();

		// Bumper assemblies (x2)
		step(2, "Connect the two bumper assemblies", "400 300 -6 7 19 64 1 212 625" )
		    for (x=[0,1], y=[0,1])
			mirror([0,y,0])
			mirror([x,0,0])
			translate([(BaseDiameter/2-10) * cos(45), (BaseDiameter/2-10) * sin(45), -8 ])
			rotate([0,0,-30])
			MicroSwitch();

		step(3, "Push the two motor drivers onto the mounting posts", "400 300 -6 7 19 64 1 212 625") {
		    // Left Motor Driver
		    attach(LogoBot_Con_LeftMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperLeft), ExplodeSpacing=-20)
			    ULN2003DriverBoard();

		    // Right Motor Driver
		    attach(LogoBot_Con_RightMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperRight), ExplodeSpacing=-20)
			    ULN2003DriverBoard();
		}

		// Motor + Wheel assemblies (x2)
		step(4, "Clip the two wheels assemblies onto the base and connect the motor leads to the the motor drivers", "400 300 -4 6 47 66 0 190 740")
            for (i=[0:1])
                mirror([i,0,0])
                translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
                translate([-15,0,0])
                attach(DefConDown, DefConDown, ExplodeSpacing = 40)
                translate([15,0,0])
                rotate([-90, 0, 90]) {
                    assign($rightSide= i == 0? 1 : 0)
                        WheelAssembly();
                }

		// Connect jumper wires
		step(5,
		    "Connect the jumper wires between the motor drivers and the Arduino",
		    "400 300 9 26 54 38 0 161 766")
		    {
		        // TODO: Insert appropriate connectors
		        JumperWire(
                    type = JumperWire_FM4,
                    con1 = [[34,43,10], [0,0,-1], 0,0,0],
                    con2 = [[11.5,31.5, -6], [0,0,-1], 0,0,0],
                    length = 70,
                    conVec1 = [1,0,0],
                    conVec2 = [0,-1,0],
                    midVec = [0.5,-1,0]
                );

                // TODO: Insert appropriate connectors
		        JumperWire(
                    type = JumperWire_FM4,
                    con1 = [[-31.5,43,10], [0,0,-1], 0,0,0],
                    con2 = [[-9,31.5, -6], [0,0,-1], 0,0,0],
                    length = 70,
                    conVec1 = [1,0,0],
                    conVec2 = [0,-1,0],
                    midVec = [0,0,-1]
                );

		    }

		// Battery assembly
		step(6, "Clip in the battery pack", "400 300 -6 7 19 64 1 212 625")
		    translate([-25, -45, 12])
			rotate([90, 0, 90])
			battery_pack_double(2, 4);

		// Power Switch

		// LED
		step(7, "Clip the LED into place", "400 300 -6 7 19 64 1 212 625")
		    translate([0, -10, BaseDiameter/2])
			LED();

		// Piezo
		step(8, "Clip the piezo sounder into place", "400 300 -6 7 19 64 1 212 625")
		    translate([-37, -32, 10])
			murata_7BB_12_9();

		// Caster
		//   Example of using attach
		// TODO: Correct ground clearance!
		step(9, "Push the caster assembly into the base so that it snaps into place",
		    "400 300 -6 7 19 115 1 26 625")
		    attach(LogoBot_Con_Caster, MarbleCastor_Con_Default, ExplodeSpacing=15)
			MarbleCasterAssembly();

		// Conditional Design Elements
		// ---------------------------

		// PenLift
		//   Placeholder of a micro servo to illustrate having conditional design elements
		if (PenLift) {
			// TODO: wrap into a PenLift sub-assembly
			step(10, "Fit the pen lift assembly", "400 300 -6 7 19 64 1 212 625")
			    attach( LogoBot_Con_PenLift, MicroServo_Con_Horn)
				MicroServo();
		}

		// Shell + fixings
		step(PenLift ? 11 : 10,
		    "Push the shell down onto the base and twist to lock into place",
		    "400 400 11 -23 65 66 0 217 1171")
	        attach(DefConDown, DefConDown, ExplodeSpacing=BaseDiameter/2)
	        BasicShell_STL();
	}

	End("LogoBot");
}

/*
	STL: LogoBotBase_STL

	The printable base plate for the robot.

	The _STL suffix denotes this is a printable part, and is used by the bulk STL generating program

	Local Frame:
		Base lies on XY plane, centered on origin, front of robot is towards y+

	Parameters:
		None

	Returns:
		Base plate, rendered and colored
*/

module LogoBotBase_STL() {

    STL("LogoBotBase");

    // turn off explosions inside STL!!!
    $Explode = false;

	// Color it as a printed plastic part
	color(PlasticColor)
	    if (UseSTL) {
	        import(str(STLPath, "LogoBotBase.stl"));
	    } else {
	        union() {
	            // Start with an extruded base plate, with all the relevant holes punched in it
                linear_extrude(BaseThickness)
                    difference() {
                        // Union together all the bits to make the plate
                        union() {
                            // Base plate
                            circle(r=BaseDiameter/2);
                        }

                        // hole for breadboard
                        attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170))
                            difference() {
                                translate([2,2,0])
                                    square([Breadboard_Width(Breadboard_170)-4, Breadboard_Depth(Breadboard_170)-4]);

                                // attach mounting points, has the effect of not differencing them
                                attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon) {
                                    circle(r=4/2 + dw);
                                    translate([0, -2 - dw, 0])
                                        square([10, 4 + 2*dw]);
                                }

                                attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon) {
                                    circle(r=4/2 + dw);
                                    rotate([0,0,180])
                                        translate([0, -2 - dw, 0])
                                        square([10, 4 + 2*dw]);
                                }
                            }

                        // mounting holes for breadboard
                        attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170)) {
                            attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon)
                                circle(r=4.3/2);

                            attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon)
                                circle(r=4.3/2);
                        }

                        // weight loss holes under the motor drivers
                        attach(LogoBot_Con_LeftMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperLeft))
                            roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);

		                // Right Motor Driver
		                attach(LogoBot_Con_RightMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperRight))
			                roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);

                        // slots for wheels
                        for (i=[0:1])
                            mirror([i,0,0])
                            translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
                            rotate([-90, 0, 90]) {
                                translate([-1,0,0])
                                    roundedSquare([WheelThickness + tw, WheelDiameter + tw], (WheelThickness + tw)/2, center=true);
                            }

                        // weight loss under battery pack
                        translate([-57/2, -45])
                            roundedSquare([57, 30], tw);

                        // Centre hole for pen
                        circle(r=PenHoleDiameter/2);

                        // Chevron at the front so we're clear where the front is!
                        translate([0, BaseDiameter/2 - 10, 0])
                            Chevron(width=10, height=6, thickness=3);

                        // Caster
                        //   Example of using attach to punch an appropriate fixing hole
                        attach(LogoBot_Con_Caster, PlasticCastor_Con_Default)
                            circle(r = connector_bore(PlasticCastor_Con_Default) / 2);

                        // A load of fixing holes around the edge...  just as an example of how to do it
                        // NB: This is one of the examples we discussed during the wk1 session
                        // TODO: Decide if we want these back...
                        *for (i=[0:9])
                            rotate([0, 0, i * 360/10])
                            translate([BaseDiameter/2 - 5, 0, 0])
                            circle(r=4.3/2);  // M4 fixing holes

                        // Remove shell fittings
                        Shell_TwistLockCutouts();
                    }

                // Now union on any other bits (i.e. sticky-up bits!)

                // clips for the motors
                LogoBotBase_MotorClips();

                LogoBotBase_MotorDriverPosts();

            }
        }
}

module LogoBotBase_STL_View() {
    echo("400 300 0 0 0 58 0 225 681");
}

module LogoBotBase_MotorClips() {
    // TODO: feed these local variables from the motor vitamin global vars
    mbr = 28/2;  // motor body radius
    mao = 8;     // motor axle offset from centre of motor body
    mth = 7;     // motor tab height

    // clips
    for (i=[0:1], j=[0,1])
        mirror([i,0,0])
        translate([BaseDiameter/2 + MotorOffsetX - 6 - j*12, 0, MotorOffsetZ + mao])
        rotate([-90, 0, 90])
        rotate([0,0, 0])
        linear_extrude(5) {
            difference() {
                union() {

                    difference() {
                        hull() {
                            // main circle
                            circle(r=mbr + dw);

                            // extension to correctly union to the base plate
                            translate([-(mbr * 1.5)/2,  MotorOffsetZ + mao -1,0])
                                square([mbr * 1.5, 1]);
                        }

                        // trim the top off
                        rotate([0,0,180 + 30])
                            sector2D(r=mbr+30, a=120);
                    }

                    // welcoming hands
                    for (k=[0,1])
                        mirror([k,0,0])
                        rotate([0,0,180+30])
                        translate([mbr, -dw, 0])
                        polygon([[1.5,0],[4,5],[3,5],[0,dw]],4);
                }

                // hollow for the motor
                circle(r=mbr);

            }
        }
}

module LogoBotBase_MotorDriverPosts() {
    // mounting posts for motor drivers
    // left driver example
    // TODO: Replace this with a loop
    // using a mirror because the layout is symmetrical
    for (i=[0,1])
        mirror([i,0,0])
        attach(
            offsetConnector(invertConnector(LogoBot_Con_LeftMotorDriver), [0,0, -LogoBot_Con_LeftMotorDriver[0][2]]),
            ULN2003DriverBoard_Con_UpperLeft)
        {
            // Now we're inside the driver boards local coordinate system, which is relative to the LowerLeft mount point.
            // To position things relative to the local frame, we need to "reverse" attach them by swapping the order
            // of the connectors.
            attach(ULN2003DriverBoard_Con_UpperLeft, ULN2003DriverBoard_Con_LowerLeft)
                LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);

            attach(ULN2003DriverBoard_Con_UpperRight, ULN2003DriverBoard_Con_LowerLeft)
                 LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);

            attach(ULN2003DriverBoard_Con_LowerRight, ULN2003DriverBoard_Con_LowerLeft)
                 LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);

            // the lower left post doesn't need translating
            LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);
        }
}

module LogoBotBase_MountingPost(r1=5/2, h1=3, r2=3/2, h2=3) {
    cylinder(r=r1, h=h1);
    translate([0,0,h1-eta])
        cylinder(r1=r2, r2=r2 * 0.7, h=h2 +eta);
}

/*
	Assembly: LogoBotAssembly

	Master assembly for the LogoBot

	Local Frame:
		Centred on the origin, such that bottom edge of wheels sit on XY plane.
		Front of the robot faces towards y+

	Parameters:
		PenLift - Set to true to include a PenLift, defaults to false

	Returns:
		Complete LogoBot model

*/

// Connectors
// ----------
// These are used within this module to layout the various vitamins/sub-assemblies
// The same connectors are used to shape associated portions of the LogoBotBase_STL

LogoBot_Con_Breadboard          = [[0, 12, 0],[0,0,-1], -90,0,0];

LogoBot_Con_LeftMotorDriver     = [[-20, 16, 3],[0,0,1],180,0,0];
LogoBot_Con_RightMotorDriver    = [[20, 16, 3],[0,0,1],180,0,0];

LogoBot_Con_Caster = [ [0, -BaseDiameter/2 + 10, 0], [0,0,1], 0, 0, 0];

LogoBot_Con_PenLift = [ [-20, -5, 10], [0,1,0], 0, 0, 0];

// Assembly
// --------

function LogoBotAssembly_NumSteps() = 10 + (PenLift ? 1 : 0);

module LogoBotAssembly ( PenLift=false ) {

    Assembly("LogoBot");

	translate([0, 0, GroundClearance]) {

		// Default Design Elements
		// -----------------------

		// Base
		LogoBotBase_STL();

        step(1,
            "Connect the breadboard assembly to the underside of the base",
            "400 300 0 17 12 112 0 222 513")
		    attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170), ExplodeSpacing=-20)
		    BreadboardAssembly();

		// Bumper assemblies (x2)
		step(2, "Connect the two bumper assemblies", "400 300 -6 7 19 64 1 212 625" )
		    for (x=[0,1], y=[0,1])
			mirror([0,y,0])
			mirror([x,0,0])
			translate([(BaseDiameter/2-10) * cos(45), (BaseDiameter/2-10) * sin(45), -8 ])
			rotate([0,0,-30])
			MicroSwitch();

		step(3, "Push the two motor drivers onto the mounting posts", "400 300 -6 7 19 64 1 212 625") {
		    // Left Motor Driver
		    attach(LogoBot_Con_LeftMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperLeft), ExplodeSpacing=-20)
			    ULN2003DriverBoard();

		    // Right Motor Driver
		    attach(LogoBot_Con_RightMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperRight), ExplodeSpacing=-20)
			    ULN2003DriverBoard();
		}

		// Motor + Wheel assemblies (x2)
		step(4, "Clip the two wheels assemblies onto the base and connect the motor leads to the the motor drivers", "400 300 -4 6 47 66 0 190 740")
            for (i=[0:1])
                mirror([i,0,0])
                translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
                translate([-15,0,0])
                attach(DefConDown, DefConDown, ExplodeSpacing = 40)
                translate([15,0,0])
                rotate([-90, 0, 90]) {
                    assign($rightSide= i == 0? 1 : 0)
                        WheelAssembly();
                }

		// Connect jumper wires
		step(5,
		    "Connect the jumper wires between the motor drivers and the Arduino",
		    "400 300 9 26 54 38 0 161 766")
		    {
		        // TODO: Insert appropriate connectors
		        JumperWire(
                    type = JumperWire_FM4,
                    con1 = [[34,43,10], [0,0,-1], 0,0,0],
                    con2 = [[11.5,31.5, -6], [0,0,-1], 0,0,0],
                    length = 70,
                    conVec1 = [1,0,0],
                    conVec2 = [0,-1,0],
                    midVec = [0.5,-1,0]
                );

                // TODO: Insert appropriate connectors
		        JumperWire(
                    type = JumperWire_FM4,
                    con1 = [[-31.5,43,10], [0,0,-1], 0,0,0],
                    con2 = [[-9,31.5, -6], [0,0,-1], 0,0,0],
                    length = 70,
                    conVec1 = [1,0,0],
                    conVec2 = [0,-1,0],
                    midVec = [0,0,-1]
                );

		    }

		// Battery assembly
		step(6, "Clip in the battery pack", "400 300 -6 7 19 64 1 212 625")
		    translate([-25, -45, 12])
			rotate([90, 0, 90])
			battery_pack_double(2, 4);

		// Power Switch

		// LED
		step(7, "Clip the LED into place", "400 300 -6 7 19 64 1 212 625")
		    translate([0, -10, BaseDiameter/2])
			LED();

		// Piezo
		step(8, "Clip the piezo sounder into place", "400 300 -6 7 19 64 1 212 625")
		    translate([-37, -32, 10])
			murata_7BB_12_9();

		// Caster
		//   Example of using attach
		// TODO: Correct ground clearance!
		step(9, "Push the caster assembly into the base so that it snaps into place",
		    "400 300 -6 7 19 115 1 26 625")
		    attach(LogoBot_Con_Caster, MarbleCastor_Con_Default, Explode=Explode, ExplodeSpacing=15)
			MarbleCasterAssembly();

		// Conditional Design Elements
		// ---------------------------

		// PenLift
		//   Placeholder of a micro servo to illustrate having conditional design elements
		if (PenLift) {
			// TODO: wrap into a PenLift sub-assembly
			step(10, "Fit the pen lift assembly", "400 300 -6 7 19 64 1 212 625")
			    attach( LogoBot_Con_PenLift, MicroServo_Con_Horn)
				MicroServo();
		}

		// Shell + fixings
		step(PenLift ? 11 : 10,
		    "Push the shell down onto the base and twist to lock into place",
		    "400 400 11 -23 65 66 0 217 1171")
	        attach(DefConDown, DefConDown, ExplodeSpacing=BaseDiameter/2)
	        BasicShell_STL();
	}

	End("LogoBot");
}

/*
	STL: LogoBotBase_STL

	The printable base plate for the robot.

	The _STL suffix denotes this is a printable part, and is used by the bulk STL generating program

	Local Frame:
		Base lies on XY plane, centered on origin, front of robot is towards y+

	Parameters:
		None

	Returns:
		Base plate, rendered and colored
*/

module LogoBotBase_STL() {

    STL("LogoBotBase");

    // turn off explosions inside STL!!!
    $Explode = false;

	// Color it as a printed plastic part
	color(PlasticColor)
	    if (UseSTL) {
	        import(str(STLPath, "LogoBotBase.stl"));
	    } else {
	        union() {
	            // Start with an extruded base plate, with all the relevant holes punched in it
                linear_extrude(BaseThickness)
                    difference() {
                        // Union together all the bits to make the plate
                        union() {
                            // Base plate
                            circle(r=BaseDiameter/2);
                        }

                        // hole for breadboard
                        attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170))
                            difference() {
                                translate([2,2,0])
                                    square([Breadboard_Width(Breadboard_170)-4, Breadboard_Depth(Breadboard_170)-4]);

                                // attach mounting points, has the effect of not differencing them
                                attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon) {
                                    circle(r=4/2 + dw);
                                    translate([0, -2 - dw, 0])
                                        square([10, 4 + 2*dw]);
                                }

                                attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon) {
                                    circle(r=4/2 + dw);
                                    rotate([0,0,180])
                                        translate([0, -2 - dw, 0])
                                        square([10, 4 + 2*dw]);
                                }
                            }

                        // mounting holes for breadboard
                        attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170)) {
                            attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon)
                                circle(r=4.3/2);

                            attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon)
                                circle(r=4.3/2);
                        }

                        // weight loss holes under the motor drivers
                        attach(LogoBot_Con_LeftMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperLeft))
                            roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);

		                // Right Motor Driver
		                attach(LogoBot_Con_RightMotorDriver, invertConnector(ULN2003DriverBoard_Con_UpperRight))
			                roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);

                        // slots for wheels
                        for (i=[0:1])
                            mirror([i,0,0])
                            translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
                            rotate([-90, 0, 90]) {
                                translate([-1,0,0])
                                    roundedSquare([WheelThickness + tw, WheelDiameter + tw], (WheelThickness + tw)/2, center=true);
                            }

                        // weight loss under battery pack
                        translate([-57/2, -45])
                            roundedSquare([57, 30], tw);

                        // Centre hole for pen
                        circle(r=PenHoleDiameter/2);

                        // Chevron at the front so we're clear where the front is!
                        translate([0, BaseDiameter/2 - 10, 0])
                            Chevron(width=10, height=6, thickness=3);

                        // Caster
                        //   Example of using attach to punch an appropriate fixing hole
                        attach(LogoBot_Con_Caster, PlasticCastor_Con_Default)
                            circle(r = connector_bore(PlasticCastor_Con_Default) / 2);

                        // A load of fixing holes around the edge...  just as an example of how to do it
                        // NB: This is one of the examples we discussed during the wk1 session
                        // TODO: Decide if we want these back...
                        *for (i=[0:9])
                            rotate([0, 0, i * 360/10])
                            translate([BaseDiameter/2 - 5, 0, 0])
                            circle(r=4.3/2);  // M4 fixing holes

                        // Remove shell fittings
                        Shell_TwistLockCutouts();
                    }

                // Now union on any other bits (i.e. sticky-up bits!)

                // clips for the motors
                LogoBotBase_MotorClips();

                LogoBotBase_MotorDriverPosts();

            }
        }
}

module LogoBotBase_STL_View() {
    echo("400 300 0 0 0 58 0 225 681");
}

module LogoBotBase_MotorClips() {
    // TODO: feed these local variables from the motor vitamin global vars
    mbr = 28/2;  // motor body radius
    mao = 8;     // motor axle offset from centre of motor body
    mth = 7;     // motor tab height

    // clips
    for (i=[0:1], j=[0,1])
        mirror([i,0,0])
        translate([BaseDiameter/2 + MotorOffsetX - 6 - j*12, 0, MotorOffsetZ + mao])
        rotate([-90, 0, 90])
        rotate([0,0, 0])
        linear_extrude(5) {
            difference() {
                union() {

                    difference() {
                        hull() {
                            // main circle
                            circle(r=mbr + dw);

                            // extension to correctly union to the base plate
                            translate([-(mbr * 1.5)/2,  MotorOffsetZ + mao -1,0])
                                square([mbr * 1.5, 1]);
                        }

                        // trim the top off
                        rotate([0,0,180 + 30])
                            sector2D(r=mbr+30, a=120);
                    }

                    // welcoming hands
                    for (k=[0,1])
                        mirror([k,0,0])
                        rotate([0,0,180+30])
                        translate([mbr, -dw, 0])
                        polygon([[1.5,0],[4,5],[3,5],[0,dw]],4);
                }

                // hollow for the motor
                circle(r=mbr);

            }
        }
}

module LogoBotBase_MotorDriverPosts() {
    // mounting posts for motor drivers
    // left driver example
    // TODO: Replace this with a loop
    // using a mirror because the layout is symmetrical
    for (i=[0,1])
        mirror([i,0,0])
        attach(
            offsetConnector(invertConnector(LogoBot_Con_LeftMotorDriver), [0,0, -LogoBot_Con_LeftMotorDriver[0][2]]),
            ULN2003DriverBoard_Con_UpperLeft)
        {
            // Now we're inside the driver boards local coordinate system, which is relative to the LowerLeft mount point.
            // To position things relative to the local frame, we need to "reverse" attach them by swapping the order
            // of the connectors.
            attach(ULN2003DriverBoard_Con_UpperLeft, ULN2003DriverBoard_Con_LowerLeft)
                LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);

            attach(ULN2003DriverBoard_Con_UpperRight, ULN2003DriverBoard_Con_LowerLeft)
                 LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);

            attach(ULN2003DriverBoard_Con_LowerRight, ULN2003DriverBoard_Con_LowerLeft)
                 LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);

            // the lower left post doesn't need translating
            LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_LeftMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);
        }
}

module LogoBotBase_MountingPost(r1=5/2, h1=3, r2=3/2, h2=3) {
    cylinder(r=r1, h=h1);
    translate([0,0,h1-eta])
        cylinder(r1=r2, r2=r2 * 0.7, h=h2 +eta);
}


Changing roof

Changing roof

shed_length = 5184;
shed_width = 1829+305/2;
shed_front_height = 2350;
shed_front_back = 2135;
base_timber = 47;
timber_construct_w = 38;
timber_construct_h = 63;
base_sheet_t = 18;

module cubeI(size,center=false)
{
    echo("Dim: ",size);
    cube(size, center=center);
}

module shedBaseSection(x,y,timber_width=75,timber_length=47, braces=0)
{
    echo("shedBaseSection: x,y,timber_width,timber_length, braces ", x,y,timber_width,timber_length, braces);
    echo("http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47X75-x-3000mm-Single/p/107186#tab-DynamicDeliveryWebTab_content");
    cubeI([x,timber_width,timber_length]);
    translate([0,y-timber_width,0]) cubeI([x,timber_width,timber_length]);
    translate([0,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    translate([x-timber_width,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    if(braces > 0)
    {
        middle_y = y - timber_width*2;
        part_y = middle_y/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,timber_width/(braces+1) + (loop*part_y),0]) cubeI([x-timber_width*2, timber_width,timber_length]);
        }
    }
}

module shedBase(x,y,number_x,number_y, braces=1)
{
    for(base_y = [0 : number_y-1])
    {
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),base_y*(y/number_y),0]) shedBaseSection(x/number_x,y/number_y, braces = braces);
            }
    }
}

module floorMembrane(x,y,thickness,color="Black", overlap=10)
{
    echo("x,y,thickness=1,color, overlap=1",x,y,thickness,color, overlap);
    echo("http://www.screwfix.com/p/dmp-damp-proof-membrane-black-1000ga-3-x-4m/50464#product_additional_details_container");
    translate([-overlap,-overlap,0]) color(color, 0.5) cube([x+overlap*2,y+overlap*2, thickness]);
}

module breatherMembrane(x,y, thickness=0.5)
{
    // google timber frame breather membrane
    rotate([90,0,0])  floorMembrane(x,y,thickness=thickness,color="Black", overlap=thickness);
}

module breatherMembraneSide(x,y, thickness=0.5)
{
    // google timber frame breather membrane

    opp = y-shed_front_back;
    adj = x - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    echo("Degrees: ", theta);
    translate([0,0,0]) rotate([90,0,90]) difference()
    {
        floorMembrane(x,y,thickness=thickness,color="Black", overlap=thickness);
        translate([0,y,-opp/2]) rotate([0,0,-theta]) cube([x+10,opp,opp]);
    }
}

module breatherMembraneFront(x,y, thickness, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h)
{
    echo ("lll", x,y, thickness, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h);
    translate([0,0,0]) rotate([90,0,0]) difference()
    {
        floorMembrane(x,y,thickness=thickness,color="Black", overlap=thickness);
        translate([space_to_window_w,space_to_window_h,-100]) rotate([0,0,0]) cube([window_w,window_h,200]);
        translate([space_to_window_w*2+window_w,0,-100]) cube([door_w,door_h,200]);
    }
}

//module shedLowerFloor(x,y, panel_slf_w = 607, panel_slf_l =   1829, panel_slf_t =   5.5, panel_min_dimentions = 100)
module shedLowerFloor(x,y, panel_slf_w = 1220, panel_slf_l = 2440, panel_slf_t = base_sheet_t, panel_min_dimentions = 100)
{
//    echo("http://www.wickes.co.uk/Wickes-Non-Structural-Hardwood-Plywood-5-5x607x1829mm/p/111196");
    echo("http://www.wickes.co.uk/Wickes-General-Purpose-OSB3-Board-18x1220x2440mm/p/110517");
    x_panels = round(-0.5+x/panel_slf_w);
    y_panels = round(-0.5+y/panel_slf_l);
    echo(y_panels);
    if(y_panels>0) // Panel is Not longer than width
    {
        for(base_y = [0 : y_panels-1]) //Truncate not round
        {
            for(base_x = [0 : x_panels-1])
            {
                translate([base_x*panel_slf_w,base_y*panel_slf_l,0]) cubeI([panel_slf_w, panel_slf_l,panel_slf_t]);
                if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
                translate([base_x*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([panel_slf_w, y-(panel_slf_l*y_panels),panel_slf_t]);
                }
            if(x-(panel_slf_w*x_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,(y_panels-1)*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), panel_slf_l,panel_slf_t]);
            if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), y-(panel_slf_l*y_panels),panel_slf_t]);
        }
    }
    else // Panel is longer than width
    {
     for(base_x = [0 : x_panels-1])
     {
        translate([base_x*panel_slf_w,0,0]) cubeI([panel_slf_w, y,panel_slf_t]);
     }
     if(x-(panel_slf_w*x_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,0,0]) cubeI([x-(panel_slf_w*x_panels), y,panel_slf_t]);
    }
}

module verticalStruts(x,z,timber_width=timber_construct_w,timber_length=timber_construct_h, braces=0, beams=0)
{
//    echo("shedBaseSection(x,z,timber_width,timber_length, braces",x,z,timber_width,timber_length, braces);
//    echo("Walls - http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47-x-47-x-2400mm-Pack-6/p/107114");
    cubeI([x,timber_length,timber_width]);
    translate([0,0,z-timber_width]) cubeI([x,timber_length,timber_width]);
    translate([0,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    translate([x-timber_width,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    if(braces > 0)
    {
        middle_z = z - timber_width*2;
        part_z = middle_z/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,0,timber_width/(braces+1) + (loop*part_z)]) cubeI([x-timber_width*2,timber_length, timber_width]);
        }
    }
    if(beams> 0)
    {
        middle_x = x - timber_length*2;
        part_x = middle_x/(beams+1);
        for(loop=[1 : beams ])
        {
            translate([timber_length/(beams+1) + (loop*part_x),0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
        }
    }
}

module backWallStruts(x,z,number_x, braces=1)
{
    echo("backWallStruts(x,z,number_x, braces)",x,z,number_x, braces);
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),0,0]) verticalStruts(x/number_x,z, braces = braces);
            }
}

module sideWallStruts(x,z,number_x, braces=1)
{
    echo("sideWallStruts(x,z,number_x, braces)",x,z,number_x, braces);
    rotate([0,0,90]) backWallStruts(x,z,number_x, braces);
}

module prism(l, w, h)
{
    polyhedron(
        points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
        faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]
        );
}

module cadding(x, board_height, board_width_b, board_width_s, degrees)
{
    translate([0, -board_width_b, 0]) color("Peru",0.6) rotate([-degrees,0,0])
    {
        cube([x, board_width_b - board_width_s, board_height]);
        translate([0,- board_width_s,0]) prism(x, board_width_s, board_height);
    }
}

module caddings(x, z, board_height = 150, board_width_b = 11, board_width_s = 6, overlap = 34, degrees=3)
{
    panel_uplift = board_height - overlap;
    panels = round(-0.5+z/panel_uplift);
    for(p = [0 : panels-1])
    {
        translate([0,0,panel_uplift * p])
         cadding(x, board_height, board_width_b, board_width_s, degrees);
    }
}

module claddingBackWall(x,z)
{
    translate([x,0,0]) rotate([0,0,180]) caddings(x, z);
}

module claddingLeftSideWall(x,z)
{
    opp = z-shed_front_back;
    adj = x - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    echo("Degrees: ", theta);
    translate([0,x,0]) rotate([0,0,270]) difference()
    {
        caddings(x, z);
      translate([0,-opp/2,z-opp]) rotate([0,-theta,0]) cube([x+10,opp,opp]);
    }
}

module claddingRightSideWall(x,z)
{
    opp = z-shed_front_back;
    adj = x - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    echo("Degrees: ", theta);
    translate([0,0,0]) rotate([0,0,90]) difference()
    {
        caddings(x, z);
        translate([0,-opp/2,z]) rotate([0,theta,0]) cube([x+10,opp,opp]);
    }
}

module claddingFrontWall(x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h)
{
    translate([0,0,0]) rotate([0,0,0]) difference()
    {
        caddings(x, z);
        translate([space_to_window_w,-100,space_to_window_h]) rotate([0,0,0]) cube([window_w,200,window_h]);
        translate([space_to_window_w*2+window_w,-100,0]) rotate([0,0,0]) cube([door_w,200,door_h]);
    }
}

module frontWallStruts(x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h, braces)
{
    echo("frontWallStruts(x,z,space_to_window_w, space_to_window_h, window_w, window_h, door_w, braces)", x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h, braces);
    translate([0,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    translate([space_to_window_w,0,0]) verticalStruts(window_w,space_to_window_h, braces = braces, beams=1);
    translate([0,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,z-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2,0,space_to_window_h+window_h]) verticalStruts(window_w+space_to_window_w,z-(space_to_window_h+window_h), braces = braces, beams=2);
    translate([space_to_window_w+window_w,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    if(door_h-(space_to_window_h+window_h>0))  translate([space_to_window_w/2+window_w+space_to_window_w,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,door_h-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2+window_w+space_to_window_w,0,door_h]) verticalStruts(x-(space_to_window_w/2+window_w+space_to_window_w),z-door_h, braces = braces, beams=2);
    translate([space_to_window_w+window_w+space_to_window_w+door_w,0,0]) verticalStruts(x-(space_to_window_w+window_w+space_to_window_w+door_w),door_h, braces = braces);
}

module roofJoist(y,timber_width=75,timber_length=47)
{
    cubeI([timber_width,y,timber_length]);
}

module roofJoists(x,y,number_x,timber_width=75,timber_length=47)
{
    x_inside = x - timber_length*2 - timber_width;
    x_inside_part = x_inside / number_x;
    for(base_x = [0 : number_x])
       {
        translate([timber_length+(x_inside_part*base_x),0,-timber_length])  roofJoist(y);
       }
}

module roof(x,y,z_front,z_back, panel_slf_w = 1220, panel_slf_l = 2440, panel_slf_t = base_sheet_t)
{
    opp = z_front-z_back;
    adj = y - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    overhang = (panel_slf_l-hyp)/2;
    overhang_flat = cos(theta) * overhang;
    overhang_up = sin(theta) * overhang;

    // Want it to line up with front of timber truct not back
    height_adjust = base_timber * tan(theta);
    echo("overhang", overhang, opp, hyp, theta, height_adjust, overhang_flat, overhang_up);
    translate([-overhang_flat,-overhang_flat,z_front+base_timber+panel_slf_t+height_adjust+overhang_up]) rotate([-theta,0,0])
    {
        shedLowerFloor(x+overhang_flat*2,panel_slf_l);
        translate([overhang,overhang,0]) roofJoists(x,y,7 );
    }
}

module insulate(space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h, thickness=50, colour="White")
{
    translate([0,shed_width-thickness,0]) color(colour,0.5) cube([shed_length, thickness, shed_front_back]);

    opp = shed_front_height-shed_front_back;
    adj = shed_width - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    echo("Degrees: ", theta);
    translate([0,0,0]) rotate([0,0,90]) difference()
    {
        translate([0,-thickness,0]) color(colour,0.5) cube([shed_width, thickness, shed_front_back]);
        translate([0,-opp/2,shed_front_height]) rotate([0,theta,0]) cube([shed_width+10,opp,opp]);
    }
    translate([shed_length-thickness,0,0]) rotate([0,0,90]) difference()
    {
        translate([0,-thickness,0]) color(colour,0.5) cube([shed_width, thickness, shed_front_back]);
        translate([0,-opp/2,shed_front_height]) rotate([0,theta,0]) cube([shed_width+10,opp,opp]);
    }

    translate([0,0,0]) rotate([0,0,0]) difference()
    {
    translate([0,0,0]) color(colour,0.5) cube([shed_length, thickness, shed_front_height]);
        translate([space_to_window_w,-100,space_to_window_h]) rotate([0,0,0]) cube([window_w,200,window_h]);
        translate([space_to_window_w*2+window_w,-100,0]) rotate([0,0,0]) cube([door_w,200,door_h]);
    }

}

module restOfGarden(offset_x=-381, offset_y=+381 + shed_width)
{
    garden_width= 5947.4;
    garden_length_shed_space = 2440;
    translate([offset_x, offset_y,0])
    {
        color("Green") cube([garden_width, 10, 1830]);
        translate([0,-garden_length_shed_space-381,0]) color("Green") cube([10, garden_length_shed_space+381, 1830]);
        translate([garden_width,-garden_length_shed_space,0]) color("Green") cube([10, garden_length_shed_space, 1830]);
    }
    translate([-381,-3507.5-2592.5-381,0]) color("Green") cube([10, +3507.5+2592.5+381, 1830]);
//    translate([garden_width-381,-3507.5-2592.5-381,0]) color("Green") cube([10, +3507.5+2592.5+381, 1830]);

    translate([-381+0.1,-3507.5-381,0]) color("red") cube([3202.5, 3507.5, 400]);
    translate([-381+0.1,-3507.5-2592.5-381,0]) color("red") cube([4727.5,2592.5, 400]);

    }

annimate_step = 1/20;

if($t>annimate_step*1)
    shedBase(shed_length, shed_width, number_x=5,number_y=1, braces=1);

if($t>annimate_step*2)
    translate([0,0,base_timber]) floorMembrane(shed_length, shed_width, thickness = 1);

if($t>annimate_step*3)
    translate([0,0,base_timber+1]) shedLowerFloor(shed_length, shed_width);

if($t>annimate_step*4)
    translate([0,shed_width-timber_construct_h,base_timber+1+base_sheet_t])  backWallStruts(shed_length, shed_front_back, 5);
if($t>annimate_step*5)
    translate([timber_construct_h,timber_construct_h,base_timber+1+base_sheet_t])  sideWallStruts(shed_width-timber_construct_h*2,shed_front_back,2, braces=1);

if($t>annimate_step*6)
    translate([shed_length,timber_construct_h,base_timber+1+base_sheet_t])  sideWallStruts(shed_width-timber_construct_h*2,shed_front_back,2, braces=1);

if($t>annimate_step*7)
    translate([0,0,base_timber+1+base_sheet_t])  frontWallStruts(shed_length,shed_front_height, space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, door_h=2030, braces=0);

if($t>annimate_step*8)
    roof(shed_length, shed_width,shed_front_height,shed_front_back);

if($t>annimate_step*9)
    translate([0,shed_width+0.51,base_timber])
        breatherMembrane(shed_length, shed_front_back);

if($t>annimate_step*10)
    translate([-0.51,0,base_timber])
        breatherMembraneSide(shed_width, shed_front_height);

if($t>annimate_step*11)
    translate([shed_length+0.5,0,base_timber])
        breatherMembraneSide(shed_width, shed_front_height);

if($t>annimate_step*12)
    translate([0,-0.5,timber_construct_h])
        breatherMembraneFront(shed_length,shed_front_height, thickness=0.5,  space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, door_h=2030);

//shedBaseSection(shed_length/6,shed_width, braces = 1);
if($t>annimate_step*13)
    translate([0,shed_width+3,timber_construct_h])
        claddingBackWall(shed_length, shed_front_back);

if($t>annimate_step*14)
    translate([-3,0,timber_construct_h])
        claddingLeftSideWall(shed_width, shed_front_height);

if($t>annimate_step*15)
    translate([shed_length+3,0,timber_construct_h])
        claddingRightSideWall(shed_width, shed_front_height);

if($t>annimate_step*16)
    translate([-3,0,timber_construct_h])
        claddingFrontWall(shed_length,shed_front_height,  space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, door_h=2030);

if($t>annimate_step*16)
    translate([0,0,timber_construct_h])
        insulate(space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, door_h=2030);

//restOfGarden();

shed_length = 5184;
shed_width = 1829+305/2;
shed_front_height = 2350;
shed_front_back = 2135;
base_timber = 47;
timber_construct_w = 38;
timber_construct_h = 63;
base_sheet_t = 18;

module cubeI(size,center=false)
{
    echo("Dim: ",size);
    cube(size, center=center);
}

module shedBaseSection(x,y,timber_width=75,timber_length=47, braces=0)
{
    echo("shedBaseSection: x,y,timber_width,timber_length, braces ", x,y,timber_width,timber_length, braces);
    echo("http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47X75-x-3000mm-Single/p/107186#tab-DynamicDeliveryWebTab_content");
    cubeI([x,timber_width,timber_length]);
    translate([0,y-timber_width,0]) cubeI([x,timber_width,timber_length]);
    translate([0,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    translate([x-timber_width,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    if(braces > 0)
    {
        middle_y = y - timber_width*2;
        part_y = middle_y/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,timber_width/(braces+1) + (loop*part_y),0]) cubeI([x-timber_width*2, timber_width,timber_length]);
        }
    }
}

module shedBase(x,y,number_x,number_y, braces=1)
{
    for(base_y = [0 : number_y-1])
    {
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),base_y*(y/number_y),0]) shedBaseSection(x/number_x,y/number_y, braces = braces);
            }
    }
}

module floorMembrane(x,y,thickness,color="Black", overlap=10)
{
    echo("x,y,thickness=1,color, overlap=1",x,y,thickness,color, overlap);
    echo("http://www.screwfix.com/p/dmp-damp-proof-membrane-black-1000ga-3-x-4m/50464#product_additional_details_container");
    translate([-overlap,-overlap,0]) color(color, 0.5) cube([x+overlap*2,y+overlap*2, thickness]);
}

module breatherMembrane(x,y, thickness=0.5)
{
    // google timber frame breather membrane
    rotate([90,0,0])  floorMembrane(x,y,thickness=thickness,color="Black", overlap=thickness);
}

module breatherMembraneSide(x,y, thickness=0.5)
{
    // google timber frame breather membrane

    opp = y-shed_front_back;
    adj = x - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    echo("Degrees: ", theta);
    translate([0,0,0]) rotate([90,0,90]) difference()
    {
        floorMembrane(x,y,thickness=thickness,color="Black", overlap=thickness);
        translate([0,y,-opp/2]) rotate([0,0,-theta]) cube([x+10,opp,opp]);
    }
}

module breatherMembraneFront(x,y, thickness, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h)
{
    echo ("lll", x,y, thickness, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h);
    translate([0,0,0]) rotate([90,0,0]) difference()
    {
        floorMembrane(x,y,thickness=thickness,color="Black", overlap=thickness);
        translate([space_to_window_w,space_to_window_h,-100]) rotate([0,0,0]) cube([window_w,window_h,200]);
        translate([space_to_window_w*2+window_w,0,-100]) cube([door_w,door_h,200]);
    }
}

//module shedLowerFloor(x,y, panel_slf_w = 607, panel_slf_l =   1829, panel_slf_t =   5.5, panel_min_dimentions = 100)
module shedLowerFloor(x,y, panel_slf_w = 1220, panel_slf_l = 2440, panel_slf_t = base_sheet_t, panel_min_dimentions = 100)
{
//    echo("http://www.wickes.co.uk/Wickes-Non-Structural-Hardwood-Plywood-5-5x607x1829mm/p/111196");
    echo("http://www.wickes.co.uk/Wickes-General-Purpose-OSB3-Board-18x1220x2440mm/p/110517");
    x_panels = round(-0.5+x/panel_slf_w);
    y_panels = round(-0.5+y/panel_slf_l);
    echo(y_panels);
    if(y_panels>0) // Panel is Not longer than width
    {
        for(base_y = [0 : y_panels-1]) //Truncate not round
        {
            for(base_x = [0 : x_panels-1])
            {
                translate([base_x*panel_slf_w,base_y*panel_slf_l,0]) cubeI([panel_slf_w, panel_slf_l,panel_slf_t]);
                if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
                translate([base_x*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([panel_slf_w, y-(panel_slf_l*y_panels),panel_slf_t]);
                }
            if(x-(panel_slf_w*x_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,(y_panels-1)*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), panel_slf_l,panel_slf_t]);
            if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), y-(panel_slf_l*y_panels),panel_slf_t]);
        }
    }
    else // Panel is longer than width
    {
     for(base_x = [0 : x_panels-1])
     {
        translate([base_x*panel_slf_w,0,0]) cubeI([panel_slf_w, y,panel_slf_t]);
     }
     if(x-(panel_slf_w*x_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,0,0]) cubeI([x-(panel_slf_w*x_panels), y,panel_slf_t]);
    }
}

module verticalStruts(x,z,timber_width=timber_construct_w,timber_length=timber_construct_h, braces=0, beams=0)
{
//    echo("shedBaseSection(x,z,timber_width,timber_length, braces",x,z,timber_width,timber_length, braces);
//    echo("Walls - http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47-x-47-x-2400mm-Pack-6/p/107114");
    cubeI([x,timber_length,timber_width]);
    translate([0,0,z-timber_width]) cubeI([x,timber_length,timber_width]);
    translate([0,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    translate([x-timber_width,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    if(braces > 0)
    {
        middle_z = z - timber_width*2;
        part_z = middle_z/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,0,timber_width/(braces+1) + (loop*part_z)]) cubeI([x-timber_width*2,timber_length, timber_width]);
        }
    }
    if(beams> 0)
    {
        middle_x = x - timber_length*2;
        part_x = middle_x/(beams+1);
        for(loop=[1 : beams ])
        {
            translate([timber_length/(beams+1) + (loop*part_x),0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
        }
    }
}

module backWallStruts(x,z,number_x, braces=1)
{
    echo("backWallStruts(x,z,number_x, braces)",x,z,number_x, braces);
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),0,0]) verticalStruts(x/number_x,z, braces = braces);
            }
}

module sideWallStruts(x,z,number_x, braces=1)
{
    echo("sideWallStruts(x,z,number_x, braces)",x,z,number_x, braces);
    rotate([0,0,90]) backWallStruts(x,z,number_x, braces);
}

module prism(l, w, h)
{
    polyhedron(
        points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
        faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]
        );
}

module cadding(x, board_height, board_width_b, board_width_s, degrees)
{
    translate([0, -board_width_b, 0]) color("Peru",0.6) rotate([-degrees,0,0])
    {
        cube([x, board_width_b - board_width_s, board_height]);
        translate([0,- board_width_s,0]) prism(x, board_width_s, board_height);
    }
}

module caddings(x, z, board_height = 150, board_width_b = 11, board_width_s = 6, overlap = 25, degrees=3)
{
    panel_uplift = board_height - overlap;
    panels = round(-0.5+z/panel_uplift);
    for(p = [0 : panels-1])
    {
        translate([0,0,panel_uplift * p])
         cadding(x, board_height, board_width_b, board_width_s, degrees);
    }
}

module claddingBackWall(x,z)
{
    translate([x,0,0]) rotate([0,0,180]) caddings(x, z);
}

module claddingLeftSideWall(x,z)
{
    opp = z-shed_front_back;
    adj = x - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    echo("Degrees: ", theta);
    translate([0,x,0]) rotate([0,0,270]) difference()
    {
        caddings(x, z);
      translate([0,-opp/2,z-opp]) rotate([0,-theta,0]) cube([x+10,opp,opp]);
    }
}

module claddingRightSideWall(x,z)
{
    opp = z-shed_front_back;
    adj = x - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    echo("Degrees: ", theta);
    translate([0,0,0]) rotate([0,0,90]) difference()
    {
        caddings(x, z);
        translate([0,-opp/2,z]) rotate([0,theta,0]) cube([x+10,opp,opp]);
    }
}

module claddingFrontWall(x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h)
{
    translate([0,0,0]) rotate([0,0,0]) difference()
    {
        caddings(x, z);
        translate([space_to_window_w,-100,space_to_window_h]) rotate([0,0,0]) cube([window_w,200,window_h]);
        translate([space_to_window_w*2+window_w,-100,0]) rotate([0,0,0]) cube([door_w,200,door_h]);
    }
}

module frontWallStruts(x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h, braces)
{
    echo("frontWallStruts(x,z,space_to_window_w, space_to_window_h, window_w, window_h, door_w, braces)", x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h, braces);
    translate([0,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    translate([space_to_window_w,0,0]) verticalStruts(window_w,space_to_window_h, braces = braces, beams=1);
    translate([0,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,z-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2,0,space_to_window_h+window_h]) verticalStruts(window_w+space_to_window_w,z-(space_to_window_h+window_h), braces = braces, beams=2);
    translate([space_to_window_w+window_w,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    if(door_h-(space_to_window_h+window_h>0))  translate([space_to_window_w/2+window_w+space_to_window_w,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,door_h-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2+window_w+space_to_window_w,0,door_h]) verticalStruts(x-(space_to_window_w/2+window_w+space_to_window_w),z-door_h, braces = braces, beams=2);
    translate([space_to_window_w+window_w+space_to_window_w+door_w,0,0]) verticalStruts(x-(space_to_window_w+window_w+space_to_window_w+door_w),door_h, braces = braces);
}

module roofJoist(y,timber_width=75,timber_length=47)
{
    cubeI([timber_width,y,timber_length]);
}

module roofJoists(x,y,number_x,timber_width=75,timber_length=47)
{
    x_inside = x - timber_length*2 - timber_width;
    x_inside_part = x_inside / number_x;
    for(base_x = [0 : number_x])
       {
        translate([timber_length+(x_inside_part*base_x),0,-timber_length])  roofJoist(y);
       }
}

module roof(x,y,z_front,z_back, panel_slf_w = 1220, panel_slf_l = 2440, panel_slf_t = base_sheet_t)
{
    opp = z_front-z_back;
    adj = y - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    overhang = (panel_slf_l-hyp)/2;
    overhang_flat = cos(theta) * overhang;
    overhang_up = sin(theta) * overhang;

    // Want it to line up with front of timber truct not back
    height_adjust = base_timber * tan(theta);
    echo("overhang", overhang, opp, hyp, theta, height_adjust, overhang_flat, overhang_up);
    translate([-overhang_flat,-overhang_flat,z_front+base_timber+panel_slf_t+height_adjust+overhang_up]) rotate([-theta,0,0])
    {
        shedLowerFloor(x+overhang_flat*2,panel_slf_l);
        translate([overhang,overhang,0]) roofJoists(x,y,7 );
    }
}

module restOfGarden(offset_x=-381, offset_y=+381 + shed_width)
{
    garden_width= 5947.4;
    garden_length_shed_space = 2440;
    translate([offset_x, offset_y,0])
    {
        color("Green") cube([garden_width, 10, 1830]);
        translate([0,-garden_length_shed_space-381,0]) color("Green") cube([10, garden_length_shed_space+381, 1830]);
        translate([garden_width,-garden_length_shed_space,0]) color("Green") cube([10, garden_length_shed_space, 1830]);
    }
    translate([-381,-3507.5-2592.5-381,0]) color("Green") cube([10, +3507.5+2592.5+381, 1830]);
//    translate([garden_width-381,-3507.5-2592.5-381,0]) color("Green") cube([10, +3507.5+2592.5+381, 1830]);

    translate([-381+0.1,-3507.5-381,0]) color("red") cube([3202.5, 3507.5, 400]);
    translate([-381+0.1,-3507.5-2592.5-381,0]) color("red") cube([4727.5,2592.5, 400]);

    }

annimate_step = 1/20;

if($t>annimate_step*1)
    shedBase(shed_length, shed_width, number_x=5,number_y=1, braces=1);

if($t>annimate_step*2)
    translate([0,0,base_timber]) floorMembrane(shed_length, shed_width, thickness = 1);

if($t>annimate_step*3)
    translate([0,0,base_timber+1]) shedLowerFloor(shed_length, shed_width);

if($t>annimate_step*4)
    translate([0,shed_width-timber_construct_h,base_timber+1+base_sheet_t])  backWallStruts(shed_length, shed_front_back, 5);
if($t>annimate_step*5)
    translate([timber_construct_h,timber_construct_h,base_timber+1+base_sheet_t])  sideWallStruts(shed_width-timber_construct_h*2,shed_front_back,2, braces=1);

if($t>annimate_step*6)
    translate([shed_length,timber_construct_h,base_timber+1+base_sheet_t])  sideWallStruts(shed_width-timber_construct_h*2,shed_front_back,2, braces=1);

if($t>annimate_step*7)
    translate([0,0,base_timber+1+base_sheet_t])  frontWallStruts(shed_length,shed_front_height, space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, door_h=2030, braces=0);

if($t>annimate_step*8)
    roof(shed_length, shed_width,shed_front_height,shed_front_back);

if($t>annimate_step*9)
    translate([0,shed_width+0.5,base_timber])
        breatherMembrane(shed_length, shed_front_back);

if($t>annimate_step*10)
    translate([-0.5,0,base_timber])
        breatherMembraneSide(shed_width, shed_front_height);

if($t>annimate_step*11)
    translate([shed_length+3,0,base_timber])
        breatherMembraneSide(shed_width, shed_front_height);

if($t>annimate_step*12)
    translate([-3,0,base_timber])
        breatherMembraneFront(shed_length,shed_front_height, thickness=0.5,  space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, door_h=2030);

//shedBaseSection(shed_length/6,shed_width, braces = 1);
if($t>annimate_step*13)
    translate([0,shed_width+3,base_timber])
        claddingBackWall(shed_length, shed_front_back);

if($t>annimate_step*14)
    translate([-3,0,base_timber])
        claddingLeftSideWall(shed_width, shed_front_height);

if($t>annimate_step*15)
    translate([shed_length+3,0,base_timber])
        claddingRightSideWall(shed_width, shed_front_height);

if($t>annimate_step*16)
    translate([-3,0,base_timber])
        claddingFrontWall(shed_length,shed_front_height,  space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, door_h=2030);

//restOfGarden();


updated holder element and rmeoved dead code

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

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

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

module whole_holder(names)
{
  // plate
  translate([0, 5+1, -5])
    rotate([90, 0, 0])
      difference()
      {
        rounded_surface_([36, 35], 3);
        cube([36, 5, 3*2]);
      }
  // back wall
  rounded_half_surface_([36, 50], 5);
  // holders
  for(i=[0:len(names)-1])
    translate([7+i*20, 25, 4])
      holder();
}

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

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

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

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

module whole_holder(names)
{
  translate([0, 5+1, -5])
    rotate([90, 0, 0])
      difference()
      {
        rounded_surface_([36, 35], 3);
        cube([36, 5, 3*2]);
      }
  rounded_half_surface_([36, 50], 5);
  translate(1/2*[36, 50, 0]) // TODO: temporary
  {
  for(dx=[-1, +1])
    translate([dx*20/2-1, -1, 2])
      holder([3,25,18]);
  }
}

whole_holder(["John", "Jane"]);
//rounded_surface_([36, 5, 30], 3/2);

//rounded_surface_([30, 20], 3);
//rounded_half_surface_([30, 20], 5);


misc: add v_slice

misc: add v_slice

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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