//---------------------------------------------------------------
//-- Openscad Bevel library
//-- Bevel the edges or add buttress to your parts!
//---------------------------------------------------------------
//-- This is a component of the obiscad opescad tools by Obijuan
//-- (C) Juan Gonzalez-Gomez (Obijuan)
//-- Sep-2012
//---------------------------------------------------------------
//-- Released under the GPL license
//---------------------------------------------------------------
use <vector.scad>
use <attach.scad>

//-----------------------------------------------------------------
//- Rotate a vector an angle teta around the axis given by the
//-- unit vector k
//-----------------------------------------------------------------
function Rot_axis_ang(p,k,teta) =
  p*cos(teta) + cross(k,p*sin(teta)) + k*dot(k,p)*(1-cos(teta));

//-- Transformation defined by rotating vfrom vector to vto
//-- It is applied to vector v
//-- It returns the transformed vector
function Tovector(vfrom, vto, v) = 
   Rot_axis_ang(v, unitv(cross(vfrom,vto)), anglev(vfrom,vto));

//-- Auxiliary function for extending a vector of 3 components to 4
function ev(v,c=0) = [v[0], v[1], v[2], c];

//-- Calculate the determinant of a matrix given by 3 row vectors
function det(a,b,c) = 
   a[0]*(b[1]*c[2]-b[2]*c[1])
 - a[1]*(b[0]*c[2]-b[2]*c[0])  
 + a[2]*(b[0]*c[1]-b[1]*c[0]);


//-- Sign function. It only returns 2 values: -1 when x is negative,
//-- or 1 when x=0 or x>0
function sign2(x) = sign(x)+1 - abs(sign(x));

//--------------------------------------------------------------------
//-- Beveled concave corner
//-- NOT AN INTERFACE MODULE (The user should call bconcave_corner instead)
//--
//-- Parameters:
//--   * cr: Corner radius
//--   * cres: Corner resolution
//--   * l: Length
//-    * th: Thickness
//--------------------------------------------------------------------
module bconcave_corner_aux(cr,cres,l,th)
{
  
  //-- vector for translating the  main cube
  //-- so that the top rigth corner is on the origin
  v1 = -[(cr+th)/2, (cr+th)/2, 0];

  //-- The part frame of reference is on the
  //-- internal corner
  v2 = [cr,cr,0];

  //-- Locate the frame of ref. in the internal
  //-- corner
  translate(v2)
  difference() {

    //-- Main cube for doing the corner
    translate(v1)
        //color("yellow",0.5)
        cube([cr+th, cr+th, l],center=true);
 
    //-- Cylinder used for beveling...
    cylinder(r=cr, h=l+1, center=true, $fn=4*(cres+1));
  }
}


//-----------------------------------------------------------------------------
//-- API MODULE
//--
//-- Beveled concave corner
//--
//-- Parameters:
//--   * cr: Corner radius
//--   * cres: Corner resolution
//--   * l: Length
//-    * th: Thickness
//--   * ext_corner: Where the origin is locate. By default it is located
//--       in the internal corner (concave zone). If true, 
//--       it will be in the external corner (convex zone)
//----------------------------------------------------------------------------
module bconcave_corner(cr=1,cres=4,th=1,l=10,ext_corner=false)
{
  //-- Locate the origin in the exterior edge
  if (ext_corner==true)
    translate([th,th,0]) 
      bconcave_corner_aux(cr,cres,l,th);
  else
     //-- Locate the origin in the interior edge
     translate([0.01, 0.01,0])
       bconcave_corner_aux(cr,cres,l,th);
}

//----------------------------------------------------------------------
//-- Auxiliary module (NOT FOR THE USER!)
//-- It is and standar "attach", particularized for placing concave
//-- corners
//----------------------------------------------------------------------
module bconcave_corner_attach_final(
        cfrom,  //-- Origin connector
        cto,    //-- Target connector
        cr,
        cres,
        l,
        th,
        ext_corner)
{
 
  //-- This block represent an attach operation
  //-- It is equivalent to:  attach(cto,cfrom)
  translate(cto[0])
    rotate(a=cto[2], v=cto[1])
      rotate(a=anglev(cfrom[1],cto[1]), 
             v=cross(cfrom[1],cto[1]) )
        translate(-cfrom[0]) 

  //-- Place the concave corner (along with some debug information)
  union() {
    //color("Blue")
    //connector(cfrom);
    //connector([cfrom[0],cnormal_v,0]);
    bconcave_corner(cr=cr,
             cres=cres, 
             l=l,
             th=th,
             ext_corner=ext_corner);
  }
}


