// ---------------------------------------------------------------------------
// (c) 2006 KystAtlas, Hans Martin Mohn
// ---------------------------------------------------------------------------
// Utility class for matrix manipulation
//
// Dependencies:
gtAssert("gtPoint != null");
//

// ---------------------------------------------------------------------------
// class gtMatrix
// ---------------------------------------------------------------------------

function gtMatrix(matrix_string)
{
  //
  // Member variables
  //
  if (matrix_string)
    this.setFromString(matrix_string);
  else
  {
    this.m_sx = 1.0;
    this.m_sy = 1.0;
    this.m_rx = 0.0;
    this.m_ry = 0.0;
    this.m_tx = 0.0;
    this.m_ty = 0.0;
  }
} // end gtMatrix



// Methods
gtMatrix.prototype.set = function set(sx, sy, rx, ry, tx, ty)
{
  this.m_sx = sx;
  this.m_rx = rx;
  this.m_tx = tx;
  this.m_sy = sy;
  this.m_ry = ry;
  this.m_ty = ty;
}; // end set()


gtMatrix.prototype.identity = function identity()
{
  this.m_sx = this.m_sy = 1.0;
  this.m_rx = this.m_ry = 0.0;
  this.m_tx = this.m_ty = 0.0;
}; // end identity()


gtMatrix.prototype.det = function det()
{
  return this.m_sx * this.m_sy - this.m_ry * this.m_rx;
}; // end det()


gtMatrix.prototype.invert = function invert()
{
  var d = this.det();
  set(
    this.m_sy / d,
    this.m_sx / d,
   -this.m_rx / d,
   -this.m_ry / d,
    (this.m_rx * this.m_ty - this.m_tx * this.m_sy) / d,
    (this.m_tx * this.m_ry - this.m_sx * this.m_ty) / d);
}; // end invert()


gtMatrix.prototype.inverse = function inverse()
{
  var d = this.det();
  var i = new gtMatrix();
  i.set(
    this.m_sy / d,
    this.m_sx / d,
   -this.m_rx / d,
   -this.m_ry / d,
    (this.m_rx * this.m_ty - this.m_tx * this.m_sy) / d,
    (this.m_tx * this.m_ry - this.m_sx * this.m_ty) / d);
  return i;
}; // end inverse()


gtMatrix.prototype.copy = function copy(m)
{
  this.m_sx = m.m_sx;
  this.m_sy = m.m_sy;
  this.m_rx = m.m_rx;
  this.m_ry = m.m_ry;
  this.m_tx = m.m_tx;
  this.m_ty = m.m_ty;
}; // end copy()


gtMatrix.prototype.clone = function clone()
{
  var c = new gtMatrix();
  c.copy(this);
  return c;
}; // end clone()


gtMatrix.prototype.multiply = function multiply(m)
{
  var sx1 = m.m_sx * this.m_sx + m.m_rx * this.m_ry;
  var rx1 = m.m_sx * this.m_rx + m.m_rx * this.m_sy;
  var tx1 = m.m_sx * this.m_tx + m.m_rx * this.m_ty + m.m_tx;
  var ry1 = m.m_ry * this.m_sx + m.m_sy * this.m_ry;
  var sy1 = m.m_ry * this.m_rx + m.m_sy * this.m_sy;
  this.m_ty = m.m_ry * this.m_tx + m.m_sy * this.m_ty + m.m_ty;

  this.m_sx = sx1;
  this.m_rx = rx1;
  this.m_sy = sy1;
  this.m_ry = ry1;
  this.m_tx = tx1;
}; // end multiply()


gtMatrix.prototype.scale = function scale(x, y)
{
  var tmp = new gtMatrix();
  tmp.m_sx = x;
  tmp.m_sy = y;
  this.multiply(tmp);
}; // end scale()



gtMatrix.prototype.scaleAround = function gtmatrix_scaleAround(sx, sy, x, y)
{
  this.translate(-x, -y);
  this.scale(sx, sy);
  this.translate(x, y);
// The following was taken from Pablo, BUT IT DOESN'T WORK!!!
//  var tmp1 = -(x * sx);
//  var tmp2 = tmp1 + x;
//  var tmp3 = sy - 1;

//  this.set(
//    this.m_sx * sx,
//    this.m_rx * sy,
//    this.m_tx + this.m_sx * tmp2 - tmp3 * y * this.m_rx,
//    this.m_sy * sy,
//    this.m_ry * sx,
//    this.m_ty + this.m_ry * tmp2 - tmp3 * y * this.m_sy);
}; // end scaleAround()



