gimp/libgimpmath/gimpmatrix.c
Michael Natterer 8005eea835 Remove the "GIMP" from all "Since: GIMP 2.x" API doc comments
because it confuses gtk-doc and breaks some links. Also change the
"Index of new symbols in GIMP 2.x" sections to be what seems to be the
modern standard (looked at the GLib and GTK+ docs), and update some
other stuff.
2015-05-31 21:18:09 +02:00

886 lines
22 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpmatrix.c
* Copyright (C) 1998 Jay Cox <jaycox@gimp.org>
*
* 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 3 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
* Library 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
* <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib-object.h>
#include "gimpmath.h"
/**
* SECTION: gimpmatrix
* @title: GimpMatrix
* @short_description: Utilities to set up and manipulate 3x3
* transformation matrices.
* @see_also: #GimpVector2, #GimpVector3, #GimpVector4
*
* When doing image manipulation you will often need 3x3
* transformation matrices that define translation, rotation, scaling,
* shearing and arbitrary perspective transformations using a 3x3
* matrix. Here you'll find a set of utility functions to set up those
* matrices and to perform basic matrix manipulations and tests.
*
* Each matrix class has a 2 dimensional gdouble coeff member. The
* element for row r and column c of the matrix is coeff[r][c].
**/
#define EPSILON 1e-6
static GimpMatrix2 * matrix2_copy (const GimpMatrix2 *matrix);
/**
* gimp_matrix2_get_type:
*
* Reveals the object type
*
* Returns: the #GType for Matrix2 objects
*
* Since: 2.4
**/
GType
gimp_matrix2_get_type (void)
{
static GType matrix_type = 0;
if (!matrix_type)
matrix_type = g_boxed_type_register_static ("GimpMatrix2",
(GBoxedCopyFunc) matrix2_copy,
(GBoxedFreeFunc) g_free);
return matrix_type;
}
/*
* GIMP_TYPE_PARAM_MATRIX2
*/
#define GIMP_PARAM_SPEC_MATRIX2(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_MATRIX2, GimpParamSpecMatrix2))
static void gimp_param_matrix2_class_init (GParamSpecClass *class);
static void gimp_param_matrix2_init (GParamSpec *pspec);
static void gimp_param_matrix2_set_default (GParamSpec *pspec,
GValue *value);
static gint gimp_param_matrix2_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2);
typedef struct _GimpParamSpecMatrix2 GimpParamSpecMatrix2;
struct _GimpParamSpecMatrix2
{
GParamSpecBoxed parent_instance;
GimpMatrix2 default_value;
};
/**
* gimp_param_matrix2_get_type:
*
* Reveals the object type
*
* Returns: the #GType for a GimpMatrix2 object
*
* Since: 2.4
**/
GType
gimp_param_matrix2_get_type (void)
{
static GType spec_type = 0;
if (!spec_type)
{
static const GTypeInfo type_info =
{
sizeof (GParamSpecClass),
NULL, NULL,
(GClassInitFunc) gimp_param_matrix2_class_init,
NULL, NULL,
sizeof (GimpParamSpecMatrix2),
0,
(GInstanceInitFunc) gimp_param_matrix2_init
};
spec_type = g_type_register_static (G_TYPE_PARAM_BOXED,
"GimpParamMatrix2",
&type_info, 0);
}
return spec_type;
}
static void
gimp_param_matrix2_class_init (GParamSpecClass *class)
{
class->value_type = GIMP_TYPE_MATRIX2;
class->value_set_default = gimp_param_matrix2_set_default;
class->values_cmp = gimp_param_matrix2_values_cmp;
}
static void
gimp_param_matrix2_init (GParamSpec *pspec)
{
GimpParamSpecMatrix2 *cspec = GIMP_PARAM_SPEC_MATRIX2 (pspec);
gimp_matrix2_identity (&cspec->default_value);
}
static void
gimp_param_matrix2_set_default (GParamSpec *pspec,
GValue *value)
{
GimpParamSpecMatrix2 *cspec = GIMP_PARAM_SPEC_MATRIX2 (pspec);
g_value_set_static_boxed (value, &cspec->default_value);
}
static gint
gimp_param_matrix2_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2)
{
GimpMatrix2 *matrix1;
GimpMatrix2 *matrix2;
gint i, j;
matrix1 = value1->data[0].v_pointer;
matrix2 = value2->data[0].v_pointer;
/* try to return at least *something*, it's useless anyway... */
if (! matrix1)
return matrix2 != NULL ? -1 : 0;
else if (! matrix2)
return matrix1 != NULL;
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
if (matrix1->coeff[i][j] != matrix2->coeff[i][j])
return 1;
return 0;
}
/**
* gimp_param_spec_matrix2:
* @name: Canonical name of the param
* @nick: Nickname of the param
* @blurb: Brief desciption of param.
* @default_value: Value to use if none is assigned.
* @flags: a combination of #GParamFlags
*
* Creates a param spec to hold a #GimpMatrix2 value.
* See g_param_spec_internal() for more information.
*
* Returns: a newly allocated #GParamSpec instance
*
* Since: 2.4
**/
GParamSpec *
gimp_param_spec_matrix2 (const gchar *name,
const gchar *nick,
const gchar *blurb,
const GimpMatrix2 *default_value,
GParamFlags flags)
{
GimpParamSpecMatrix2 *cspec;
g_return_val_if_fail (default_value != NULL, NULL);
cspec = g_param_spec_internal (GIMP_TYPE_PARAM_MATRIX2,
name, nick, blurb, flags);
cspec->default_value = *default_value;
return G_PARAM_SPEC (cspec);
}
static GimpMatrix2 *
matrix2_copy (const GimpMatrix2 *matrix)
{
return (GimpMatrix2 *) g_memdup (matrix, sizeof (GimpMatrix2));
}
/**
* gimp_matrix2_identity:
* @matrix: A matrix.
*
* Sets the matrix to the identity matrix.
*/
void
gimp_matrix2_identity (GimpMatrix2 *matrix)
{
static const GimpMatrix2 identity = { { { 1.0, 0.0 },
{ 0.0, 1.0 } } };
*matrix = identity;
}
/**
* gimp_matrix2_mult:
* @matrix1: The first input matrix.
* @matrix2: The second input matrix which will be overwritten by the result.
*
* Multiplies two matrices and puts the result into the second one.
*/
void
gimp_matrix2_mult (const GimpMatrix2 *matrix1,
GimpMatrix2 *matrix2)
{
GimpMatrix2 tmp;
tmp.coeff[0][0] = (matrix1->coeff[0][0] * matrix2->coeff[0][0] +
matrix1->coeff[0][1] * matrix2->coeff[1][0]);
tmp.coeff[0][1] = (matrix1->coeff[0][0] * matrix2->coeff[0][1] +
matrix1->coeff[0][1] * matrix2->coeff[1][1]);
tmp.coeff[1][0] = (matrix1->coeff[1][0] * matrix2->coeff[0][0] +
matrix1->coeff[1][1] * matrix2->coeff[1][0]);
tmp.coeff[1][1] = (matrix1->coeff[1][0] * matrix2->coeff[0][1] +
matrix1->coeff[1][1] * matrix2->coeff[1][1]);
*matrix2 = tmp;
}
static GimpMatrix3 * matrix3_copy (const GimpMatrix3 *matrix);
/**
* gimp_matrix3_get_type:
*
* Reveals the object type
*
* Returns: the #GType for Matrix3 objects
*
* Since: 2.8
**/
GType
gimp_matrix3_get_type (void)
{
static GType matrix_type = 0;
if (!matrix_type)
matrix_type = g_boxed_type_register_static ("GimpMatrix3",
(GBoxedCopyFunc) matrix3_copy,
(GBoxedFreeFunc) g_free);
return matrix_type;
}
/*
* GIMP_TYPE_PARAM_MATRIX3
*/
#define GIMP_PARAM_SPEC_MATRIX3(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_MATRIX3, GimpParamSpecMatrix3))
static void gimp_param_matrix3_class_init (GParamSpecClass *class);
static void gimp_param_matrix3_init (GParamSpec *pspec);
static void gimp_param_matrix3_set_default (GParamSpec *pspec,
GValue *value);
static gint gimp_param_matrix3_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2);
typedef struct _GimpParamSpecMatrix3 GimpParamSpecMatrix3;
struct _GimpParamSpecMatrix3
{
GParamSpecBoxed parent_instance;
GimpMatrix3 default_value;
};
/**
* gimp_param_matrix3_get_type:
*
* Reveals the object type
*
* Returns: the #GType for a GimpMatrix3 object
*
* Since: 2.8
**/
GType
gimp_param_matrix3_get_type (void)
{
static GType spec_type = 0;
if (!spec_type)
{
static const GTypeInfo type_info =
{
sizeof (GParamSpecClass),
NULL, NULL,
(GClassInitFunc) gimp_param_matrix3_class_init,
NULL, NULL,
sizeof (GimpParamSpecMatrix3),
0,
(GInstanceInitFunc) gimp_param_matrix3_init
};
spec_type = g_type_register_static (G_TYPE_PARAM_BOXED,
"GimpParamMatrix3",
&type_info, 0);
}
return spec_type;
}
static void
gimp_param_matrix3_class_init (GParamSpecClass *class)
{
class->value_type = GIMP_TYPE_MATRIX3;
class->value_set_default = gimp_param_matrix3_set_default;
class->values_cmp = gimp_param_matrix3_values_cmp;
}
static void
gimp_param_matrix3_init (GParamSpec *pspec)
{
GimpParamSpecMatrix3 *cspec = GIMP_PARAM_SPEC_MATRIX3 (pspec);
gimp_matrix3_identity (&cspec->default_value);
}
static void
gimp_param_matrix3_set_default (GParamSpec *pspec,
GValue *value)
{
GimpParamSpecMatrix3 *cspec = GIMP_PARAM_SPEC_MATRIX3 (pspec);
g_value_set_static_boxed (value, &cspec->default_value);
}
static gint
gimp_param_matrix3_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2)
{
GimpMatrix3 *matrix1;
GimpMatrix3 *matrix2;
gint i, j;
matrix1 = value1->data[0].v_pointer;
matrix2 = value2->data[0].v_pointer;
/* try to return at least *something*, it's useless anyway... */
if (! matrix1)
return matrix2 != NULL ? -1 : 0;
else if (! matrix2)
return matrix1 != NULL;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
if (matrix1->coeff[i][j] != matrix2->coeff[i][j])
return 1;
return 0;
}
/**
* gimp_param_spec_matrix3:
* @name: Canonical name of the param
* @nick: Nickname of the param
* @blurb: Brief desciption of param.
* @default_value: Value to use if none is assigned.
* @flags: a combination of #GParamFlags
*
* Creates a param spec to hold a #GimpMatrix3 value.
* See g_param_spec_internal() for more information.
*
* Returns: a newly allocated #GParamSpec instance
*
* Since: 2.8
**/
GParamSpec *
gimp_param_spec_matrix3 (const gchar *name,
const gchar *nick,
const gchar *blurb,
const GimpMatrix3 *default_value,
GParamFlags flags)
{
GimpParamSpecMatrix3 *cspec;
cspec = g_param_spec_internal (GIMP_TYPE_PARAM_MATRIX3,
name, nick, blurb, flags);
if (default_value)
cspec->default_value = *default_value;
return G_PARAM_SPEC (cspec);
}
static GimpMatrix3 *
matrix3_copy (const GimpMatrix3 *matrix)
{
return (GimpMatrix3 *) g_memdup (matrix, sizeof (GimpMatrix3));
}
/**
* gimp_matrix3_identity:
* @matrix: A matrix.
*
* Sets the matrix to the identity matrix.
*/
void
gimp_matrix3_identity (GimpMatrix3 *matrix)
{
static const GimpMatrix3 identity = { { { 1.0, 0.0, 0.0 },
{ 0.0, 1.0, 0.0 },
{ 0.0, 0.0, 1.0 } } };
*matrix = identity;
}
/**
* gimp_matrix3_transform_point:
* @matrix: The transformation matrix.
* @x: The source X coordinate.
* @y: The source Y coordinate.
* @newx: The transformed X coordinate.
* @newy: The transformed Y coordinate.
*
* Transforms a point in 2D as specified by the transformation matrix.
*/
void
gimp_matrix3_transform_point (const GimpMatrix3 *matrix,
gdouble x,
gdouble y,
gdouble *newx,
gdouble *newy)
{
gdouble w;
w = matrix->coeff[2][0] * x + matrix->coeff[2][1] * y + matrix->coeff[2][2];
if (w == 0.0)
w = 1.0;
else
w = 1.0/w;
*newx = (matrix->coeff[0][0] * x +
matrix->coeff[0][1] * y +
matrix->coeff[0][2]) * w;
*newy = (matrix->coeff[1][0] * x +
matrix->coeff[1][1] * y +
matrix->coeff[1][2]) * w;
}
/**
* gimp_matrix3_mult:
* @matrix1: The first input matrix.
* @matrix2: The second input matrix which will be overwritten by the result.
*
* Multiplies two matrices and puts the result into the second one.
*/
void
gimp_matrix3_mult (const GimpMatrix3 *matrix1,
GimpMatrix3 *matrix2)
{
gint i, j;
GimpMatrix3 tmp;
gdouble t1, t2, t3;
for (i = 0; i < 3; i++)
{
t1 = matrix1->coeff[i][0];
t2 = matrix1->coeff[i][1];
t3 = matrix1->coeff[i][2];
for (j = 0; j < 3; j++)
{
tmp.coeff[i][j] = t1 * matrix2->coeff[0][j];
tmp.coeff[i][j] += t2 * matrix2->coeff[1][j];
tmp.coeff[i][j] += t3 * matrix2->coeff[2][j];
}
}
*matrix2 = tmp;
}
/**
* gimp_matrix3_translate:
* @matrix: The matrix that is to be translated.
* @x: Translation in X direction.
* @y: Translation in Y direction.
*
* Translates the matrix by x and y.
*/
void
gimp_matrix3_translate (GimpMatrix3 *matrix,
gdouble x,
gdouble y)
{
gdouble g, h, i;
g = matrix->coeff[2][0];
h = matrix->coeff[2][1];
i = matrix->coeff[2][2];
matrix->coeff[0][0] += x * g;
matrix->coeff[0][1] += x * h;
matrix->coeff[0][2] += x * i;
matrix->coeff[1][0] += y * g;
matrix->coeff[1][1] += y * h;
matrix->coeff[1][2] += y * i;
}
/**
* gimp_matrix3_scale:
* @matrix: The matrix that is to be scaled.
* @x: X scale factor.
* @y: Y scale factor.
*
* Scales the matrix by x and y
*/
void
gimp_matrix3_scale (GimpMatrix3 *matrix,
gdouble x,
gdouble y)
{
matrix->coeff[0][0] *= x;
matrix->coeff[0][1] *= x;
matrix->coeff[0][2] *= x;
matrix->coeff[1][0] *= y;
matrix->coeff[1][1] *= y;
matrix->coeff[1][2] *= y;
}
/**
* gimp_matrix3_rotate:
* @matrix: The matrix that is to be rotated.
* @theta: The angle of rotation (in radians).
*
* Rotates the matrix by theta degrees.
*/
void
gimp_matrix3_rotate (GimpMatrix3 *matrix,
gdouble theta)
{
gdouble t1, t2;
gdouble cost, sint;
cost = cos (theta);
sint = sin (theta);
t1 = matrix->coeff[0][0];
t2 = matrix->coeff[1][0];
matrix->coeff[0][0] = cost * t1 - sint * t2;
matrix->coeff[1][0] = sint * t1 + cost * t2;
t1 = matrix->coeff[0][1];
t2 = matrix->coeff[1][1];
matrix->coeff[0][1] = cost * t1 - sint * t2;
matrix->coeff[1][1] = sint * t1 + cost * t2;
t1 = matrix->coeff[0][2];
t2 = matrix->coeff[1][2];
matrix->coeff[0][2] = cost * t1 - sint * t2;
matrix->coeff[1][2] = sint * t1 + cost * t2;
}
/**
* gimp_matrix3_xshear:
* @matrix: The matrix that is to be sheared.
* @amount: X shear amount.
*
* Shears the matrix in the X direction.
*/
void
gimp_matrix3_xshear (GimpMatrix3 *matrix,
gdouble amount)
{
matrix->coeff[0][0] += amount * matrix->coeff[1][0];
matrix->coeff[0][1] += amount * matrix->coeff[1][1];
matrix->coeff[0][2] += amount * matrix->coeff[1][2];
}
/**
* gimp_matrix3_yshear:
* @matrix: The matrix that is to be sheared.
* @amount: Y shear amount.
*
* Shears the matrix in the Y direction.
*/
void
gimp_matrix3_yshear (GimpMatrix3 *matrix,
gdouble amount)
{
matrix->coeff[1][0] += amount * matrix->coeff[0][0];
matrix->coeff[1][1] += amount * matrix->coeff[0][1];
matrix->coeff[1][2] += amount * matrix->coeff[0][2];
}
/**
* gimp_matrix3_affine:
* @matrix: The input matrix.
* @a: the 'a' coefficient
* @b: the 'b' coefficient
* @c: the 'c' coefficient
* @d: the 'd' coefficient
* @e: the 'e' coefficient
* @f: the 'f' coefficient
*
* Applies the affine transformation given by six values to @matrix.
* The six values form define an affine transformation matrix as
* illustrated below:
*
* ( a c e )
* ( b d f )
* ( 0 0 1 )
**/
void
gimp_matrix3_affine (GimpMatrix3 *matrix,
gdouble a,
gdouble b,
gdouble c,
gdouble d,
gdouble e,
gdouble f)
{
GimpMatrix3 affine;
affine.coeff[0][0] = a;
affine.coeff[1][0] = b;
affine.coeff[2][0] = 0.0;
affine.coeff[0][1] = c;
affine.coeff[1][1] = d;
affine.coeff[2][1] = 0.0;
affine.coeff[0][2] = e;
affine.coeff[1][2] = f;
affine.coeff[2][2] = 1.0;
gimp_matrix3_mult (&affine, matrix);
}
/**
* gimp_matrix3_determinant:
* @matrix: The input matrix.
*
* Calculates the determinant of the given matrix.
*
* Returns: The determinant.
*/
gdouble
gimp_matrix3_determinant (const GimpMatrix3 *matrix)
{
gdouble determinant;
determinant = (matrix->coeff[0][0] *
(matrix->coeff[1][1] * matrix->coeff[2][2] -
matrix->coeff[1][2] * matrix->coeff[2][1]));
determinant -= (matrix->coeff[1][0] *
(matrix->coeff[0][1] * matrix->coeff[2][2] -
matrix->coeff[0][2] * matrix->coeff[2][1]));
determinant += (matrix->coeff[2][0] *
(matrix->coeff[0][1] * matrix->coeff[1][2] -
matrix->coeff[0][2] * matrix->coeff[1][1]));
return determinant;
}
/**
* gimp_matrix3_invert:
* @matrix: The matrix that is to be inverted.
*
* Inverts the given matrix.
*/
void
gimp_matrix3_invert (GimpMatrix3 *matrix)
{
GimpMatrix3 inv;
gdouble det;
det = gimp_matrix3_determinant (matrix);
if (det == 0.0)
return;
det = 1.0 / det;
inv.coeff[0][0] = (matrix->coeff[1][1] * matrix->coeff[2][2] -
matrix->coeff[1][2] * matrix->coeff[2][1]) * det;
inv.coeff[1][0] = - (matrix->coeff[1][0] * matrix->coeff[2][2] -
matrix->coeff[1][2] * matrix->coeff[2][0]) * det;
inv.coeff[2][0] = (matrix->coeff[1][0] * matrix->coeff[2][1] -
matrix->coeff[1][1] * matrix->coeff[2][0]) * det;
inv.coeff[0][1] = - (matrix->coeff[0][1] * matrix->coeff[2][2] -
matrix->coeff[0][2] * matrix->coeff[2][1]) * det;
inv.coeff[1][1] = (matrix->coeff[0][0] * matrix->coeff[2][2] -
matrix->coeff[0][2] * matrix->coeff[2][0]) * det;
inv.coeff[2][1] = - (matrix->coeff[0][0] * matrix->coeff[2][1] -
matrix->coeff[0][1] * matrix->coeff[2][0]) * det;
inv.coeff[0][2] = (matrix->coeff[0][1] * matrix->coeff[1][2] -
matrix->coeff[0][2] * matrix->coeff[1][1]) * det;
inv.coeff[1][2] = - (matrix->coeff[0][0] * matrix->coeff[1][2] -
matrix->coeff[0][2] * matrix->coeff[1][0]) * det;
inv.coeff[2][2] = (matrix->coeff[0][0] * matrix->coeff[1][1] -
matrix->coeff[0][1] * matrix->coeff[1][0]) * det;
*matrix = inv;
}
/* functions to test for matrix properties */
/**
* gimp_matrix3_is_identity:
* @matrix: The matrix that is to be tested.
*
* Checks if the given matrix is the identity matrix.
*
* Returns: %TRUE if the matrix is the identity matrix, %FALSE otherwise
*/
gboolean
gimp_matrix3_is_identity (const GimpMatrix3 *matrix)
{
gint i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
if (i == j)
{
if (fabs (matrix->coeff[i][j] - 1.0) > EPSILON)
return FALSE;
}
else
{
if (fabs (matrix->coeff[i][j]) > EPSILON)
return FALSE;
}
}
}
return TRUE;
}
/**
* gimp_matrix3_is_diagonal:
* @matrix: The matrix that is to be tested.
*
* Checks if the given matrix is diagonal.
*
* Returns: %TRUE if the matrix is diagonal, %FALSE otherwise
*/
gboolean
gimp_matrix3_is_diagonal (const GimpMatrix3 *matrix)
{
gint i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
if (i != j && fabs (matrix->coeff[i][j]) > EPSILON)
return FALSE;
}
}
return TRUE;
}
/**
* gimp_matrix3_is_affine:
* @matrix: The matrix that is to be tested.
*
* Checks if the given matrix defines an affine transformation.
*
* Returns: %TRUE if the matrix defines an affine transformation,
* %FALSE otherwise
*
* Since: 2.4
*/
gboolean
gimp_matrix3_is_affine (const GimpMatrix3 *matrix)
{
return (fabs (matrix->coeff[2][0]) < EPSILON &&
fabs (matrix->coeff[2][1]) < EPSILON &&
fabs (matrix->coeff[2][2] - 1.0) < EPSILON);
}
/**
* gimp_matrix3_is_simple:
* @matrix: The matrix that is to be tested.
*
* Checks if we'll need to interpolate when applying this matrix as
* a transformation.
*
* Returns: %TRUE if all entries of the upper left 2x2 matrix are
* either 0 or 1, %FALSE otherwise
*/
gboolean
gimp_matrix3_is_simple (const GimpMatrix3 *matrix)
{
gdouble absm;
gint i, j;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 2; j++)
{
absm = fabs (matrix->coeff[i][j]);
if (absm > EPSILON && fabs (absm - 1.0) > EPSILON)
return FALSE;
}
}
return TRUE;
}
/**
* gimp_matrix4_to_deg:
* @matrix:
* @a:
* @b:
* @c:
*
*
**/
void
gimp_matrix4_to_deg (const GimpMatrix4 *matrix,
gdouble *a,
gdouble *b,
gdouble *c)
{
*a = 180 * (asin (matrix->coeff[1][0]) / G_PI_2);
*b = 180 * (asin (matrix->coeff[2][0]) / G_PI_2);
*c = 180 * (asin (matrix->coeff[2][1]) / G_PI_2);
}