//-------------------------------------------------------------------------
//-- Auxiliary module (NOT FOR THE USER!)
//-- It is the general module for performing the bconcave corner attach
//-- All the parameters should be passed to it
//--
//--  External connectors are where de concave corner will be placed. They
//--  are provided by the user
//--
//--  Internal connectors refers to the connectors of the concave corner
//--
//--  Then an attach between the internal and external connectors is done
//-------------------------------------------------------------------------
module bconcave_corner_attach_aux(

         //-- External connectors
         edge_c, 
         normal_c,

         //-- Internal connectors
         iedge_c,
         inormal_c,

	 //-- Other params
         cr,
         cres,
         th,
         l,
         ext_corner)

{
  //-- Get the Corner vectors from the internal connectors
  cedge_v = iedge_c[1];         //-- Corner edge vector
  cnormal_v = inormal_c[1];     //-- Corner normal vector

  //-- Get the vector paralell and normal to the edge
  //-- From the external connectors
  edge_v = edge_c[1];      //-- Edge verctor
  enormal_v = normal_c[1]; //-- Edge normal vector

  //---------------------------------------------------------------
  //-- For doing a correct attach, first the roll angle for the  
  //-- external connector should be calculated. It determines the
  //-- orientation of the concave corner around the edge vector
  //--
  //-- This orientation is calculated using the edge normal vectors
  //-- that bisec the corner
  //--
  //-- There are 2 different cases: depending on the relative angle
  //-- between the internal and external edges. They can be parallel
  //-- or not
  //-----------------------------------------------------------------
  //-- The roll angle has two components: the value and the sign

  //-- Calculate the sign of the rotation (the sign of roll)
  s=sign2(det(cnormal_v,enormal_v,edge_v));

  //-- Calculate the roll when the edges are paralell
  rollp = s*anglev(cnormal_v, enormal_v);

  //-- Calculate the roll in the general case
  Tcnormal_v = Tovector(cedge_v, edge_v, cnormal_v);
  rollg=s*anglev(Tcnormal_v, enormal_v);

  //-- For the paralell case... use rollp
  if (mod(cross(cedge_v,edge_v))==0) {
    //echo("Paralell");

     //-- Place the concave bevel corner!
     bconcave_corner_attach_final(
       cfrom = [[0,0,0],   cedge_v,   0],
       cto   = [edge_c[0], edge_c[1], rollp],
       cr    = cr,
       cres  = cres,
       l     = l,
       th    = th,
       ext_corner = ext_corner);
  }

  //-- For the general case, use rollg
  else {
    //echo("not paralell");

     //-- Place the concave bevel corner!
     bconcave_corner_attach_final(
       cfrom = [[0,0,0],   cedge_v,   0],
       cto   = [edge_c[0], edge_c[1], rollg],
       cr    = cr,
       cres  = cres,
       l     = l,
       th    = th,
       ext_corner = ext_corner);
  }
}

//---------------------------------------------------------------------------
//-- API MODULE
//--
//--  Bevel an edge. A concave corner is located so that the calling 
//--  module can easily perform a difference() operation
//--
//--  Two connectors are needed:
//--    * edge_c   : Connector located on the edge, paralell to the edge
//--    * normal_c : Connector located on the same point than edge_c 
//--                 pointing to the internal corner part, in the direction
//--                 of the corner bisector
//--    * cr        : Corner radius
//--    * cres      : Corner resolution
//--    * l         : Corner length
//--------------------------------------------------------------------------  
module bevel(
           edge_c, 
           normal_c,
           cr=3,
           cres=3,
           l=5)
{

  //-- Call the general module with the correct internal connectors
  bconcave_corner_attach_aux(

         //-- External connectors
         edge_c   = edge_c,
         normal_c = normal_c,

	 //-- Internal connectors 
         iedge_c   = [[0,0,0], unitv([0,0,1]), 0],
         inormal_c = [[0,0,0], [-1,-1,0]       , 0],

         //-- The other params
         cr=cr,
         cres=cres,
         l=l,
         th=1,
         ext_corner=false);
}


