decreased spacing for the original model mount, for a better fit

eps = 0.01;
spacing = 4;
size = [74.1+2*spacing, 32.2+2*spacing];
rounding = 5;

module holder_()
{
  z=2;
  x=74.1;
  y=32.2;
  w=3.08;
  for(dx=[0, x-w])
    translate([dx,0,0])
    {
      cube([w, y, z]);
      translate(6*[0, 1/2, 0])
        cube([w, y-6, z+20]);
    }
  translate([0, y/2-w/2, z])
    cube([x, w, z]);
}

module holder_space_()
{
  space=1;
  z=2-0.5;
  x=74.1;
  y=32.2+2*space;
  w=3.08+2*space;
  for(dx=[0, x-w+2*space])
    translate([dx, 0, 0])
      cube([w, y, z]);
}

module main_block_()
{
  // desk mount
  difference()
  {
    cube([size[0], 19+2*2, 20+4]);
    translate([-eps, 2, 4])
      cube([size[0]+2*eps, 19, 20+1]);
  }
  // main surface
  for(i=[0, size[0]-rounding*2-1])
    translate([i, 0, 0])
      hull()
      {
        cube([2*rounding+1, 1, 3]);
        for(j=[0, 1])
          translate(spacing*[1,0,0] + [j+1, size[1], 0])
            cylinder(r=rounding, h=3, $fn=120);
      }
}

module holder()
{
  difference()
  {
    main_block_();
    translate([3, spacing+1, 0])
      #holder_space_();
    translate(2*[1, 1, 0] + [size[0]-spacing-2, spacing, 1.5])
      rotate([0, 180, 0])
        %holder_();
  }
}

holder();

eps = 0.01;
spacing = 4;
size = [74.1+2*spacing, 32.2+2*spacing];
rounding = 5;

module holder_()
{
  z=2;
  x=74.1;
  y=32.2;
  w=3.08;
  for(dx=[0, x-w])
    translate([dx,0,0])
    {
      cube([w, y, z]);
      translate(6*[0, 1/2, 0])
        cube([w, y-6, z+20]);
    }
  translate([0, y/2-w/2, z])
    cube([x, w, z]);
}

module holder_space_()
{
  space=2;
  z=2-0.5;
  x=74.1;
  y=32.2+2*space;
  w=3.08+2*space;
  for(dx=[0, x-w+2*space])
    translate([dx, 0, 0])
      cube([w, y, z]);
}

module main_block_()
{
  // desk mount
  difference()
  {
    cube([size[0], 19+2*2, 20+4]);
    translate([-eps, 2, 4])
      cube([size[0]+2*eps, 19, 20+1]);
  }
  // main surface
  for(i=[0, size[0]-rounding*2-1])
    translate([i, 0, 0])
      hull()
      {
        cube([2*rounding+1, 1, 3]);
        for(j=[0, 1])
          translate(spacing*[1,0,0] + [j+1, size[1], 0])
            cylinder(r=rounding, h=3, $fn=120);
      }
}

module holder()
{
  difference()
  {
    main_block_();
    translate([2, spacing, 0])
      #holder_space_();
    translate(2*[1, 1, 0] + [size[0]-spacing-2, spacing, 1.5])
      rotate([0, 180, 0])
        %holder_();
  }
}

holder();


fix ttb and rotate on cylinder

fix ttb and rotate on cylinder

Fixes for tb,btt on circles and cylinders, as well as error in rotate on
cylinder.

/*  text on ....
   This is a rework of version 3 of write.scad to use the new OpenSCAD internal text() primitive.
   All credit to Harlan Martin (harlan@sutlog.com) for his great effort on the original.
   Great thanks to t-paul (and the OpenSCAD dev team) on adding the new text() primitive.
*/

/*
The arguments to be provided for each function are explictly in the modules.

All modules take close to all the arguments to text() [Not all features are supported in all text_on_object() modules though -- try them out warnings will be given if it is not supported]

text() params:
t
size
spacing
font
direction -- ltr, ttb, btt or rtl
language
script
halign -- left, right or center
valign -- baseline, bottom, top or center

All modules also take the additional arguments:
extrusion_height = depth of the letter, i.e. how far it sits proud
rotate
center //center the text at the location it is being written (NOT that the object is centered)

ALL faux-objects that the text is applied to have center=true
TODO: object_center=true/false (already done for cylinder)

The above arguemnts can be seen in text_extrude() as all modules call that module evetually (often multiple times).

locn_vector -- Where the faux-object has been translated to. TODO: Rename object to translate_vector?

Additional arguments for text_on_cube():

Additional arguments for text_on_cylinder():

Additional arguments for text_on_circle():

Additional arguments for text_on_sphere():

*/

// These control the default values for text_extrude(), text_on_sphere(), text_on_cube(), text_on_cylinder(), text_on_circle()
// if the arguments are not included in the call to the module.

// Defaults
// Defaults for all modules
default_t="text_on";
default_size = 4; //TODO - Can we make this 10?? To match internal size? There is an underlying value in text() -- This masks that.
default_font = "Liberation Mono";
default_spacing = 1; //Spacing between characters. There is an underlying value in text() -- This masks that. We try and match it here.
default_rotate  = 0; // text rotation (clockwise)
default_center  = true; //Text-centering
default_scale = [1,1,1];
default_extrusion_height = 2; //mm letter extrusion height

//Defaults for Cube
default_cube_face        = "front";	 // default face (top,bottom,left,right,back,front)
default_sphere_rounded   = false;	 //default for rounded letters on text_on_sphere
default_cube_updown = 0; //mm up (-down) from center on face of cube
default_cube_rightleft = 0;		 //mm right(-left) from center on face of cube

//Defaults for Sphere
default_sphere_northsouth= 0;
default_sphere_eastwest  = 0;
default_sphere_spin      = 0; //TODO:Different to rotate? or up/down. Better name?

//Defaults for Cylinder (including circle as it is on top/bottom)
default_circle_middle    = 0;      //(mm toward middle of circle)
default_circle_ccw       = false;  //write on top or bottom in a ccw direction
default_circle_eastwest  = 0;
default_cylinder_face    = "side";
default_cylinder_updown  = 0;

//Internal values - don't play with these :)
//This is much more accurate than the PI constant internal to Openscad.
internal_pi=3.1415926535897932384626433832795028841971693993751058209;
internal_pi2=internal_pi*2;

//Internal values - You might want to play with these if you are using a proportional font
internal_space_fudge = 0.80; //Fudge for working out lengths (widths) of strings

debug = true;

//---- Helper Functions ----
//String width (length from left to right or RTL) in OpenSCAD units
//NOTE: These are innacurate because we don't know the real spacing of the chars and so have to approximate
//They work for monospaced (fixed-width) fonts well
function width_of_text_char( size, spacing ) = (size * internal_space_fudge * spacing);
function width_of_text_string_num_length(length, size, spacing) = ( width_of_text_char( size, spacing ) * (length));
function width_of_text_string( t, size, spacing ) = ( width_of_text_string_num_length(len(t), size, spacing) );

function cylinder_center_adjusted_top( height, center )    = (center==true) ? height/2 : height ;
function cylinder_center_adjusted_bottom( height, center ) = (center==true) ? height/2 : 0 ;

//Angle that measures width of letters on perimeter of a circle (and sphere and cylinder)
function rotation_for_character(size, spacing, r, rotate = 0)
 = ( width_of_text_char( size, spacing ) /(internal_pi2*r))*360*(1-abs(rotate)/90);

