// Perform an affine transformation of matrix M on coordinate v.
//
// [Scale X]          [Shear X along Y] [Shear X along Z] [Translate X]
// [Shear Y along X]  [Scale Y]         [Shear Y along Z] [Translate Y]
// [Shear Z along X]  [Shear Z along Y] [Scale Z]         [Translate Z]
// or rotation matrix [[cos,-sin],[sin,cos]] in the 2 axes for a plane.
function Affine(M, v) = M * [v[0], v[1], v[2], 1];


// Combine a list of affine transformation matrices into one.
function AffMerge(Mlist) = let(
    AffMergeRec = function(Mlist, i) i >= len(Mlist) ?
      [[1,0,0,0],[0,1,0,0],[0,0,1,0]] :
      let (
        rest = AffMergeRec(Mlist, i+1),
        prod = Mlist[i] * [rest[0], rest[1], rest[2], [0,0,0,1]]
      )
      [prod[0], prod[1], prod[2]]
  )
  AffMergeRec(Mlist, 0);


// Prepare a matrix to rotate around the x-axis.
function RotX(a) =
  [[     1,      0,       0, 0],
   [     0, cos(a), -sin(a), 0],
   [     0, sin(a),  cos(a), 0]];

// Prepare a matrix to rotate around the y-axis.
function RotY(a) =
  [[ cos(a), 0, sin(a), 0],
   [     0,  1,      0, 0],
   [-sin(a), 0, cos(a), 0]];

// Prepare a matrix to rotate around the z-axis.
function RotZ(a) =
  [[cos(a), -sin(a), 0, 0],
   [sin(a),  cos(a), 0, 0],
   [     0,       0, 1, 0]];

// Prepare a matrix to rotate around x, then y, then z.
function Rotate(rotvec) =
  AffMerge([RotZ(rotvec[2]), RotY(rotvec[1]), RotX(rotvec[0])]);

// Prepare a matrix to translate by vector v.
function Translate(v) =
  [[1, 0, 0, v[0]],
   [0, 1, 0, v[1]],
   [0, 0, 1, v[2]]];

// Prepare a matrix to scale by vector v.
function Scale(v) =
  [[v[0],    0,    0, 0],
   [   0, v[1],    0, 0],
   [   0,    0, v[2], 0]];


$fn=64;ang1=45;ang2=15;rad1=4;rad2=10;
rotate([0,0,ang2]) translate([0,-rad2]){
    %cylinder(h=.001,r=2);
    rotate([0,0,ang1]) translate([0,-rad1]) cylinder(h=.001,r=2);
}

M = AffMerge([Rotate([0,0,ang2]), Translate([0,-rad2,0]), Rotate([0,0,ang1]), Translate([0,-rad1,0])]);
p = Affine(M, [0,0,0]);
echo(p);
#translate([p[0]+2, p[1]<0 ? p[1] : 0, 0]) cube([6.95,abs(p[1]),.001]);
