/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*-
 *
 * Pigment media rendering library
 *
 * Copyright © 2006, 2007, 2008 Fluendo Embedded S.L.
 *
 * 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 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, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Author(s): Loïc Molinari <loic@fluendo.com>
 *            Florian Boucault <florian@fluendo.com>
 */

#ifndef __PGM_LINEAR_ALGEBRA_H__
#define __PGM_LINEAR_ALGEBRA_H__

#include <glib-object.h>

G_BEGIN_DECLS

#define PGM_TYPE_VEC3   (pgm_vec3_get_type ())
#define PGM_TYPE_VEC4   (pgm_vec4_get_type ())
#define PGM_TYPE_MAT3X3 (pgm_mat3x3_get_type ())
#define PGM_TYPE_MAT4X4 (pgm_mat4x4_get_type ())

typedef struct _PgmVec3   PgmVec3;
typedef struct _PgmVec4   PgmVec4;
typedef struct _PgmMat3x3 PgmMat3x3;
typedef struct _PgmMat4x4 PgmMat4x4;

/**
 * PgmMat4x4Predefined:
 * @PGM_MAT4X4_FLIP_HORIZONTAL: Horizontal flipping.
 * @PGM_MAT4X4_FLIP_VERTICAL: Vertical flipping.
 * @PGM_MAT4X4_ROTATE_CLOCKWISE: Clockwise rotation.
 * @PGM_MAT4X4_ROTATE_COUNTER_CLOCKWISE: Counter-clockwise rotation.
 *
 * Predefined 4x4 matrices describing several common effects.
 */
typedef enum {
  PGM_MAT4X4_FLIP_HORIZONTAL,
  PGM_MAT4X4_FLIP_VERTICAL,
  PGM_MAT4X4_ROTATE_CLOCKWISE,
  PGM_MAT4X4_ROTATE_COUNTER_CLOCKWISE
} PgmMat4x4Predefined;

/**
 * PgmVec3:
 * @v: the 3 components vector array.
 *
 * Describes a 3 components vector.
 */
struct _PgmVec3 {
  /*< public >*/
  gfloat v[3];
};

/**
 * PgmVec4:
 * @v: the 4 components vector.
 *
 * Describes a 4 components vector.
 */
struct _PgmVec4 {
  /*< public >*/
  gfloat v[4];
};

/**
 * PgmMat3x3:
 * @m: the 3x3 matrix array in row-major order.
 *
 * Describes a 3x3 matrix.
 */
struct _PgmMat3x3 {
  /*< public >*/
  gfloat m[9];
};

/**
 * PgmMat4x4:
 * @m: the 4x4 matrix array in row-major order.
 *
 * Describes a 4x4 matrix.
 */
struct _PgmMat4x4 {
  /*< public >*/
  gfloat m[16];
};

/* 3-components vector methods */

GType    pgm_vec3_get_type         (void);

PgmVec3 *pgm_vec3_new              (void);

PgmVec3 *pgm_vec3_new_from_scalars (gfloat v0,
                                    gfloat v1,
                                    gfloat v2);

PgmVec3 *pgm_vec3_copy             (PgmVec3 *vec3);

void     pgm_vec3_free             (PgmVec3 *vec3);

void     pgm_vec3_set_from_scalars (PgmVec3 *vec3,
                                    gfloat v0,
                                    gfloat v1,
                                    gfloat v2);

void     pgm_vec3_set_from_vec3    (PgmVec3 *vec3,
                                    const PgmVec3 *v);

gfloat   pgm_vec3_length           (PgmVec3 *vec3);

PgmVec3 *pgm_vec3_normalize        (PgmVec3 *vec3);

gfloat   pgm_vec3_dot_product      (PgmVec3 *vec3,
                                    const PgmVec3 *v);

PgmVec3 *pgm_vec3_cross_product    (PgmVec3 *vec3,
                                    const PgmVec3 *v);

PgmVec3 *pgm_vec3_add_scalar       (PgmVec3 *vec3,
                                    gfloat s);

PgmVec3 *pgm_vec3_add_vec3         (PgmVec3 *vec3,
                                    const PgmVec3 *v);