//Rotate 1/2 width of text if centering
//One less -- if we have a single char we are already centred..
function rotation_for_center_text_string( t, size, spacing,r,rotate,center)
	= (center) ? (width_of_text_string_num_length( len(t)-1, size, spacing )/2/(internal_pi2*r)*360) : 0 ;

//Rotate according to rotate and if centred text also 1/2 width of text
function rotation_for_center_text_string_and_rotate( t, size, spacing,r,rotate,center)
	= ((center) ? (width_of_text_string( t, size, spacing )/2/(internal_pi2*r)*360) : 1) * (1-abs(rotate)/90);

//---- Text on Object Functions ----
//Text on the top or side of a cylinder.
//Supports writing on normal and slanty cylinders
//cylinder(h,r,center)
//cylinder(h,r1,r2,center)
module text_on_cylinder(t=default_t,
                        //Object-specific
                        locn_vec = [0,0,0],
                        r,r1,r2,h,
                        face             = default_cylinder_face,
                        updown           = default_cylinder_updown,
                        rotate           = default_rotate,
                        eastwest         = default_circle_eastwest,
                        middle           = default_circle_middle,
                        ccw              = default_circle_ccw,
                        cylinder_center  = false, //if the cylinder object we are writing on is center==true
                        //TODO: add rounded

                        //All objects
                        extrusion_height = default_extrusion_height,
                        center           = default_center,
                        //All objects -- arguments as for text()
                        font             = undef,
                        size             = default_size,
                        direction        = undef,
                        halign           = undef,
                        valign           = undef,
                        language         = undef,
                        script           = undef,
                        spacing          = default_spacing
){
//    echo (str("text_on_cylinder:There are " ,len(t) ," letters in t" , t));
//    echo (str("text_on_cylinder:","h" , h));
//    echo (str("text_on_cylinder:","r=" , r));
//    echo (str("text_on_cylinder:","r1=" , r1));
//    echo (str("text_on_cylinder:","r2=" , r2));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_cylinder:","WARNING " , "halign and valign are NOT supported."));
    }

	rtl_sign = (direction=="rtl") ? -1 : 1;
	if ((face=="top")||(face=="bottom") ){
		//Work on a circle
		locn_offset_vec = (face=="top" ) ? [0,0,cylinder_center_adjusted_top(h,cylinder_center)] : [0,0,cylinder_center_adjusted_bottom(h,cylinder_center)] ;
		rotation_angle = (face=="top" ) ? 0 : 180 ;
		int_radius =  (r==undef) ? ((face=="top" ) ? r2 : r1 ) : r;  //Use the top/bottom radius for slanty-cylinders
			rotate(rotation_angle,[1,0,0])
			text_on_circle(t,locn_vec+locn_offset_vec,r=int_radius-size,
				font=font,size=size,
				spacing=spacing,direction=direction,language=language,script=script,halign=halign,valign=valign,
				extrusion_height=extrusion_height,
				rotate=rotate,
				eastwest=eastwest,middle=middle,ccw=ccw);
	}else{
		if((middle!=undef) && (middle!=default_circle_middle))
		{
			//TODO: Which others?
			//TODO: Which side things aren't supported on the circle
			echo (str("text_on_cylinder:","WARNING " , "middle NOT supported for the SIDE of a cylinder."));
		}
		//Work on the side
		locn_offset_vec =  (cylinder_center==true) ? [0,0,0] : [0,0,h/2];
		rotate(-rtl_sign * rotation_for_center_text_string_and_rotate(t, size, spacing,r,rotate,center), [0,0,1])
		translate(locn_vec+locn_offset_vec)
		__internal_text_on_cylinder_side(t,locn_vec,r=r,h=h,r1=r1,r2=r2,
			cylinder_center=cylinder_center,
			center=center,
			font=font,size=size,
			spacing=spacing,direction=direction,language=language,script=script,halign=halign,valign=valign,
			extrusion_height=extrusion_height,
			rotate=rotate,face=face,updown=updown,
			eastwest=eastwest);
	}
}

//circle(r)
module text_on_circle(t=default_t,
                      //Object-specific
                      locn_vec = [0,0,0],
                      r,
                      eastwest = default_circle_eastwest,
                      middle = default_circle_middle,
                      ccw = default_circle_ccw,

                      //All objects
                      extrusion_height = default_extrusion_height,
                      rotate = default_rotate,
                      center = default_center,

                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef,
                      valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
){
//    echo (str("text_on_circle:","There are " ,len(t) ," letters in t" , t));
//    echo (str("text_on_circle:","rotate=" , rotate));
//    echo (str("text_on_circle:","eastwest=" , eastwest));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_circle:","WARNING " , "halign and valign are NOT supported."));
    }

	ccw_sign = (ccw==true) ? 1 : -1;
	rtl_sign = (direction=="rtl") ? -1 : 1;
    ttb_btt_inaction = (direction=="ttb" || direction=="btt") ? 0 : 1;
	rotate_z_outer =  -rotate + ccw_sign*eastwest;
	rotate_z_inner = -rtl_sign * ccw_sign * ttb_btt_inaction * rotation_for_center_text_string( t, size, spacing, r-middle, rotate, center);
	rotate( rotate_z_outer, [0,0,1] )
	rotate( rotate_z_inner, [0,0,1] )
	translate(locn_vec)
	for (l=[0:len(t)-1]){
		//TTB/BTT means no per letter rotation
		rotate_z_inner2 = -ccw_sign * 90 + ttb_btt_inaction*rtl_sign*ccw_sign*l*rotation_for_character(size, spacing, r-middle, rotate=0);   //Bottom out=-270+r
		//TTB means we go toward center, BTT means away
		vert_x_offset = (direction=="ttb" || direction=="btt") ?  (l * size * ((direction=="btt") ? -1 : 1)) :  0;
		rotate( rotate_z_inner2, [0,0,1] )
		translate([r - middle - vert_x_offset,0,0])
		rotate(-ccw_sign*270,[0,0,1])  // flip text (botom out = -270)
		text_extrude(t[l],center=true,
			font=font,size=size,
			rotate=undef,
			spacing=spacing,
			direction=undef, //We don't pass direction ( misaligns inside text() ). TODO: Investigate why
			language=language,script=script,halign=halign,valign=valign,
			extrusion_height=extrusion_height );
	}
}

