/* Copyright (C) 2008 Jonathon Jongsma
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */
#ifndef __CAIROMM_MATRIX_H
#define __CAIROMM_MATRIX_H

#include <cairommconfig.h>

#include <cairo.h>

namespace Cairo
{

/** @class cairo_matrix_t
 * See the <a
 * href="http://www.cairographics.org/manual/cairo-matrix.html">cairo_matrix_t
 * reference</a> in the cairo manual for more information
 */

/** A Transformation matrix.
 *
 * Cairo::Matrix is used throughout cairomm to convert between different
 * coordinate spaces. A Matrix holds an affine transformation, such as
 * a scale, rotation, shear, or a combination of these. The transformation of
 * a point (x,y) is given by:
 *
 * @code
 * x_new = xx * x + xy * y + x0;
 * y_new = yx * x + yy * y + y0;
 * @endcode
 *
 * The current transformation matrix of a Context, represented as a
 * Matrix, defines the transformation from user-space coordinates to
 * device-space coordinates.
 * @sa Context::get_matrix()
 * @sa Context::set_matrix()
 */
class CAIROMM_API Matrix : public cairo_matrix_t
{
public:
  /** Creates an uninitialized matrix.  If you want a matrix initialized to a
   * certain value, either specify the values explicitly with the other
   * constructor or use one of the free functions for initializing matrices with
   * specific scales, rotations, etc.
   *
   * @sa identity_matrix()
   * @sa rotation_matrix()
   * @sa translation_matrix()
   * @sa scaling_matrix()
   */
  Matrix();

  /** Creates a matrix Sets to be the affine transformation given by xx, yx, xy,
   * yy, x0, y0. The transformation is given by:
   *
   * @code
   * x_new = xx * x + xy * y + x0;
   * y_new = yx * x + yy * y + y0;
   * @endcode
   *
   * @param xx xx component of the affine transformation
   * @param yx yx component of the affine transformation
   * @param xy xy component of the affine transformation
   * @param yy yy component of the affine transformation
   * @param x0 X translation component of the affine transformation
   * @param y0 Y translation component of the affine transformation
   */
  Matrix(double xx, double yx, double xy, double yy, double x0, double y0);

  /** Applies a translation by tx, ty to the transformation in matrix. The
   * effect of the new transformation is to first translate the coordinates by
   * tx and ty, then apply the original transformation to the coordinates.
   *
   * @param tx amount to translate in the X direction
   * @param ty amount to translate in the Y direction
   */
  void translate(double tx, double ty);

  /** Applies scaling by sx, sy to the transformation in matrix. The effect of
   * the new transformation is to first scale the coordinates by sx and sy, then
   * apply the original transformation to the coordinates.
   *
   * @param sx scale factor in the X direction
   * @param sy scale factor in the Y direction
   */
  void scale(double sx, double sy);

  /** Applies rotation by radians to the transformation in matrix. The effect of
   * the new transformation is to first rotate the coordinates by radians, then
   * apply the original transformation to the coordinates.
   *
   * @param radians angle of rotation, in radians. The direction of rotation is
   * defined such that positive angles rotate in the direction from the positive
   * X axis toward the positive Y axis. With the default axis orientation of
   * cairo, positive angles rotate in a clockwise direction.
   */
  void rotate(double radians);

  /** Changes matrix to be the inverse of it's original value. Not all
   * transformation matrices have inverses; if the matrix collapses points
   * together (it is degenerate), then it has no inverse and this function will
   * throw an exception.
   *
   * @exception
   */
  void invert();

  /** Multiplies the affine transformations in a and b together and stores the
   * result in this matrix. The effect of the resulting transformation is to first
   * apply the transformation in a to the coordinates and then apply the
   * transformation in b to the coordinates.
   *
   * It is allowable for result to be identical to either a or b.
   *
   * @param a a Matrix
   * @param b a Matrix
   *
   * @sa operator*()
   */
  void multiply(Matrix& a, Matrix& b);

  /** Transforms the distance vector (dx,dy) by matrix. This is similar to
   * transform_point() except that the translation components of the
   * transformation are ignored. The calculation of the returned vector is as
   * follows:
   *
   * @code
   * dx2 = dx1 * a + dy1 * c;
   * dy2 = dx1 * b + dy1 * d;
   * @endcode
   *
   * Affine transformations are position invariant, so the same vector always
   * transforms to the same vector. If (x1,y1) transforms to (x2,y2) then
   * (x1+dx1,y1+dy1) will transform to (x1+dx2,y1+dy2) for all values of x1 and
   * x2.
   *
   * @param dx X component of a distance vector. An in/out parameter
   * @param dy Y component of a distance vector. An in/out parameter
   */
  void transform_distance(double& dx, double& dy) const;

  /** Transforms the point (x, y) by this matrix.
   *
   * @param x X position. An in/out parameter
   * @param y Y position. An in/out parameter
   */
  void transform_point(double& x, double& y) const;
};

/** Returns a Matrix initialized to the identity matrix
 *
 * @relates Matrix
 */
CAIROMM_API
Matrix identity_matrix();

/** Returns a Matrix initialized to a transformation that translates by tx and
 * ty in the X and Y dimensions, respectively.
 *
 * @param tx amount to translate in the X direction
 * @param ty amount to translate in the Y direction
 *
 * @relates Matrix
 */
CAIROMM_API
Matrix translation_matrix(double tx, double ty);

/** Returns a Matrix initialized to a transformation that scales by sx and sy in
 * the X and Y dimensions, respectively.
 *
 * @param sx scale factor in the X direction
 * @param sy scale factor in the Y direction
 *
 * @relates Matrix
 */
CAIROMM_API
Matrix scaling_matrix(double sx, double sy);

/** Returns a Matrix initialized to a transformation that rotates by radians.
 *
 * @param radians angle of rotation, in radians. The direction of rotation is
 * defined such that positive angles rotate in the direction from the positive X
 * axis toward the positive Y axis. With the default axis orientation of cairo,
 * positive angles rotate in a clockwise direction.
 *
 * @relates Matrix
 */
CAIROMM_API
Matrix rotation_matrix(double radians);

/** Multiplies the affine transformations in a and b together and returns the
 * result. The effect of the resulting transformation is to first
 * apply the transformation in a to the coordinates and then apply the
 * transformation in b to the coordinates.
 *
 * It is allowable for result to be identical to either a or b.
 *
 * @param a a Matrix
 * @param b a Matrix
 *
 * @relates Matrix
 */
CAIROMM_API
Matrix operator*(const Matrix& a, const Matrix& b);

} // namespace Cairo

#endif // __CAIROMM_MATRIX_H