PgmVec3 *pgm_vec3_substract_scalar (PgmVec3 *vec3,
                                    gfloat s);

PgmVec3 *pgm_vec3_substract_vec3   (PgmVec3 *vec3,
                                    const PgmVec3 *v);

PgmVec3 *pgm_vec3_multiply_scalar  (PgmVec3 *vec3,
                                    gfloat s);

PgmVec3 *pgm_vec3_multiply_vec3    (PgmVec3 *vec3,
                                    const PgmVec3 *v);

gchar   *pgm_vec3_to_string        (PgmVec3 *vec3);

/* 4-components vector methods */

GType    pgm_vec4_get_type         (void);

PgmVec4 *pgm_vec4_new              (void);

PgmVec4 *pgm_vec4_new_from_scalars (gfloat v0,
                                    gfloat v1,
                                    gfloat v2,
                                    gfloat v3);

PgmVec4 *pgm_vec4_copy             (PgmVec4 *vec4);

void     pgm_vec4_free             (PgmVec4 *vec4);

void     pgm_vec4_set_from_scalars (PgmVec4 *vec4,
                                    gfloat v0,
                                    gfloat v1,
                                    gfloat v2,
                                    gfloat v3);

void     pgm_vec4_set_from_vec4    (PgmVec4 *vec4,
                                    const PgmVec4 *v);

gfloat   pgm_vec4_length           (PgmVec4 *vec4);

PgmVec4 *pgm_vec4_normalize        (PgmVec4 *vec4);

PgmVec4 *pgm_vec4_add_scalar       (PgmVec4 *vec4,
                                    gfloat s);

PgmVec4 *pgm_vec4_add_vec4         (PgmVec4 *vec4,
                                    const PgmVec4 *v);

PgmVec4 *pgm_vec4_substract_scalar (PgmVec4 *vec4,
                                    gfloat s);

PgmVec4 *pgm_vec4_substract_vec4   (PgmVec4 *vec4,
                                    const PgmVec4 *v);

PgmVec4 *pgm_vec4_multiply_scalar  (PgmVec4 *vec4,
                                    gfloat s);

PgmVec4 *pgm_vec4_multiply_vec4    (PgmVec4 *vec4,
                                    const PgmVec4 *v);

gchar   *pgm_vec4_to_string        (PgmVec4 *vec4);

/* 3x3 matrix methods */

GType      pgm_mat3x3_get_type         (void);

PgmMat3x3 *pgm_mat3x3_new              (void);

PgmMat3x3 *pgm_mat3x3_new_from_scalars (gfloat v0,
                                        gfloat v1,
                                        gfloat v2,
                                        gfloat v3,
                                        gfloat v4,
                                        gfloat v5,
                                        gfloat v6,
                                        gfloat v7,
                                        gfloat v8);

PgmMat3x3 *pgm_mat3x3_new_from_vec3    (const PgmVec3 *v0,
                                        const PgmVec3 *v1,
                                        const PgmVec3 *v2);

PgmMat3x3 *pgm_mat3x3_new_identity     (void);

PgmMat3x3 *pgm_mat3x3_copy             (PgmMat3x3 *mat3x3);

void       pgm_mat3x3_free             (PgmMat3x3 *mat3x3);

void       pgm_mat3x3_set_from_scalars (PgmMat3x3 *mat3x3,
                                        gfloat v0,
                                        gfloat v1,
                                        gfloat v2,
                                        gfloat v3,
                                        gfloat v4,
                                        gfloat v5,
                                        gfloat v6,
                                        gfloat v7,
                                        gfloat v8);

void       pgm_mat3x3_set_from_vec3    (PgmMat3x3 *mat3x3,
                                        const PgmVec3 *v0,
                                        const PgmVec3 *v1,
                                        const PgmVec3 *v2);

void       pgm_mat3x3_set_from_mat3x3  (PgmMat3x3 *mat3x3,
                                        const PgmMat3x3 *m);

gboolean   pgm_mat3x3_is_identity      (PgmMat3x3 *mat3x3);

PgmMat3x3 *pgm_mat3x3_inverse          (PgmMat3x3 *mat3x3);