//Only expected to be called from another function. No defaults as expected to be done in parent.
//NOTE: This is hacked. Requires more mathematics than I feel like to do properly today....
//TODO: Refactor the two operations - side of normal and slanty
//No default checks in this funciton -- done in parent
module __internal_text_on_cylinder_side(t=default_t,
                      //Object-specific
                      locn_vec = [0,0,0],
                      r,r1,r2,h,
                      cylinder_center,

                      //All objects
                      extrusion_height = default_extrusion_height,
                      center           = undef,
                      rotate           = default_rotate,

                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef, //Overridden
                      valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
)
{
//    echo (str("__internal_text_on_cylinder_side:There are " ,len(t) ," letters in t" , t));
//    echo (str("__internal_text_on_cylinder_side:","h=" , h));
//    echo (str("__internal_text_on_cylinder_side:","r=" , r));
//    echo (str("__internal_text_on_cylinder_side:","r1=" , r1));
//    echo (str("__internal_text_on_cylinder_side:","r2=" , r2));
//    echo (str("__internal_text_on_cylinder_side:","direction=" , direction));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_cylinder:","WARNING " , "halign and valign are NOT supported."));
    }

    wid = width_of_text_char( size, spacing ); //TODO: refactor this out?

    function calc_radius_at_height_offset(r1,r2,h_total,h_offset) = ( r1 + ((r2-r1) * (h_total-h_offset)/h_total) );

  	//Has to factor in up/downedness
    //Starts at middle height then height displacement is reduced under rotation and by the slope of the uneven radii
    function calc_height_offset_at_length(r1,r2,h,length,rotate, updown) = ( h/2 - updown + length * rotate/90 * cos( atan( (r2-r1)/h ) )  );

    function calc_radius_at_length(r1,r2,h,length,rotate,updown) = ( calc_radius_at_height_offset(r1,r2,h,calc_height_offset_at_length(r1,r2,h,length,rotate,updown)));

    if(r==undef)
    {
        //Slanty-cylinder (truncated cone) warnings
        if((direction=="btt") || (direction=="ttb"))
        {
            //TODO: After the issues are resolved refactoring should give this pretty easily
            echo (str("text_on_cylinder:","WARNING" , " - Slanty-cylinders definitely don't directions TTB or BTT on sides yet. Setting direction to ltr."));
        }
        if(center==true)
        {
            echo (str("text_on_cylinder:","WARNING" , " - Slanty-cylinders don't support centering yet. Setting center to false. You can adjust updown and eastwest and achieve most of what you are after...."));
        }
    }

    r1  = (r1!=undef) ? r1 : r;
    r2  = (r2!=undef) ? r2 : r;
    //NOTE: r is not used after here. We operate on generalised slanty cone (r1 and r2)

  	//TODO: Look at changing this to extrusion_height_center
	//and renaming the other as text_center
	rr1 = (center) ? r1-extrusion_height/2 : r1;
	rr2 = (center) ? r2-extrusion_height/2 : r2;

    ccenter  = (r!=undef) ? center : false; //We don't do (text) centering for the slanty cones/helical paths (yet? ever?). Needs to do quite a few offset calcs.
    ddirection   = ((r==undef) && ((direction=="ttb")||(direction=="btt"))) ? "ltr" : direction; //We don't do ttb or btt directions on slanty
    rtl_sign = (ddirection=="rtl") ? -1 : 1;

        //This code takes care of slanty cylinders and "normal" cylinders
        translate([0,0,updown])
        rotate(eastwest,[0,0,1])
        for (l=[0:len(t)-1]){
            //TODO: TTB and BTT need to have a different concept of path/length than this for RTL/LTR
   	        //width_of_... is half a char too long -- add 0.5 (counting from zero)
            length_to_center_of_char = width_of_text_string_num_length(l+0.5,size,spacing);
            radius_here = calc_radius_at_length(rr1,rr2,h, length_to_center_of_char, rotate, updown);
            //Rotating into position and tangentially to surface -- Don't rotate per character for ttb/btt
            //-90 is to get centering at origin
		    rotate_z_inner  = -90 + rtl_sign * rotation_for_character(size, spacing, ( radius_here ), rotate) * ((ddirection=="ttb" || ddirection=="btt") ?  0 : l);
	        rotate(rotate_z_inner,[0,0,1])
			{
	        //Positioning - based on (somewhat innacurate) string length
		    //Offset per character to go up/down the side in ttb/btt -- TTB down, BTT up
		    vert_z_char_offset = (ddirection=="ttb" || ddirection=="btt") ?  (l * size * ((ddirection=="ttb") ? -1 : 1 )) :  0 ;
		    //Only if RTL/LTR and if center -- center the text (starts off in a more visually appealing location)
		    vert_z_half_text_offset_tmp = (len(t)-1)/2 * (rotate/90*wid);
		    vert_z_half_text_offset =  ((ddirection=="ttb" || ddirection=="btt") || (ccenter==false)) ? 0 : vert_z_half_text_offset_tmp;
            translate([ radius_here , 0, vert_z_char_offset - l * ((rotate)/90*wid) + vert_z_half_text_offset])

	        //Flip to tangent on the sloping side (without respecting rotation impact on the tangent -- rotate seems a little off. TODO: Investigate).
	        rotate( atan( (rr2-rr1)/h ) , [0,1,0])
	        //Flip onto face of "normal" cylinder
	        rotate(90,[1,0,0])
	        rotate(90,[0,1,0])

    	    //Modify the offset of the baselined text to center
            translate([0,(ccenter) ? -size/2 : 0,0])

	        text_extrude(t[l],
	            center=false,
	            rotate=rotate,
	            font=font,size=size,
	            spacing=spacing,
	            direction=undef, //We don't pass direction ( misaligns inside text() ). TODO: Investigate why
	            language=language,script=script,
	            halign="center", //This can be relaxed eventually
                valign="baseline", //Need this to keep all letters on the same line (could also support top -- otherw will make
	            extrusion_height=extrusion_height
	            );
			}
        }
}

//sphere(r)
//NB: Iterates through characters/glyphs and presents them individually (but supports actions across the entire string still)
//supports all features of text() except: halign and valign
module text_on_sphere(t=default_t,
                      //Object-specific
                      locn_vec = [0,0,0],
                      r,
                      eastwest         = default_sphere_eastwest,
                      northsouth       = default_sphere_northsouth,
                      rotate           = default_rotate,
                      spin             = default_sphere_spin,
                      rounded          = default_sphere_rounded,
                      //All objects
                      extrusion_height = default_extrusion_height,
                      center           = default_center, //center = false doesn't really even make sense to do (IMHO)
                      scale  = default_scale,
                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef, //Not supported - only here to enable a warning
                      valign           = undef, //Not supported - only here to enable a warning
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
){
    //echo ("text_on_sphere:There are " ,len(t) ," letters in t" , t);

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_sphere:","WARNING " , "halign and valign are NOT supported (functionality used in centering)."));
    }

    //TODO: refactor this?
	rtl_sign = (direction=="rtl") ? -1 : 1;

	//TODO: Look at changing this to extrusion_height_center
	//and renaming the other as text_center
	//If we are centered - we sink the radius by half the
	//extrusion height to draw "just below the surface"
	rr = (center) ? r-extrusion_height/2 : r ;

	rotate(eastwest,[0,0,1])
	rotate(-northsouth,[1,0,0])
	rotate(spin,[0,1,0])
	{
	//This tries to center the text (for RTL text).
    ttb_btt_inaction = (direction=="ttb" || direction=="btt") ? 0 : 1;
	rotate( -rtl_sign * ttb_btt_inaction * rotation_for_center_text_string(t, size, spacing, rr, rotate, center), [0,0,1])
	{
	    translate(locn_vec)
		if ( rounded == false ){
			__internal_text_on_sphere_helper(t=t,r=rr,
	    		rotate=rotate,
	    		center=center,
	    		scale=scale,
	            font=font,size=size,
                spacing=spacing,
                direction=direction,
                language=language,script=script,halign=halign,valign=valign,
                extrusion_height=extrusion_height
			);
		}else{
		    //If rounding then clip the text inside an inner sphere and outer sphere
			intersection()
			{
			__internal_text_on_sphere_helper(t=t,r=rr,
        		rotate=rotate,
        		center=center,
        		scale=scale,
                font=font,size=size,
                spacing=spacing,
                direction=direction,
                language=language,script=script,halign=halign,valign=valign,
                extrusion_height=extrusion_height*2 //Make it proud to clip it off.
		    );

            //Shell - bounding inner and outer
			difference()
			{   //rounded outside
				sphere(rr+extrusion_height);
				// rounded inside for indented text
			    sphere( rr  );
			}

		    }
		}
		}
	}
}