gtMatrix.prototype.rotate = function rotate(angle)
{
  var tmp = new gtMatrix();
  var cv  = Math.cos(angle / 180.0 * Math.PI);
  var sv  = Math.sin(angle / 180.0 * Math.PI);
  tmp.m_sx =  cv;
  tmp.m_rx = -sv;
  tmp.m_ry =  sv;
  tmp.m_sy =  cv;
  this.multiply(tmp);
}; // end rotate()


gtMatrix.prototype.translate = function translate(x, y)
{
  var tmp = new gtMatrix();

  tmp.m_tx = x;
  tmp.m_ty = y;
  this.multiply(tmp);
}; // end translate()


// Transform point p
gtMatrix.prototype.transformPoint = function transformPoint(p)
{
  var x = this.m_sx * p.x + this.m_rx * p.y + this.m_tx;
  p.y = this.m_sy * p.y + this.m_ry * p.x + this.m_ty;
  p.x = x;
  return p;
}; // end transformPoint()



gtMatrix.prototype.transformPolygon = function transformPolygon(p)
{
  var count = p.getPointCount();
  for (var i = 0; i < count; i++)
  {
    var x    = this.m_sx * p.m_x[i] + this.m_rx * p.m_y[i] + this.m_tx;
    p.m_y[i] = this.m_sy * p.m_y[i] + this.m_ry * p.m_x[i] + this.m_ty;
    p.m_x[i] = x;
  }
  return p;
}; // end transformPointsString()



// The points string contains coordinates separated by spaces or commas
gtMatrix.prototype.transformPointsString = function transformPointsString(points)
{
  var new_coords = "";
  var p = points.split(/[, ]/g);
  for (var i = 0; i < p.length; i += 2)
  {
    var x = parseFloat(p[i]);
    var y = parseFloat(p[i + 1]);
    new_coords += (this.m_sx * x + this.m_rx * y + this.m_tx) + "," + (this.m_sy * y + this.m_ry * x + this.m_ty) + " ";
  }

  if (new_coords.length > 0)
    return new_coords.substring(0, new_coords.length - 1);
  else
    return new_coords;
}; // end transformPointsString()


// The points string contains coordinates separated by path commands and commas
gtMatrix.prototype.transformPathString = function transformPathString(path)
{
  var new_coords = "";
  var re = /[0-9\.-]*/g;  // Floating point number
  var x = 0.0;
  var y = 0.0;
  var arr;
  while ((arr = re.exec(path)) != null)
  {
    if (arr[0].length === 0)
    {
      var sep = path.substring(arr.index, arr.lastIndex);
      if (sep != ",")
        new_coords += sep;  // Path command: M, L, Z, etc
    }
    else
    {
      // A number.
      if (x == 0.0)
        x = parseFloat(arr[0]);
      else
      {
        y = parseFloat(arr[0]);
        new_coords += (this.m_sx * x + this.m_rx * y + this.m_tx).toPrecision(12) + "," + (this.m_sy * y + this.m_ry * x + this.m_ty).toPrecision(12);
        x = 0.0; // So that next number is treated as an x!
      }
    }
  }
  return new_coords;
}; // end transformPathString()



gtMatrix.prototype.toString = function toString()
{
  return "m_sx=" + this.m_sx + ", m_sy=" + this.m_sy + ", m_rx=" + this.m_rx + ", m_ry=" + this.m_ry + ", m_tx=" + this.m_tx + ", m_ty=" + this.m_ty;
}; // end toString()


gtMatrix.prototype.setFromString = function setFromString(s)
{
  var values = s.split(",");
  if (values.length == 6)
  {
    this.set(
      parseFloat(values[0]), parseFloat(values[1]),
      parseFloat(values[2]), parseFloat(values[3]),
      parseFloat(values[4]), parseFloat(values[5]));
  }
}; // end setFromString()


// ---------------------------------------------------------------------------