PgmMat3x3 *pgm_mat3x3_transpose        (PgmMat3x3 *mat3x3);

PgmMat3x3 *pgm_mat3x3_add_scalar       (PgmMat3x3 *mat3x3,
                                        gfloat s);

PgmMat3x3 *pgm_mat3x3_add_mat3x3       (PgmMat3x3 *mat3x3,
                                        const PgmMat3x3 *m);

PgmMat3x3 *pgm_mat3x3_substract_scalar (PgmMat3x3 *mat3x3,
                                        gfloat s);

PgmMat3x3 *pgm_mat3x3_substract_mat3x3 (PgmMat3x3 *mat3x3,
                                        const PgmMat3x3 *m);

PgmMat3x3 *pgm_mat3x3_multiply_scalar  (PgmMat3x3 *mat3x3,
                                        gfloat s);

PgmVec3   *pgm_mat3x3_multiply_vec3    (PgmMat3x3 *mat3x3,
                                        const PgmVec3 *v);

PgmMat3x3 *pgm_mat3x3_multiply_mat3x3  (PgmMat3x3 *mat3x3,
                                        const PgmMat3x3 *m);

gchar     *pgm_mat3x3_to_string        (PgmMat3x3 *mat3x3);

/* 4x4 matrix methods */

GType      pgm_mat4x4_get_type                     (void);

PgmMat4x4 *pgm_mat4x4_new                          (void);

PgmMat4x4 *pgm_mat4x4_new_from_scalars             (gfloat v0,
                                                    gfloat v1,
                                                    gfloat v2,
                                                    gfloat v3,
                                                    gfloat v4,
                                                    gfloat v5,
                                                    gfloat v6,
                                                    gfloat v7,
                                                    gfloat v8,
                                                    gfloat v9,
                                                    gfloat v10,
                                                    gfloat v11,
                                                    gfloat v12,
                                                    gfloat v13,
                                                    gfloat v14,
                                                    gfloat v15);

PgmMat4x4 *pgm_mat4x4_new_from_vec4                (const PgmVec4 *v0,
                                                    const PgmVec4 *v1,
                                                    const PgmVec4 *v2,
                                                    const PgmVec4 *v3);

PgmMat4x4 *pgm_mat4x4_new_identity                 (void);

PgmMat4x4 *pgm_mat4x4_new_predefined               (PgmMat4x4Predefined predefined);

PgmMat4x4 *pgm_mat4x4_new_translate_from_vec3      (const PgmVec3 *t);

PgmMat4x4 *pgm_mat4x4_new_translate_from_scalars   (gfloat tx,
                                                    gfloat ty,
                                                    gfloat tz);

PgmMat4x4 *pgm_mat4x4_new_scale_from_vec3          (const PgmVec3 *s);

PgmMat4x4 *pgm_mat4x4_new_scale_from_scalars       (gfloat sx,
                                                    gfloat sy,
                                                    gfloat sz);

PgmMat4x4 *pgm_mat4x4_new_rotate_x                 (gfloat angle);

PgmMat4x4 *pgm_mat4x4_new_rotate_y                 (gfloat angle);

PgmMat4x4 *pgm_mat4x4_new_rotate_z                 (gfloat angle);

PgmMat4x4 *pgm_mat4x4_new_rotate_axis_from_vec3    (gfloat angle,
                                                    const PgmVec3 *axis);

PgmMat4x4 *pgm_mat4x4_new_rotate_axis_from_scalars (gfloat angle,
                                                    gfloat axis_x,
                                                    gfloat axis_y,
                                                    gfloat axis_z);

PgmMat4x4 *pgm_mat4x4_copy                         (PgmMat4x4 *mat4x4);

void       pgm_mat4x4_free                         (PgmMat4x4 *mat4x4);

void       pgm_mat4x4_set_from_scalars             (PgmMat4x4 *mat4x4,
                                                    gfloat v0,
                                                    gfloat v1,
                                                    gfloat v2,
                                                    gfloat v3,
                                                    gfloat v4,
                                                    gfloat v5,
                                                    gfloat v6,
                                                    gfloat v7,
                                                    gfloat v8,
                                                    gfloat v9,
                                                    gfloat v10,
                                                    gfloat v11,
                                                    gfloat v12,
                                                    gfloat v13,
                                                    gfloat v14,
                                                    gfloat v15);