//internal function -- don't call directly
//No default checks in this funciton -- done in parent
module __internal_text_on_sphere_helper(t=default_t,
                      //Object-specific
                      r,
                      //All objects
                      extrusion_height = default_extrusion_height,
                      center           = default_center,
                      scale  = default_scale,
                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      //halign           = undef,
                      //valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
)
{
    rtl_sign = (direction=="rtl") ? -1 : 1;
    ttb_btt_inaction = (direction=="ttb" || direction=="btt") ? 0 : 1;
    ttb_btt_action   = (direction=="ttb" || direction=="btt") ? 1 : 0;

    for (l=[0:len(t)-1]){
	    rotate_z_inner   = -90 + rtl_sign * ttb_btt_inaction * l * rotation_for_character(size, spacing, r, 0);

        rotate( rotate_z_inner, [0,0,1] )
		{
        translate_sign   = (direction=="ttb" || direction=="btt") ? ((direction=="btt") ? 1 : -1)  : 0 ;
        //translate_effect = (direction=="ttb" || direction=="btt") ? 1  :  0 ;
        //Translate letter to be located on the sphere in up/down direction when we are doing TTB and BTT
	    translate(r * [cos(0 + ttb_btt_action * rotation_for_character(size, spacing, r, rotate) * l ), 0, translate_sign * ttb_btt_action * sin(0 +  rotation_for_character(size, spacing, r, rotate) * l ) ])
        //Rotate letter to be tangential to the new part of the sphere when we are doing TTB and BTT
	    rotate(-90 * translate_sign * (0 + ttb_btt_action * rotation_for_character(size, spacing, r, rotate) * l )/90 , [0,1,0])
        //Flip character into position to be flush against sphere
	    rotate(90,[1,0,0])
	    rotate(90,[0,1,0])

	    //Modify the offset of the baselined text to center
	    translate([0,(center) ? -size/2 : 0 ,0])

	    text_extrude(t[l],
	        center=false,
	        rotate=rotate,
	        scale=scale,
	        font=font,
	        size=size,
            spacing=spacing,
            direction=undef, //We don't pass direction ( misaligns inside text() ). TODO: Investigate why
            language=language,script=script,
            halign="center", //This can be relaxed eventually
            valign="baseline", //Need this to keep all letters on the same line (could also support top -- otherw will make wonky letters)
            extrusion_height=extrusion_height );
		}
    }
}

//cube(size)
//cube([width,h,depth])
//NB: cube_size is used instead of size (the name size is used in the text module)
//NB: Passes through text as strings -- supports all features of text()
module text_on_cube(  t=default_t,
                      //Object-specific
                      cube_size, //number or 3-vector
                      face      = default_cube_face,
                      rightleft = default_cube_rightleft,
                      updown    = default_cube_updown,
                      //All objects
                      locn_vec = [0,0,0],
                      extrusion_height = default_extrusion_height,
                      center = undef,
                      rotate = default_rotate,
                      scale  = default_scale,
                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef,
                      valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
){
    //echo (str("text_on_cube:","There are " ,len(t) ," letters in t" , t));
    //echo (str("text_on_cube:","cube_size" , cube_size));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_cube:","WARNING " , "halign and valign are NOT supported."));
    }

    int_cube_size = (str(cube_size)[0] != "[") ? [cube_size,cube_size,cube_size] : cube_size;
    rotate_x = ( (face=="front") || (face=="back") || (face=="left") || (face=="right") ) ? 90
				: ((face=="bottom") ? 180 : 0) ; //Top is zero
	rotate_y = ( (face=="back") || (face=="left") || (face=="right") ) ? (face=="back") ? 180 : ((face=="left") ? -90 : 90) //Right is 90
				: 0 ; //Top, bottom, front are zero
	locn_vec_offset =
	        (face=="front") ?             [+rightleft,   -int_cube_size[1]/2, +updown]
	        :((face=="back")?             [+rightleft,   +int_cube_size[1]/2, +updown]
	            : ((face=="left")?         [-int_cube_size[0]/2, +rightleft,   +updown]
	                : ((face=="right")?    [+int_cube_size[0]/2, +rightleft,   +updown]
	                    : ((face=="top")?  [+rightleft,   +updown,             +int_cube_size[2]/2]
	                        :              [+rightleft,   -updown,             -int_cube_size[2]/2]
							)))); //bottom

		translate(locn_vec + locn_vec_offset)
		rotate(rotate_x,[1,0,0])
		rotate(rotate_y,[0,1,0])  // rotate around the y axis (z before rotation)
		text_extrude(t,
		    center=center,
		    rotate=rotate,
		    scale=scale,
		    font=font,
		    size=size,
		    spacing=spacing,
		    direction=direction, //Does this misalign inside text()? the individual character modules do.
		    language=language,
		    script=script,
		    halign=halign,
		    valign=valign,
		    extrusion_height=extrusion_height );
}

function default_if_undef(val, default_val) = (val!=undef) ? val : default_val ;

//Print a single character or a string at the desired extrusion height
//Passes on values to text() that are passed in
//TODO: Add x,y,z rotations get text facing the right way from the start)
module text_extrude( t=default_t,
                     extrusion_height = default_extrusion_height,
                     center = default_center, //Fudgy. YMMV. //TODO:center_extrusion, or extrusion offset??
                     rotate = default_rotate,
                     scale  = default_scale, //For scaling by different on axes (for widening etc)
                     //Following are test() params (in addition to t=)
                     font             = default_font,
                     size             = default_size,
                     direction        = undef,
                     halign           = undef,
                     valign           = undef,
                     language         = undef,
                     script           = undef,
                     spacing          = default_spacing
)
{
    //echo (str("text_extrude:","There are " ,len(t) ," letters in text" , t));
    //echo (str("text_extrude:","extrusion_height=" , extrusion_height));

    if(center==true)
    {
        if((halign!=undef) || (valign!=undef))
        {
            echo (str("text_extrude:","WARNING " , "When center is true, then halign and valign are NOT supported."));
        }
    }

    //As arguments are explicitly set as undef (from higher functions) as they come down (they don't get defaults in this function as they are set as something)
    //we need to check and replace any that we don't want to be undef
    t                 = default_if_undef(t,default_t);
    font              = default_if_undef(font,default_font);
    extrusion_height  = default_if_undef(extrusion_height,default_extrusion_height);
    center            = default_if_undef(center,default_center);
    rotate            = default_if_undef(rotate,default_rotate);
    spacing           = default_if_undef(spacing,default_spacing);
    size              = default_if_undef(size,default_size);

    halign            = (center) ? "center" : halign ;
    valign            = (center) ? "center" : valign ;
    extrusion_center  = (center) ? true : false ;

	scale( scale )
    rotate(rotate,[0,0,-1]) //TODO: Do we want to make this so that the entire vector can be set?
	{
	    linear_extrude(height=extrusion_height,convexity=10,center=extrusion_center)
        text(text=t, size = size,
            $fn = 40,
            font = font, direction = direction, spacing = spacing,
            halign = halign,
            valign = valign,
            language=language, script=script);
	}
}

/*  text on ....
   This is a rework of version 3 of write.scad to use the new OpenSCAD internal text() primitive.
   All credit to Harlan Martin (harlan@sutlog.com) for his great effort on the original.
   Great thanks to t-paul (and the OpenSCAD dev team) on adding the new text() primitive.
*/

/*
The arguments to be provided for each function are explictly in the modules.

All modules take close to all the arguments to text() [Not all features are supported in all text_on_object() modules though -- try them out warnings will be given if it is not supported]

text() params:
t
size
spacing
font
direction -- ltr, ttb, btt or rtl
language
script
halign -- left, right or center
valign -- baseline, bottom, top or center

All modules also take the additional arguments:
extrusion_height = depth of the letter, i.e. how far it sits proud
rotate
center //center the text at the location it is being written (NOT that the object is centered)

ALL faux-objects that the text is applied to have center=true
TODO: object_center=true/false (already done for cylinder)

The above arguemnts can be seen in text_extrude() as all modules call that module evetually (often multiple times).

locn_vector -- Where the faux-object has been translated to. TODO: Rename object to translate_vector?

Additional arguments for text_on_cube():

Additional arguments for text_on_cylinder():

Additional arguments for text_on_circle():

Additional arguments for text_on_sphere():

*/