//---------------------------------------------------------------------------
//-- API MODULE
//--
//--  Attach a Beveled concave corner
//--  Two connectors are needed:
//--    * edge_c   : Connector located on the edge, paralell to the edge
//--    * normal_c : Connector located on the same point than edge_c 
//--                 pointing to the internal corner part, in the direction
//--                 of the corner bisector
//--    * cr        : Corner radius
//--    * cres      : Corner resolution
//--    * l         : Corner length
//--    * th        : Corner thickness (not visible when ext_corner=false)
//--    * ext_corner: If the exterior corner is used as a reference
//--------------------------------------------------------------------------  
module bconcave_corner_attach(
           edge_c, 
           normal_c,
           cr=3,
           cres=3,
           l=5, 
           th=1,
           ext_corner=false)
{

  //-- Call the general module with the correct internal connectors
  bconcave_corner_attach_aux(

         //-- External connectors
         edge_c   = edge_c,
         normal_c = normal_c,

	 //-- Internal connectors 
         iedge_c   = [[0,0,0], unitv([0,0,1]), 0],
         inormal_c = [[0,0,0], [1,1,0]       , 0],

         //-- The other params
         cr=cr,
         cres=cres,
         l=l,
         th=th,
         ext_corner=ext_corner);

}
  
//-----------------------------------------------------------
//---   TEST MODULES
//-----------------------------------------------------------

//-----------------------------------------------------------------
//--  Testing the Bevel operator... All the 12 edges of a cube
//--  are beveled. All the cases are covered, so it is a good
//--  test for finding bugs!
//----------------------------------------------------------------
module Test1_beveled_cube()
{
  //-------- Main object
  size=[30,30,30];


  //-- Define all the edges connectors
  ec1 = [[size[0]/2, 0,size[2]/2], [0,1,0], 0];
  en1 = [ec1[0],                   [1,0,1], 0];

  ec2 = [[-size[0]/2, 0,size[2]/2], [0,1,0], 0];
  en2 = [ec2[0],                   [-1,0,1], 0];

  ec3 = [[-size[0]/2, 0,-size[2]/2], [0,1,0], 0];
  en3 = [ec3[0],                   [-1,0,-1], 0];

  ec4 = [[size[0]/2, 0,-size[2]/2], [0,1,0], 0];
  en4 = [ec4[0],                   [1,0,-1], 0];

  ec5 = [[0, size[0]/2,size[2]/2], [1,0,0], 0];
  en5 = [ec5[0],                   [0,1,1], 0];

  ec6 = [[0, -size[0]/2,size[2]/2], [1,0,0], 0];
  en6 = [ec6[0],                   [0,-1,1], 0];

  ec7 = [[0, -size[0]/2,-size[2]/2], [1,0,0], 0];
  en7 = [ec7[0],                   [0,-1,-1], 0];

  ec8 = [[0, size[0]/2,-size[2]/2], [1,0,0], 0];
  en8 = [ec8[0],                    [0,1,-1], 0];

  ec9 = [[size[2]/2, size[0]/2,0 ], [0,0,1], 0];
  en9 = [ec9[0],                    [1,1,0], 0];

  ec10 = [[size[2]/2, -size[0]/2,0 ], [0,0,1], 0];
  en10 = [ec10[0],                    [1,-1,0], 0];

  ec11 = [[-size[2]/2, -size[0]/2,0 ], [0,0,1], 0];
  en11 = [ec11[0],                    [-1,-1,0], 0];

  ec12 = [[-size[2]/2, size[0]/2,0 ], [0,0,1], 0];
  en12 = [ec12[0],                    [-1,1,0], 0];


  //-- for Debuging... Show a specefic connector
  *connector(ec12);
  *connector(en12);