void       pgm_mat4x4_set_from_vec4                (PgmMat4x4 *mat4x4,
                                                    const PgmVec4 *v0,
                                                    const PgmVec4 *v1,
                                                    const PgmVec4 *v2,
                                                    const PgmVec4 *v3);

void       pgm_mat4x4_set_from_mat4x4              (PgmMat4x4 *mat4x4,
                                                    const PgmMat4x4 *m);

gboolean   pgm_mat4x4_is_identity                  (PgmMat4x4 *mat4x4);

PgmMat4x4 *pgm_mat4x4_inverse                      (PgmMat4x4 *mat4x4);

PgmMat4x4 *pgm_mat4x4_transpose                    (PgmMat4x4 *mat4x4);

void       pgm_mat4x4_translate_from_vec3          (PgmMat4x4 *mat4x4,
                                                    const PgmVec3 *t);

void       pgm_mat4x4_translate_from_scalars       (PgmMat4x4 *mat4x4,
                                                    gfloat tx,
                                                    gfloat ty,
                                                    gfloat tz);

void       pgm_mat4x4_scale_from_vec3              (PgmMat4x4 *mat4x4,
                                                    const PgmVec3 *s);

void       pgm_mat4x4_scale_from_scalars           (PgmMat4x4 *mat4x4,
                                                    gfloat sx,
                                                    gfloat sy,
                                                    gfloat sz);

void       pgm_mat4x4_rotate_x                     (PgmMat4x4 *mat4x4,
                                                    gfloat angle);

void       pgm_mat4x4_rotate_y                     (PgmMat4x4 *mat4x4,
                                                    gfloat angle);

void       pgm_mat4x4_rotate_z                     (PgmMat4x4 *mat4x4,
                                                    gfloat angle);

void       pgm_mat4x4_rotate_axis_from_vec3        (PgmMat4x4 *mat4x4,
                                                    gfloat angle,
                                                    const PgmVec3 *axis);

void       pgm_mat4x4_rotate_axis_from_scalars     (PgmMat4x4 *mat4x4,
                                                    gfloat angle,
                                                    gfloat axis_x,
                                                    gfloat axis_y,
                                                    gfloat axis_z);

PgmMat4x4 *pgm_mat4x4_add_scalar                   (PgmMat4x4 *mat4x4,
                                                    gfloat s);

PgmMat4x4 *pgm_mat4x4_add_mat4x4                   (PgmMat4x4 *mat4x4,
                                                    const PgmMat4x4 *m);

PgmMat4x4 *pgm_mat4x4_substract_scalar             (PgmMat4x4 *mat4x4,
                                                    gfloat s);

PgmMat4x4 *pgm_mat4x4_substract_mat4x4             (PgmMat4x4 *mat4x4,
                                                    const PgmMat4x4 *m);

PgmMat4x4 *pgm_mat4x4_multiply_scalar              (PgmMat4x4 *mat4x4,
                                                    gfloat s);

PgmVec4   *pgm_mat4x4_multiply_vec4                (PgmMat4x4 *mat4x4,
                                                    const PgmVec4 *v);

PgmMat4x4 *pgm_mat4x4_multiply_mat4x4              (PgmMat4x4 *mat4x4,
                                                    const PgmMat4x4 *m);

gchar     *pgm_mat4x4_to_string                    (PgmMat4x4 *mat4x4);

/* Utility functions */

PgmVec3  *pgm_intersection_line_plane (const PgmVec3 *l1,
                                       const PgmVec3 *l2,
                                       const PgmVec3 *p,
                                       const PgmVec3 *pu,
                                       const PgmVec3 *pv);

gboolean  pgm_point_belongs_rectangle (const PgmVec3 *p,
                                       const PgmVec3 *r,
                                       const PgmVec3 *ru,
                                       const PgmVec3 *rv);

G_END_DECLS

#endif /* __PGM_LINEAR_ALGEBRA_H__ */