// These control the default values for text_extrude(), text_on_sphere(), text_on_cube(), text_on_cylinder(), text_on_circle()
// if the arguments are not included in the call to the module.

// Defaults
// Defaults for all modules
default_t="text_on";
default_size = 4; //TODO - Can we make this 10?? To match internal size? There is an underlying value in text() -- This masks that.
default_font = "Liberation Mono";
default_spacing = 1; //Spacing between characters. There is an underlying value in text() -- This masks that. We try and match it here.
default_rotate  = 0; // text rotation (clockwise)
default_center  = true; //Text-centering
default_scale = [1,1,1];
default_extrusion_height = 2; //mm letter extrusion height

//Defaults for Cube
default_cube_face        = "front";	 // default face (top,bottom,left,right,back,front)
default_sphere_rounded   = false;	 //default for rounded letters on text_on_sphere
default_cube_updown = 0; //mm up (-down) from center on face of cube
default_cube_rightleft = 0;		 //mm right(-left) from center on face of cube

//Defaults for Sphere
default_sphere_northsouth= 0;
default_sphere_eastwest  = 0;
default_sphere_spin      = 0; //TODO:Different to rotate? or up/down. Better name?

//Defaults for Cylinder (including circle as it is on top/bottom)
default_circle_middle    = 0;      //(mm toward middle of circle)
default_circle_ccw       = false;  //write on top or bottom in a ccw direction
default_circle_eastwest  = 0;
default_cylinder_face    = "side";
default_cylinder_updown  = 0;

//Internal values - don't play with these :)
//This is much more accurate than the PI constant internal to Openscad.
internal_pi=3.1415926535897932384626433832795028841971693993751058209;
internal_pi2=internal_pi*2;

//Internal values - You might want to play with these if you are using a proportional font
internal_space_fudge = 0.80; //Fudge for working out lengths (widths) of strings

debug = true;

//---- Helper Functions ----
//String width (length from left to right or RTL) in OpenSCAD units
//NOTE: These are innacurate because we don't know the real spacing of the chars and so have to approximate
//They work for monospaced (fixed-width) fonts well
function width_of_text_char( size, spacing ) = (size * internal_space_fudge * spacing);
function width_of_text_string_num_length(length, size, spacing) = ( width_of_text_char( size, spacing ) * (length));
function width_of_text_string( t, size, spacing ) = ( width_of_text_string_num_length(len(t), size, spacing) );

function cylinder_center_adjusted_top( height, center )    = (center==true) ? height/2 : height ;
function cylinder_center_adjusted_bottom( height, center ) = (center==true) ? height/2 : 0 ;

//Angle that measures width of letters on perimeter of a circle (and sphere and cylinder)
function rotation_for_character(size, spacing, r, rotate = 0)
 = ( width_of_text_char( size, spacing ) /(internal_pi2*r))*360*(1-abs(rotate)/90);

//Rotate 1/2 width of text if centering
//One less -- if we have a single char we are already centred..
function rotation_for_center_text_string( t, size, spacing,r,rotate,center)
	= (center) ? (width_of_text_string_num_length( len(t)-1, size, spacing )/2/(internal_pi2*r)*360) : 0 ;

//Rotate according to rotate and if centred text also 1/2 width of text
function rotation_for_center_text_string_and_rotate( t, size, spacing,r,rotate,center)
	= ((center) ? (width_of_text_string( t, size, spacing )/2/(internal_pi2*r)*360) : 1) * (1-abs(rotate)/90);

//---- Text on Object Functions ----
//Text on the top or side of a cylinder.
//Supports writing on normal and slanty cylinders
//cylinder(h,r,center)
//cylinder(h,r1,r2,center)
module text_on_cylinder(t=default_t,
                        //Object-specific
                        locn_vec = [0,0,0],
                        r,r1,r2,h,
                        face             = default_cylinder_face,
                        updown           = default_cylinder_updown,
                        rotate           = default_rotate,
                        eastwest         = default_circle_eastwest,
                        middle           = default_circle_middle,
                        ccw              = default_circle_ccw,
                        cylinder_center  = false, //if the cylinder object we are writing on is center==true
                        //TODO: add rounded

                        //All objects
                        extrusion_height = default_extrusion_height,
                        center           = default_center,
                        //All objects -- arguments as for text()
                        font             = undef,
                        size             = default_size,
                        direction        = undef,
                        halign           = undef,
                        valign           = undef,
                        language         = undef,
                        script           = undef,
                        spacing          = default_spacing
){
//    echo (str("text_on_cylinder:There are " ,len(t) ," letters in t" , t));
//    echo (str("text_on_cylinder:","h" , h));
//    echo (str("text_on_cylinder:","r=" , r));
//    echo (str("text_on_cylinder:","r1=" , r1));
//    echo (str("text_on_cylinder:","r2=" , r2));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_cylinder:","WARNING " , "halign and valign are NOT supported."));
    }

	rtl_sign = (direction=="rtl") ? -1 : 1;
	if ((face=="top")||(face=="bottom") ){
		//Work on a circle
		locn_offset_vec = (face=="top" ) ? [0,0,cylinder_center_adjusted_top(h,cylinder_center)] : [0,0,cylinder_center_adjusted_bottom(h,cylinder_center)] ;
		rotation_angle = (face=="top" ) ? 0 : 180 ;
		int_radius =  (r==undef) ? ((face=="top" ) ? r2 : r1 ) : r;  //Use the top/bottom radius for slanty-cylinders
			rotate(rotation_angle,[1,0,0])
			text_on_circle(t,locn_vec+locn_offset_vec,r=int_radius-size,
				font=font,size=size,
				spacing=spacing,direction=direction,language=language,script=script,halign=halign,valign=valign,
				extrusion_height=extrusion_height,
				rotate=rotate,
				eastwest=eastwest,middle=middle,ccw=ccw);
	}else{
		if((middle!=undef) && (middle!=default_circle_middle))
		{
			//TODO: Which others?
			//TODO: Which side things aren't supported on the circle
			echo (str("text_on_cylinder:","WARNING " , "middle NOT supported for the SIDE of a cylinder."));
		}
		//Work on the side
		locn_offset_vec =  (cylinder_center==true) ? [0,0,0] : [0,0,h/2];
		rotate(-rtl_sign * rotation_for_center_text_string_and_rotate(t, size, spacing,r,rotate,center), [0,0,1])
		translate(locn_vec+locn_offset_vec)
		__internal_text_on_cylinder_side(t,locn_vec,r=r,h=h,r1=r1,r2=r2,
			cylinder_center=cylinder_center,
			center=center,
			font=font,size=size,
			spacing=spacing,direction=direction,language=language,script=script,halign=halign,valign=valign,
			extrusion_height=extrusion_height,
			rotate=rotate,face=face,updown=updown,
			eastwest=eastwest);
	}
}