  //-- Parameters for all the beveled edges
  //-- It can be changed for testing
  cr=2;
  cres=0;

  //-- Remove from the main cube the concave corner parts
  difference() {

    //-- Draw the main cube
    cube(size,center=true); 

    //-- Attach the concave corners for beveling!
    bevel(ec1,en1,cr=cr,cres=0, l=size[1]+2);
    bevel(ec2,en2,cr=cr,cres=0, l=size[1]+2);
    bevel(ec3,en3,cr=cr,cres=0, l=size[1]+2);
    bevel(ec4,en4,cr=cr,cres=0, l=size[1]+2);

    bevel(ec5,en5,cr=cr,cres=0, l=size[0]+2);
    bevel(ec6,en6,cr=cr,cres=0, l=size[0]+2);
    bevel(ec7,en7,cr=cr,cres=0, l=size[0]+2);
    bevel(ec8,en8,cr=cr,cres=0, l=size[0]+2);

    bevel(ec9,en9,cr=cr,cres=0, l=size[0]+2);
    bevel(ec10,en10,cr=cr,cres=0, l=size[0]+2);
    bevel(ec11,en11,cr=cr,cres=0, l=size[0]+2);
    bevel(ec12,en12,cr=cr,cres=0, l=size[0]+2);

  }
}

//----------------------------------------------------------------
//-- Testing the bconcave_corner_attach operator
//-- It is used for adding buttress between two ortogonal parts
//----------------------------------------------------------------
module Test2_buttress()
{
  size=[30,30,30];
  th=3;
  l=2;
  cr = 6;
 

  //-- A cross. It divides the space in 4 quadrants
  difference() {  
    cube(size,center=true);
    translate([size[0]/4 + th/2, 0, size[0]/4 + th/2])
      cube([size[0]/2, size[1]+2, size[2]/2],center=true);

    translate([-size[0]/4 - th/2, 0, size[0]/4 + th/2])
      cube([size[0]/2, size[1]+2, size[2]/2],center=true);

    translate([-size[0]/4 - th/2, 0, -size[0]/4 - th/2])
      cube([size[0]/2, size[1]+2, size[2]/2],center=true);

    translate([size[0]/4 + th/2, 0, -size[0]/4 - th/2])
      cube([size[0]/2, size[1]+2, size[2]/2],center=true);
  }

  ec1 = [[th/2, size[1]/2-l/2, th/2], [0,1,0], 0];
  en1 = [ec1[0],[1,0,1],0];

  ec2 = [[th/2, -size[1]/2+l/2, th/2], [0,1,0], 0];
  en2 = [ec2[0],[1,0,1],0];

  ec3 = [[-th/2, 0, th/2], [0,1,0], 0];
  en3 = [ec3[0],[-1,0,1],0];

  ec4 = [[-th/2, 0, -th/2], [0,1,0], 0];
  en4 = [ec4[0],[-1,0,-1],0]; 

  ec5 = [[th/2, 0, -th/2], [0,1,0], 0];
  en5 = [ec5[0],[1,0,-1],0]; 

  *connector(ec5);
  *connector(en5);
 
  //-- quadrant 1:  two buttress
  bconcave_corner_attach(ec1,en1,cr, l=l, cres=0);
  bconcave_corner_attach(ec2,en2,cr, l=l, cres=0);
  
  //-- quadrant 2:  one bit buttress
  bconcave_corner_attach(ec3,en3,cr=3, l=size[1], cres=0);

  //-- quadrant 3: a Rounded buttress
  bconcave_corner_attach(ec4,en4,cr=8, l=size[1], cres=5);

  //-- Quadrant 4: A rounded buttress in the middle
  bconcave_corner_attach(ec5,en5,cr=8, l=size[1]/3, cres=5);

}

//-------------------------------------------------------------------
//--   TESTS
//-------------------------------------------------------------------

//-- example 1: A beveled concave corner
bconcave_corner(cr=15, cres=10, l=10, th=3, ext_corner=true);

//-- Example 2: Testing the bevel() operator
//Test1_beveled_cube();

//-- Example 3: Testing the bconcave_corner_attach() operator
//Test2_buttress();