//circle(r)
module text_on_circle(t=default_t,
                      //Object-specific
                      locn_vec = [0,0,0],
                      r,
                      eastwest = default_circle_eastwest,
                      middle = default_circle_middle,
                      ccw = default_circle_ccw,

                      //All objects
                      extrusion_height = default_extrusion_height,
                      rotate = default_rotate,
                      center = default_center,

                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef,
                      valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
){
//    echo (str("text_on_circle:","There are " ,len(t) ," letters in t" , t));
//    echo (str("text_on_circle:","rotate=" , rotate));
//    echo (str("text_on_circle:","eastwest=" , eastwest));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_circle:","WARNING " , "halign and valign are NOT supported."));
    }

	ccw_sign = (ccw==true) ? 1 : -1;
	rtl_sign = (direction=="rtl") ? -1 : 1;
    ttb_btt_inaction = (direction=="ttb" || direction=="btt") ? 0 : 1;
	rotate_z_outer =  -rotate + ccw_sign*eastwest;
	rotate_z_inner = -rtl_sign * ccw_sign * ttb_btt_inaction * rotation_for_center_text_string( t, size, spacing, r-middle, rotate, center);
	rotate( rotate_z_outer, [0,0,1] )
	rotate( rotate_z_inner, [0,0,1] )
	translate(locn_vec)
	for (l=[0:len(t)-1]){
		//TTB/BTT means no per letter rotation
		rotate_z_inner2 = -ccw_sign * 90 + ttb_btt_inaction*rtl_sign*ccw_sign*l*rotation_for_character(size, spacing, r-middle, rotate=0);   //Bottom out=-270+r
		//TTB means we go toward center, BTT means away
		vert_x_offset = (direction=="ttb" || direction=="btt") ?  size * ((direction=="btt") ? -1 : 1) :  0;
		rotate( rotate_z_inner2, [0,0,1] )
		translate([r - middle - vert_x_offset,0,0])
		rotate(-ccw_sign*270,[0,0,1])  // flip text (botom out = -270)
		text_extrude(t[l],center=true,
			font=font,size=size,
			rotate=undef,
			spacing=spacing,
			direction=undef, //We don't pass direction ( misaligns inside text() ). TODO: Investigate why
			language=language,script=script,halign=halign,valign=valign,
			extrusion_height=extrusion_height );
	}
}

//Only expected to be called from another function. No defaults as expected to be done in parent.
//NOTE: This is hacked. Requires more mathematics than I feel like to do properly today....
//TODO: Refactor the two operations - side of normal and slanty
//No default checks in this funciton -- done in parent
module __internal_text_on_cylinder_side(t=default_t,
                      //Object-specific
                      locn_vec = [0,0,0],
                      r,r1,r2,h,
                      cylinder_center,

                      //All objects
                      extrusion_height = default_extrusion_height,
                      center           = undef,
                      rotate           = default_rotate,

                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef, //Overridden
                      valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
)
{
//    echo (str("__internal_text_on_cylinder_side:There are " ,len(t) ," letters in t" , t));
//    echo (str("__internal_text_on_cylinder_side:","h=" , h));
//    echo (str("__internal_text_on_cylinder_side:","r=" , r));
//    echo (str("__internal_text_on_cylinder_side:","r1=" , r1));
//    echo (str("__internal_text_on_cylinder_side:","r2=" , r2));
//    echo (str("__internal_text_on_cylinder_side:","direction=" , direction));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_cylinder:","WARNING " , "halign and valign are NOT supported."));
    }

    wid = width_of_text_char( size, spacing ); //TODO: refactor this out?

    function calc_radius_at_height_offset(r1,r2,h_total,h_offset) = ( r1 + ((r2-r1) * (h_total-h_offset)/h_total) );

  	//Has to factor in up/downedness
    //Starts at middle height then height displacement is reduced under rotation and by the slope of the uneven radii
    function calc_height_offset_at_length(r1,r2,h,length,rotate, updown) = ( h/2 - updown + length * rotate/90 * cos( atan( (r2-r1)/h ) )  );

    function calc_radius_at_length(r1,r2,h,length,rotate,updown) = ( calc_radius_at_height_offset(r1,r2,h,calc_height_offset_at_length(r1,r2,h,length,rotate,updown)));

    if(r==undef)
    {
        //Slanty-cylinder (truncated cone) warnings
        if((direction=="btt") || (direction=="ttb"))
        {
            //TODO: After the issues are resolved refactoring should give this pretty easily
            echo (str("text_on_cylinder:","WARNING" , " - Slanty-cylinders definitely don't directions TTB or BTT on sides yet. Setting direction to ltr."));
        }
        if(center==true)
        {
            echo (str("text_on_cylinder:","WARNING" , " - Slanty-cylinders don't support centering yet. Setting center to false. You can adjust updown and eastwest and achieve most of what you are after...."));
        }
    }

    r1  = (r1!=undef) ? r1 : r;
    r2  = (r2!=undef) ? r2 : r;
    //NOTE: r is not used after here. We operate on generalised slanty cone (r1 and r2)

  	//TODO: Look at changing this to extrusion_height_center
	//and renaming the other as text_center
	rr1 = (center) ? r1-extrusion_height/2 : r1;
	rr2 = (center) ? r2-extrusion_height/2 : r2;

    ccenter  = (r!=undef) ? center : false; //We don't do (text) centering for the slanty cones/helical paths (yet? ever?). Needs to do quite a few offset calcs.
    ddirection   = ((r==undef) && ((direction=="ttb")||(direction=="btt"))) ? "ltr" : direction; //We don't do ttb or btt directions on slanty
    rtl_sign = (ddirection=="rtl") ? -1 : 1;

        //This code takes care of slanty cylinders and "normal" cylinders
        translate([0,0,updown])
        rotate(eastwest,[0,0,1])
        for (l=[0:len(t)-1]){
            //TODO: TTB and BTT need to have a different concept of path/length than this for RTL/LTR
   	        //width_of_... is half a char too long -- add 0.5 (counting from zero)
            length_to_center_of_char = width_of_text_string_num_length(l+0.5,size,spacing);
            radius_here = calc_radius_at_length(rr1,rr2,h, length_to_center_of_char, rotate, updown);
            //Rotating into position and tangentially to surface -- Don't rotate per character for ttb/btt
            //-90 is to get centering at origin
		    rotate_z_inner  = -90 + rtl_sign * rotation_for_character(size, spacing, ( radius_here ), rotate) * (ddirection=="ttb" || ddirection=="btt") ?  0 : l;
	        rotate(rotate_z_inner,[0,0,1])
			{
	        //Positioning - based on (somewhat innacurate) string length
		    //Offset per character to go up/down the side in ttb/btt -- TTB down, BTT up
		    vert_z_char_offset = (ddirection=="ttb" || ddirection=="btt") ?  size * ((ddirection=="ttb") ? -1 : 1 ) :  0 ;
		    //Only if RTL/LTR and if center -- center the text (starts off in a more visually appealing location)
		    vert_z_half_text_offset_tmp = (len(t)-1)/2 * (rotate/90*wid);
		    vert_z_half_text_offset =  ((ddirection=="ttb" || ddirection=="btt") || (ccenter==false)) ? 0 : vert_z_half_text_offset_tmp;
            translate([ radius_here , 0, vert_z_char_offset - l * ((rotate)/90*wid) + vert_z_half_text_offset])

	        //Flip to tangent on the sloping side (without respecting rotation impact on the tangent -- rotate seems a little off. TODO: Investigate).
	        rotate( atan( (rr2-rr1)/h ) , [0,1,0])
	        //Flip onto face of "normal" cylinder
	        rotate(90,[1,0,0])
	        rotate(90,[0,1,0])

    	    //Modify the offset of the baselined text to center
            translate([0,(ccenter) ? -size/2 : 0,0])

	        text_extrude(t[l],
	            center=false,
	            rotate=rotate,
	            font=font,size=size,
	            spacing=spacing,
	            direction=undef, //We don't pass direction ( misaligns inside text() ). TODO: Investigate why
	            language=language,script=script,
	            halign="center", //This can be relaxed eventually
                valign="baseline", //Need this to keep all letters on the same line (could also support top -- otherw will make
	            extrusion_height=extrusion_height
	            );
			}
        }
}

//sphere(r)
//NB: Iterates through characters/glyphs and presents them individually (but supports actions across the entire string still)
//supports all features of text() except: halign and valign
module text_on_sphere(t=default_t,
                      //Object-specific
                      locn_vec = [0,0,0],
                      r,
                      eastwest         = default_sphere_eastwest,
                      northsouth       = default_sphere_northsouth,
                      rotate           = default_rotate,
                      spin             = default_sphere_spin,
                      rounded          = default_sphere_rounded,
                      //All objects
                      extrusion_height = default_extrusion_height,
                      center           = default_center, //center = false doesn't really even make sense to do (IMHO)
                      scale  = default_scale,
                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef, //Not supported - only here to enable a warning
                      valign           = undef, //Not supported - only here to enable a warning
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
){
    //echo ("text_on_sphere:There are " ,len(t) ," letters in t" , t);

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_sphere:","WARNING " , "halign and valign are NOT supported (functionality used in centering)."));
    }

    //TODO: refactor this?
	rtl_sign = (direction=="rtl") ? -1 : 1;

	//TODO: Look at changing this to extrusion_height_center
	//and renaming the other as text_center
	//If we are centered - we sink the radius by half the
	//extrusion height to draw "just below the surface"
	rr = (center) ? r-extrusion_height/2 : r ;

	rotate(eastwest,[0,0,1])
	rotate(-northsouth,[1,0,0])
	rotate(spin,[0,1,0])
	{
	//This tries to center the text (for RTL text).
    ttb_btt_inaction = (direction=="ttb" || direction=="btt") ? 0 : 1;
	rotate( -rtl_sign * ttb_btt_inaction * rotation_for_center_text_string(t, size, spacing, rr, rotate, center), [0,0,1])
	{
	    translate(locn_vec)
		if ( rounded == false ){
			__internal_text_on_sphere_helper(t=t,r=rr,
	    		rotate=rotate,
	    		center=center,
	    		scale=scale,
	            font=font,size=size,
                spacing=spacing,
                direction=direction,
                language=language,script=script,halign=halign,valign=valign,
                extrusion_height=extrusion_height
			);
		}else{
		    //If rounding then clip the text inside an inner sphere and outer sphere
			intersection()
			{
			__internal_text_on_sphere_helper(t=t,r=rr,
        		rotate=rotate,
        		center=center,
        		scale=scale,
                font=font,size=size,
                spacing=spacing,
                direction=direction,
                language=language,script=script,halign=halign,valign=valign,
                extrusion_height=extrusion_height*2 //Make it proud to clip it off.
		    );

            //Shell - bounding inner and outer
			difference()
			{   //rounded outside
				sphere(rr+extrusion_height);
				// rounded inside for indented text
			    sphere( rr  );
			}

		    }
		}
		}
	}
}

//internal function -- don't call directly
//No default checks in this funciton -- done in parent
module __internal_text_on_sphere_helper(t=default_t,
                      //Object-specific
                      r,
                      //All objects
                      extrusion_height = default_extrusion_height,
                      center           = default_center,
                      scale  = default_scale,
                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      //halign           = undef,
                      //valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
)
{
    rtl_sign = (direction=="rtl") ? -1 : 1;
    ttb_btt_inaction = (direction=="ttb" || direction=="btt") ? 0 : 1;
    ttb_btt_action   = (direction=="ttb" || direction=="btt") ? 1 : 0;

    for (l=[0:len(t)-1]){
	    rotate_z_inner   = -90 + rtl_sign * ttb_btt_inaction * l * rotation_for_character(size, spacing, r, 0);

        rotate( rotate_z_inner, [0,0,1] )
		{
        translate_sign   = (direction=="ttb" || direction=="btt") ? ((direction=="btt") ? 1 : -1)  : 0 ;
        //translate_effect = (direction=="ttb" || direction=="btt") ? 1  :  0 ;
        //Translate letter to be located on the sphere in up/down direction when we are doing TTB and BTT
	    translate(r * [cos(0 + ttb_btt_action * rotation_for_character(size, spacing, r, rotate) * l ), 0, translate_sign * ttb_btt_action * sin(0 +  rotation_for_character(size, spacing, r, rotate) * l ) ])
        //Rotate letter to be tangential to the new part of the sphere when we are doing TTB and BTT
	    rotate(-90 * translate_sign * (0 + ttb_btt_action * rotation_for_character(size, spacing, r, rotate) * l )/90 , [0,1,0])
        //Flip character into position to be flush against sphere
	    rotate(90,[1,0,0])
	    rotate(90,[0,1,0])

	    //Modify the offset of the baselined text to center
	    translate([0,(center) ? -size/2 : 0 ,0])

	    text_extrude(t[l],
	        center=false,
	        rotate=rotate,
	        scale=scale,
	        font=font,
	        size=size,
            spacing=spacing,
            direction=undef, //We don't pass direction ( misaligns inside text() ). TODO: Investigate why
            language=language,script=script,
            halign="center", //This can be relaxed eventually
            valign="baseline", //Need this to keep all letters on the same line (could also support top -- otherw will make wonky letters)
            extrusion_height=extrusion_height );
		}
    }
}

//cube(size)
//cube([width,h,depth])
//NB: cube_size is used instead of size (the name size is used in the text module)
//NB: Passes through text as strings -- supports all features of text()
module text_on_cube(  t=default_t,
                      //Object-specific
                      cube_size, //number or 3-vector
                      face      = default_cube_face,
                      rightleft = default_cube_rightleft,
                      updown    = default_cube_updown,
                      //All objects
                      locn_vec = [0,0,0],
                      extrusion_height = default_extrusion_height,
                      center = undef,
                      rotate = default_rotate,
                      scale  = default_scale,
                      //All objects -- arguments as for text()
                      font             = undef,
                      size             = default_size,
                      direction        = undef,
                      halign           = undef,
                      valign           = undef,
                      language         = undef,
                      script           = undef,
                      spacing          = default_spacing
){
    //echo (str("text_on_cube:","There are " ,len(t) ," letters in t" , t));
    //echo (str("text_on_cube:","cube_size" , cube_size));

    if((halign!=undef) || (halign!=undef))
    {
        echo (str("text_on_cube:","WARNING " , "halign and valign are NOT supported."));
    }

    int_cube_size = (str(cube_size)[0] != "[") ? [cube_size,cube_size,cube_size] : cube_size;
    rotate_x = ( (face=="front") || (face=="back") || (face=="left") || (face=="right") ) ? 90
				: ((face=="bottom") ? 180 : 0) ; //Top is zero
	rotate_y = ( (face=="back") || (face=="left") || (face=="right") ) ? (face=="back") ? 180 : ((face=="left") ? -90 : 90) //Right is 90
				: 0 ; //Top, bottom, front are zero
	locn_vec_offset =
	        (face=="front") ?             [+rightleft,   -int_cube_size[1]/2, +updown]
	        :((face=="back")?             [+rightleft,   +int_cube_size[1]/2, +updown]
	            : ((face=="left")?         [-int_cube_size[0]/2, +rightleft,   +updown]
	                : ((face=="right")?    [+int_cube_size[0]/2, +rightleft,   +updown]
	                    : ((face=="top")?  [+rightleft,   +updown,             +int_cube_size[2]/2]
	                        :              [+rightleft,   -updown,             -int_cube_size[2]/2]
							)))); //bottom

		translate(locn_vec + locn_vec_offset)
		rotate(rotate_x,[1,0,0])
		rotate(rotate_y,[0,1,0])  // rotate around the y axis (z before rotation)
		text_extrude(t,
		    center=center,
		    rotate=rotate,
		    scale=scale,
		    font=font,
		    size=size,
		    spacing=spacing,
		    direction=direction, //Does this misalign inside text()? the individual character modules do.
		    language=language,
		    script=script,
		    halign=halign,
		    valign=valign,
		    extrusion_height=extrusion_height );
}

function default_if_undef(val, default_val) = (val!=undef) ? val : default_val ;

//Print a single character or a string at the desired extrusion height
//Passes on values to text() that are passed in
//TODO: Add x,y,z rotations get text facing the right way from the start)
module text_extrude( t=default_t,
                     extrusion_height = default_extrusion_height,
                     center = default_center, //Fudgy. YMMV. //TODO:center_extrusion, or extrusion offset??
                     rotate = default_rotate,
                     scale  = default_scale, //For scaling by different on axes (for widening etc)
                     //Following are test() params (in addition to t=)
                     font             = default_font,
                     size             = default_size,
                     direction        = undef,
                     halign           = undef,
                     valign           = undef,
                     language         = undef,
                     script           = undef,
                     spacing          = default_spacing
)
{
    //echo (str("text_extrude:","There are " ,len(t) ," letters in text" , t));
    //echo (str("text_extrude:","extrusion_height=" , extrusion_height));

    if(center==true)
    {
        if((halign!=undef) || (valign!=undef))
        {
            echo (str("text_extrude:","WARNING " , "When center is true, then halign and valign are NOT supported."));
        }
    }

    //As arguments are explicitly set as undef (from higher functions) as they come down (they don't get defaults in this function as they are set as something)
    //we need to check and replace any that we don't want to be undef
    t                 = default_if_undef(t,default_t);
    font              = default_if_undef(font,default_font);
    extrusion_height  = default_if_undef(extrusion_height,default_extrusion_height);
    center            = default_if_undef(center,default_center);
    rotate            = default_if_undef(rotate,default_rotate);
    spacing           = default_if_undef(spacing,default_spacing);
    size              = default_if_undef(size,default_size);

    halign            = (center) ? "center" : halign ;
    valign            = (center) ? "center" : valign ;
    extrusion_center  = (center) ? true : false ;

	scale( scale )
    rotate(rotate,[0,0,-1]) //TODO: Do we want to make this so that the entire vector can be set?
	{
	    linear_extrude(height=extrusion_height,convexity=10,center=extrusion_center)
        text(text=t, size = size,
            $fn = 40,
            font = font, direction = direction, spacing = spacing,
            halign = halign,
            valign = valign,
            language=language, script=script);
	}
}


Added chock to hold poster in 4mm polycarbonate board rail

$fa=3;
$fs=0.2;

module chock() {
    length = 200;
    module bulk() {
        hull() {
            cylinder(r=2.5, h=length, center=true);
            translate([0.5,7,0]) cylinder(r=2, h=length, center=true);
        }
    }
    bulk();
    translate([0,5,0]) cube([0.5,10,length],true);
}

rotate([0,0,0]) translate([0,-10,0]) chock();
rotate([0,0,120]) translate([0,-10,0]) chock();
rotate([0,0,240]) translate([0,-10,0]) chock();

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


decreased height of the model, as there is not enough space for 30mm length - 25mm is for sure, thus sticking with 20mm, just to be on a safe side


launcher prototype

launcher prototype

eps=0.01;
//wall=1;
//hose_phi=6;
//bottle_screw_phi=27.4 + 0.6;
//bottle_screw_h=9;
side = 22;

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

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

module triangle_pipe(side, height)
{
  linear_extrude(height=height)
    difference()
    {
      triangle_2d(side);
      triangle_2d(side-6);
    }
}

module launcher_core()
{
  dx = 40+22+6;
  module pad_core()
  {
    // pipe mount
    translate([50, 0, 11])
      cylinder(d=6, h=10, $fn=50);
    difference()
    {
      // block
      translate([-side/2, -side/2+0.75, 0])
        cube([dx, side, 11]);
      // triangle cut at the basecut
      translate([0, 0, 2+eps])
        rotate([0, 0, -30])
          linear_extrude(height=9)
            triangle_2d(12);
    }
    // launcher tube
    rotate([0, 0, -30])
      triangle_pipe(side, 180);
  }

  difference()
  {
    pad_core();
    // cut in for pressure to get in
    translate([50, 0, 10-6/3])
      cylinder(d=6-2, h=11+6/3+eps, $fn=50);
    // pipe hole
    translate([1-4, 0, 8/2+1])
      rotate([0, 90, 0])
        cylinder(d=8, h=dx-side/2, $fn=30);
  }
}

module launcher()
{
  launcher_core();
  for(dy=[-15, 16])
    translate([20, dy, 0])
      screw_mount();
}

launcher();

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


shapes: misc minor fixes/tweaks

shapes: misc minor fixes/tweaks

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

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

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

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

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

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

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

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

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

    extra_r_ = v_fallback(extra_r, [0]);

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

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

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

module rcylindera(
        h=10,
        r=undef,
        r1=undef,
        r2=undef,
        d=undef,
        d1=undef,
        d2=undef,
        align=[0,0,0],
        orient=[0,0,1],
        extra_h=0,
        extra_r=undef,
        extra_d=undef,
        extra_align=[0,0,0],
        round_r=1,
        debug=false
        )
{
    d1_ = v_fallback(d1, [r*2, r1*2]);
    d2_ = v_fallback(d2, [r*2, r2*2]);

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

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

    h_ = h+extra_h;

    extra_r_ = v_fallback(extra_r, [0]);

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

    size_align(size=[r_max*2,r_max*2,h], extra_size=[extra_r_*2, extra_r_*2, extra_h], orient=orient, orient_ref=[0,0,1], align=align, extra_align=extra_align)
    {
        if($preview_mode || round_r == 0)
        {
            cylindera(h=h_, r1=r1_+extra_r_, r2=r2_+extra_r_);
        }
        else
        {
            r_ = [r1_,r2_];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if(false)
{
    stack(axis=X, dist=15)
    {
        hollow_cylinder(thickness=5, h=10, taper=false, orient=[0,0,1], align=[0,0,1]);
        hollow_cylinder(thickness=5, h=10, taper=true, orient=[0,0,1], align=[0,0,1]);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        rcylindera(h=10,r=5);

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

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

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

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

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

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

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

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

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

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

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

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

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

    extra_r_ = v_fallback(extra_r, [0]);

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

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

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

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

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

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

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

    h_ = h+extra_h;

    extra_r_ = v_fallback(extra_r, [0]);

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

    size_align(size=[r_max*2,r_max*2,h], extra_size=[extra_r_*2, extra_r_*2, extra_h], orient=orient, orient_ref=[0,0,1], align=align, extra_align=extra_align)
    {
        if($preview_mode || round_r == 0)
        {
            cylindera(h=h_, r1=r1_+extra_r_, r2=r2_+extra_r_);
        }
        else
        {
            r_ = [r1_,r2_];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if(false)
{
    stack(axis=X, dist=15)
    {
        hollow_cylinder(thickness=5, h=10, taper=false, orient=[0,0,1], align=[0,0,1]);
        hollow_cylinder(thickness=5, h=10, taper=true, orient=[0,0,1], align=[0,0,1]);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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