/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/

#include "toolMatrix.h"

#include <visu_tools.h>
#include <math.h>
#include <visu_configFile.h>
#include "toolConfigFile.h"

/**
 * SECTION:toolMatrix
 * @short_description: Defines basic handlings on matrix.
 *
 * <para>Some very basic linear algebra are redefined here. It also
 * gives access to coordinates conversion, essentially between
 * cartesian and spherical.</para>
 */

/**
 * VisuBoxVertices:
 * @vertices: (array fixed-size=8) (element-type ToolVector):
 *
 * Structure used for bindings.
 *
 * Since: 3.7
 */

/**
 * VisuBoxCell:
 * @box: (array fixed-size=6) (element-type gdouble):
 *
 * Structure used for bindings.
 *
 * Since: 3.7
 */

/**
 * ToolVector:
 *
 * An opaque object, to exchange three floats as #GObject properties.
 *
 * Since: 3.8
 */

/**
 * ToolGridSize:
 * @grid: (array fixed-size=3) (element-type guint):
 *
 * Structure used for bindings.
 *
 * Since: 3.7
 */

/**
 * tool_matrix_reducePrimitiveVectors:
 * @reduced: (out caller-allocates) (array fixed-size=6): a storage for 6 floating point values ;
 * @full: (in) (array fixed-size=9): a full 3x3 matrix to be transformed.
 *
 * This routine transforms the given matrix @full into a reduced array
 * used by V_Sim to store box definition.
 *
 * Returns: FALSE if the given matrix is planar.
 */
gboolean tool_matrix_reducePrimitiveVectors(double reduced[6], double full[3][3])
{
  double X[3];
  double Y[3];
  double Z[3];
  double u[3], x[3];
  int i, j, k;
  double deltaIJ;
  double norm;

  g_return_val_if_fail(reduced && full, FALSE);

  DBG_fprintf(stderr, "Matrix: transform full to reduced matrix.\n");
  DBG_fprintf(stderr, "Matrix: full is  %8g %8g %8g\n"
              "                 %8g %8g %8g\n"
	      "                 %8g %8g %8g\n",
	      full[0][0], full[0][1], full[0][2],
	      full[1][0], full[1][1], full[1][2],
	      full[2][0], full[2][1], full[2][2]);
  /* Compute the X vector of the new basis, colinear with old x. */
  for (i = 0; i < 3; i++)
    {
      X[i] = full[0][i];
      x[i] = full[0][i];
    }

  /* Compute the Y vector of the new basis, orthogonal to X and
     coplanar with X and old y vector. */
  u[0] = full[0][1] * full[1][2] - full[0][2] * full[1][1];
  u[1] = full[0][2] * full[1][0] - full[0][0] * full[1][2];
  u[2] = full[0][0] * full[1][1] - full[0][1] * full[1][0];
/*   DBG_fprintf(stderr, "x        : %f %f %f\n", x[0], x[1], x[2]); */
/*   DBG_fprintf(stderr, "x vect y : %f %f %f\n", u[0], u[1], u[2]); */
  deltaIJ = x[0] * u[1] - x[1] * u[0];
  if (deltaIJ != 0.)
    {
      i = 0;
      j = 1;
      k = 2;
      DBG_fprintf(stderr, " Using deltaIJ scheme with (i, j, k)"
		  " = (%d, %d, %d)\n", i, j, k);
    }
  else
    {
      deltaIJ = x[0] * u[2] - x[2] * u[0];
      if (deltaIJ != 0.)
	{
	  i = 0;
	  j = 2;
	  k = 1;
	  DBG_fprintf(stderr, " Using deltaIJ scheme with (i, j, k)"
		      " = (%d, %d, %d)\n", i, j, k);
	}
      else
	{
	  deltaIJ = x[1] * u[2] - x[2] * u[1];
	  if (deltaIJ != 0.)
	    {
	      i = 1;
	      j = 2;
	      k = 0;
	      DBG_fprintf(stderr, " Using deltaIJ scheme with (i, j, k)"
			  " = (%d, %d, %d)\n", i, j, k);
	    }
	  else
            return FALSE;
	}
    }
  Y[k] = -1.;
  Y[i] = (x[k] * u[j] - x[j] * u[k]) / deltaIJ;
  Y[j] = (x[i] * u[k] - x[k] * u[i]) / deltaIJ;
  /* We need to turn Y if y.Y is negative. */
  norm = 0.;
  for (i = 0; i < 3; i++)
    norm += full[1][i] * Y[i];
  if (norm < 0.)
  for (i = 0; i < 3; i++)
    Y[i] *= -1.;
    
  /* Compute the new Z vector in order to form a direct orthogonal
     basis with X and Y. */
  Z[0] = X[1] * Y[2] - X[2] * Y[1];
  Z[1] = X[2] * Y[0] - X[0] * Y[2];
  Z[2] = X[0] * Y[1] - X[1] * Y[0];

  /* Normalise the new basis (X, Y, Z). */
  norm = 0.;
  for (i = 0; i < 3; i++)
    norm += X[i] * X[i];
  norm = sqrt(norm);
  for (i = 0; i < 3; i++)
    X[i] /= norm;
  norm = 0.;
  for (i = 0; i < 3; i++)
    norm += Y[i] * Y[i];
  norm = sqrt(norm);
  for (i = 0; i < 3; i++)
    Y[i] /= norm;
  norm = 0.;
  for (i = 0; i < 3; i++)
    norm += Z[i] * Z[i];
  norm = sqrt(norm);
  for (i = 0; i < 3; i++)
    Z[i] /= norm;

/*   DBG_fprintf(stderr, "X : %f %f %f\n", X[0], X[1], X[2]); */
/*   DBG_fprintf(stderr, "Y : %f %f %f\n", Y[0], Y[1], Y[2]); */
/*   DBG_fprintf(stderr, "Z : %f %f %f\n", Z[0], Z[1], Z[2]); */

  /* Compute the reduce value for the basis. */
  DBG_fprintf(stderr, " Write to reduced (%p).\n", (gpointer)reduced);
  reduced[0] = 0.;
  for (i = 0; i < 3; i++)
    reduced[0] += X[i] * full[0][i];

  reduced[1] = 0.;
  for (i = 0; i < 3; i++)
    reduced[1] += X[i] * full[1][i];

  reduced[2] = 0.;
  for (i = 0; i < 3; i++)
    reduced[2] += Y[i] * full[1][i];

  reduced[3] = 0.;
  for (i = 0; i < 3; i++)
    reduced[3] += X[i] * full[2][i];

  reduced[4] = 0.;
  for (i = 0; i < 3; i++)
    reduced[4] += Y[i] * full[2][i];

  reduced[5] = 0.;
  for (i = 0; i < 3; i++)
    reduced[5] += Z[i] * full[2][i];
  DBG_fprintf(stderr, " Write OK.\n");

  return TRUE;
}

/**
 * tool_matrix_dtof:
 * @mf: a matrix in single precision.
 * @md: a matrix in double precision.
 *
 * Cast @md into @mf.
 *
 * Since: 3.7
 **/
void tool_matrix_dtof(float mf[3][3], double md[3][3])
{
  int i, j;

  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      mf[i][j] = md[i][j];
}

/**
 * tool_matrix_setIdentity:
 * @mat: (array fixed-size=9): a matrix location.
 *
 * Initialise @mat with the identity.
 *
 * Since: 3.7
 **/
void tool_matrix_setIdentity(float mat[3][3])
{
  mat[0][0] = 1.f;
  mat[0][1] = 0.f;
  mat[0][2] = 0.f;
  mat[1][0] = 0.f;
  mat[1][1] = 1.f;
  mat[1][2] = 0.f;
  mat[2][0] = 0.f;
  mat[2][1] = 0.f;
  mat[2][2] = 1.f;
}
/**
 * tool_matrix_set:
 * @mat: a matrix.
 * @orig: a matrix.
 *
 * Copy @orig into @mat.
 *
 * Since: 3.8
 **/
void tool_matrix_set(float mat[3][3], float orig[3][3])
{
  mat[0][0] = orig[0][0];
  mat[0][1] = orig[0][1];
  mat[0][2] = orig[0][2];
  mat[1][0] = orig[1][0];
  mat[1][1] = orig[1][1];
  mat[1][2] = orig[1][2];
  mat[2][0] = orig[2][0];
  mat[2][1] = orig[2][1];
  mat[2][2] = orig[2][2];
}
/**
 * tool_matrix_rotate:
 * @mat: a matrix.
 * @angle: an angle in degrees.
 * @dir: a direction.
 *
 * Create a rotation matrix along axis @dir of the given @angle.
 *
 * Since: 3.8
 **/
void tool_matrix_rotate(float mat[3][3], float angle, ToolXyzDir dir)
{
  float rot[3][3], work[3][3];
  int perm[3][3] = {{1, 2, 0}, {2, 0, 1}, {0, 1, 2}};

  rot[perm[dir][0]][perm[dir][0]] = cos(angle * TOOL_PI180);
  rot[perm[dir][0]][perm[dir][1]] = -sin(angle * TOOL_PI180);
  rot[perm[dir][0]][perm[dir][2]] = 0.f;
  rot[perm[dir][1]][perm[dir][0]] = sin(angle * TOOL_PI180);
  rot[perm[dir][1]][perm[dir][1]] = cos(angle * TOOL_PI180);
  rot[perm[dir][1]][perm[dir][2]] = 0.f;
  rot[perm[dir][2]][perm[dir][0]] = 0.f;
  rot[perm[dir][2]][perm[dir][1]] = 0.f;
  rot[perm[dir][2]][perm[dir][2]] = 1.f;

  tool_matrix_set(work, mat);
  tool_matrix_productMatrix(mat, work, rot);
}

/**
 * tool_matrix_productMatrix:
 * @matRes: an array of floating point values of size 3x3 ;
 * @matA: an array of floating point values of size 3x3 ;
 * @matB: an array of floating point values of size 3x3.
 *
 * Compute the mathematical product between @matA and @matB and
 * put the result matrix in @matRes.
 *
 * Since: 3.2
 */
void tool_matrix_productMatrix(float matRes[3][3], float matA[3][3], float matB[3][3])
{
  int i, j, k;

  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      {
	matRes[i][j] = 0.;
	for (k = 0; k < 3; k++)
	  matRes[i][j] += matA[i][k] * matB[k][j];
      }
}
/**
 * tool_matrix_productVector:
 * @vectRes: an array of floating point values of size 3 ;
 * @mat: an array of floating point values of size 3x3 ;
 * @vect: an array of floating point values of size 3.
 *
 * Compute the mathematical product between @matA and @vect and
 * put the result vector in @vectRes.
 *
 * Since: 3.2
 */
void tool_matrix_productVector(float vectRes[3], float mat[3][3], float vect[3])
{
  int i, j;

  for (i = 0; i < 3; i++)
    {
      vectRes[i] = 0.;
      for (j = 0; j < 3; j++)
	vectRes[i] += mat[i][j] * vect[j];
    }
}
/**
 * tool_matrix_determinant:
 * @mat: a matrix.
 *
 * Calculate the determinant of matrix @mat.
 *
 * Since: 3.6
 *
 * Returns: the determinant value.
 */
float tool_matrix_determinant(float mat[3][3])
{
  DBG_fprintf(stderr, "Tool Matrix: %g is det( %8g %8g %8g\n"
	              "                        %8g %8g %8g\n"
	              "                        %8g %8g %8g )\n",
	      mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) -
              mat[0][1] * (mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) +
              mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]),
              mat[0][0], mat[0][1], mat[0][2],
	      mat[1][0], mat[1][1], mat[1][2],
	      mat[2][0], mat[2][1], mat[2][2]);

  return mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) -
    mat[0][1] * (mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) +
    mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]);
}
/**
 * tool_matrix_invert:
 * @inv: a matrix (out values).
 * @mat: a matrix.
 *
 * Calculate the inverse matrix of matrix @mat and store it in @inv.
 *
 * Since: 3.6
 *
 * Returns: FALSE if @mat is singular.
 */
gboolean tool_matrix_invert(float inv[3][3], float mat[3][3])
{
  float det;

  det = tool_matrix_determinant(mat);
  if (det == 0.f)
    return FALSE;

  det = 1.f / det;

  inv[0][0] = det * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]);
  inv[0][1] = det * (mat[0][2] * mat[2][1] - mat[0][1] * mat[2][2]);
  inv[0][2] = det * (mat[0][1] * mat[1][2] - mat[0][2] * mat[1][1]);

  inv[1][0] = det * (mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2]);
  inv[1][1] = det * (mat[0][0] * mat[2][2] - mat[0][2] * mat[2][0]);
  inv[1][2] = det * (mat[0][2] * mat[1][0] - mat[0][0] * mat[1][2]);

  inv[2][0] = det * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]);
  inv[2][1] = det * (mat[0][1] * mat[2][0] - mat[0][0] * mat[2][1]);
  inv[2][2] = det * (mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]);

  return TRUE;
}
/**
 * tool_matrix_getRotationFromFull:
 * @rot: a rotation matrix (out values).
 * @full: the description of basis set in full development.
 * @box: the description of basis set in align X axis.
 *
 * There is a rotation matrix to transform from full cartesian
 * coordinates into reduced box cartesian coordinates.
 *
 * Since: 3.6
 *
 * Returns: TRUE if @full does not describe properly a 3D box.
 */
gboolean tool_matrix_getRotationFromFull(float rot[3][3],
				       double full[3][3], double box[6])
{
  float boxMat[3][3], fileMat[3][3], fileMatInv[3][3];

  /* We create the rotation matrix that pass from the cartesian
     coordinates of the file to the cartesian coordinates of the box. */
  boxMat[0][0] = box[0];
  boxMat[0][1] = box[1];
  boxMat[0][2] = box[3];
  boxMat[1][0] = 0.f;
  boxMat[1][1] = box[2];
  boxMat[1][2] = box[4];
  boxMat[2][0] = 0.f;
  boxMat[2][1] = 0.f;
  boxMat[2][2] = box[5];
  fileMat[0][0] = (float)full[0][0];
  fileMat[0][1] = (float)full[1][0];
  fileMat[0][2] = (float)full[2][0];
  fileMat[1][0] = (float)full[0][1];
  fileMat[1][1] = (float)full[1][1];
  fileMat[1][2] = (float)full[2][1];
  fileMat[2][0] = (float)full[0][2];
  fileMat[2][1] = (float)full[1][2];
  fileMat[2][2] = (float)full[2][2];
  if (!tool_matrix_invert(fileMatInv, fileMat))
    return FALSE;

  tool_matrix_productMatrix(rot, boxMat, fileMatInv);
  DBG_fprintf(stderr, "Tool Matrix: rotation matrix %8g %8g %8g\n"
	              "                             %8g %8g %8g\n"
	              "                             %8g %8g %8g\n",
	      rot[0][0], rot[0][1], rot[0][2],
	      rot[1][0], rot[1][1], rot[1][2],
	      rot[2][0], rot[2][1], rot[2][2]);
  return TRUE;
}

#ifndef RAD2DEG
#define RAD2DEG(x) (57.29577951308232311 * x)
#endif

#ifndef DEG2RAD
#define DEG2RAD(x) (0.01745329251994329509 * x)
#endif

/**
 * tool_matrix_cartesianToSpherical:
 * @spherical: an allocated array of 3 floating point values to store the result ;
 * @cartesian: an allocated array of 3 floating point values to read the input.
 *
 * A method to transform cartesian coordinates in spherical
 * coordinates (radius, phi and theta).
 *
 * Since: 3.3
 */
void tool_matrix_cartesianToSpherical(float spherical[3], const float cartesian[3])
{
/* s[0] = rho, s[1] = theta, s[2] = phi
   c[0] = x, c[1] = y, c[2] = z */
  double rho;
  double theta; 
  double phi;

  if (cartesian[0] == 0 && cartesian[1] == 0 && cartesian[2] == 0)
    {
      spherical[TOOL_MATRIX_SPHERICAL_MODULUS] = 0;
      spherical[TOOL_MATRIX_SPHERICAL_THETA] = 0;
      spherical[TOOL_MATRIX_SPHERICAL_PHI] = 0;
      return;
    }

  rho = sqrt(cartesian[0] * cartesian[0] +
             cartesian[1] * cartesian[1] +
             cartesian[2] * cartesian[2]);

  if (cartesian[0] == 0 && cartesian[1] == 0)
    theta = (cartesian[2] > 0) ? 0 : G_PI;
  else 
    theta = acos(CLAMP(cartesian[2] / rho, -1., 1.));

  if (cartesian[0] != 0)
    {
      phi = atan(cartesian[1] / cartesian[0]) + G_PI*((cartesian[0] < 0) ? 1 : 0);
    }
  else
    {
      if (cartesian[1] == 0) /* facultatif*/
	phi = 0;    /* facultatif*/
      else if(cartesian[1] > 0)
	phi = G_PI_2;
      else
	phi = -G_PI_2;
    }

  spherical[TOOL_MATRIX_SPHERICAL_MODULUS] = rho;
  spherical[TOOL_MATRIX_SPHERICAL_THETA] = RAD2DEG(theta);
  spherical[TOOL_MATRIX_SPHERICAL_PHI] = tool_modulo_float(RAD2DEG(phi), 360);
}

/**
 * tool_matrix_sphericalToCartesian:
 * @cartesian: an allocated array of 3 floating point values to store the result ;
 * @spherical: an allocated array of 3 floating point values to read the input.
 *
 * A method to transform spherical coordinates (radius, phi and theta)
 * to cartesian coordinates.
 *
 * Since: 3.3
 */
void tool_matrix_sphericalToCartesian(float cartesian[3], const float spherical[3])
{
  cartesian[0] = spherical[TOOL_MATRIX_SPHERICAL_MODULUS] *
    sin(DEG2RAD(spherical[TOOL_MATRIX_SPHERICAL_THETA])) *
    cos(DEG2RAD(spherical[TOOL_MATRIX_SPHERICAL_PHI]));
  cartesian[1] = spherical[TOOL_MATRIX_SPHERICAL_MODULUS] *
    sin(DEG2RAD(spherical[TOOL_MATRIX_SPHERICAL_THETA])) *
    sin(DEG2RAD(spherical[TOOL_MATRIX_SPHERICAL_PHI]));
  cartesian[2] = spherical[TOOL_MATRIX_SPHERICAL_MODULUS] *
    cos(DEG2RAD(spherical[TOOL_MATRIX_SPHERICAL_THETA]));
}

/**
 * tool_matrix_getInter2D:
 * @lambda: a location to store a float.
 * @a: a point.
 * @b: another point.
 * @A: a point.
 * @B: another point.
 *
 * Get the intersection coeeficient of lines [ab] and [AB].
 *
 * Returns: TRUE if [ab] and [AB] have an intersection.
 */
gboolean tool_matrix_getInter2D(float *lambda,
			   float a[2], float b[2], float A[2], float B[2])
{
  float denom;

  denom = (b[0] - a[0]) * (B[1] - A[1]) - (b[1] - a[1]) * (B[0] - A[0]);
  if (denom == 0.f)
    return FALSE;
  *lambda = (A[0] - a[0]) * (B[1] - A[1]) - (A[1] - a[1]) * (B[0] - A[0]);
  *lambda /= denom;
/*   fprintf(stderr, "%g\n", *lambda); */
  return TRUE;
}
/**
 * tool_matrix_getInter2DFromList: (skip)
 * @i: a location to store a point.
 * @lambda: a location to store a float.
 * @a: a point.
 * @b: another point.
 * @set: a list of points.
 *
 * Same as tool_matrix_getInter2D(), but from a list of points.
 *
 * Returns: TRUE if an intersection exists.
 */
gboolean tool_matrix_getInter2DFromList(float i[2], float *lambda,
                                        float a[2], float b[2], GList *set)
{
  float *pt1, *pt2;
  float l, min;
  
  i[0] = a[0];
  i[1] = a[1];

  min = 1.2f;
  for (pt1 = (float*)(g_list_last(set)->data); set; set = g_list_next(set))
    {
      pt2 = (float*)set->data;
      if (tool_matrix_getInter2D(&l, a, b, pt1, pt2))
	min = (l >= 0.f)?MIN(min, l):min;
      pt1 = pt2;
    }
  if (min > 1.00001f)
    return FALSE;
  if (lambda)
    *lambda = min;
  i[0] = (b[0] - a[0]) * min + a[0];
  i[1] = (b[1] - a[1]) * min + a[1];
/*   fprintf(stderr, "%g -> %gx%g\n", min, i[0], i[1]); */
  return TRUE;
}

#define FLAG_PARAMETER_THRESHOLD "scale_log_threshold"
#define DESC_PARAMETER_THRESHOLD "Value of the threshold used in the zero centred TOOL_MATRIX_SCALING_LOG scaling function ; a positive float (1e-3)"
static float threshold = 1e-3;
static void exportParameters(GString *data, VisuData *dataObj);

/**
 * tool_matrix_getScaledLinear:
 * @x: the initial value ;
 * @minmax: the boundaries for the @x argument ;
 *
 * Transform @x into [0;1] with a linear scale.
 *
 * Returns: a value into [0;1].
 *
 * Since: 3.5
 */
float tool_matrix_getScaledLinear(float x, float minmax[2])
{
  return ((CLAMP(x, minmax[0], minmax[1]) - minmax[0]) /
	  (minmax[1] - minmax[0]));
}
/**
 * tool_matrix_getScaledLog:
 * @x: the initial value ;
 * @minmax: the boundaries for the @x argument.
 *
 * Transform @x into [0;1] with a log scale.
 *
 * Returns: a value into [0;1].
 *
 * Since: 3.5
 */
float tool_matrix_getScaledLog(float x, float minmax[2])
{
  /* float v; */
  float lMinMax[2];

  lMinMax[0] = log10(MAX(1e-12, minmax[0]));
  lMinMax[1] = log10(MAX(1e-12, minmax[1]));
  return tool_matrix_getScaledLinear(log10(MAX(1e-12, x)), lMinMax);
  /* return (v == 0.)?0.: - (log10(v) - param) / param; */
}
/**
 * tool_matrix_getScaledZeroCentredLog:
 * @x: the initial value ;
 * @minmax: the boundaries for the @x argument.
 *
 * Transform @x into [0;1] with a log scale with zero centred values.
 *
 * Returns: a value into [0;1].
 *
 * Since: 3.5
 */
float tool_matrix_getScaledZeroCentredLog(float x, float minmax[2])
{
  float v, m;

  m = MAX(minmax[1], -minmax[0]);
  v = CLAMP(x, -m, m);
  return 0.5 + (v < 0.?-1.:1.) * (log(m * threshold) -
				  log(MAX(ABS(v), m * threshold))) / 
    (2. * log(threshold));
}
/**
 * tool_matrix_getScaledLinearInv:
 * @x: the initial value ;
 * @minmax: the boundaries for the @x argument.
 *
 * Reverse function for tool_matrix_getScaledLinear().
 *
 * Returns: a value into [0;1].
 *
 * Since: 3.5
 */
float tool_matrix_getScaledLinearInv(float x, float minmax[2])
{
  return (minmax[0] + CLAMP(x, 0., 1.) * (minmax[1] - minmax[0]));
}
/**
 * tool_matrix_getScaledLogInv:
 * @x: the initial value ;
 * @minmax: the boundaries for the @x argument.
 *
 * Reverse function for tool_matrix_getScaledLog().
 *
 * Returns: a value into [0;1].
 *
 * Since: 3.5
 */
float tool_matrix_getScaledLogInv(float x, float minmax[2])
{
  return MAX(1e-12, minmax[0]) * pow(MAX(1e-12, minmax[1]) / MAX(1e-12, minmax[0]), CLAMP(x, 0., 1.));
  /* return (minmax[0] + (minmax[1] - minmax[0]) * exp((1. - CLAMP(x, 0., 1.)) * param)); */
}
/**
 * tool_matrix_getScaledZeroCentredLogInv:
 * @x: the initial value ;
 * @minmax: the boundaries for the @x argument.
 *
 * Reverse function for tool_matrix_getScaledZeroCentredLog().
 *
 * Returns: a value into [0;1].
 *
 * Since: 3.5
 */
float tool_matrix_getScaledZeroCentredLogInv(float x, float minmax[2])
{
  float s, m, out;

  DBG_fprintf(stderr, "Matrix: get inv ZCL %g (%g-%g).\n", x, minmax[0], minmax[1]);
  s = (x < 0.5)?-1.:1.;
  m = MAX(minmax[1], -minmax[0]);
  out = s * m * threshold * exp(s * (1. - 2. * CLAMP(x, 0., 1.)) * log(threshold));
  DBG_fprintf(stderr, " | %g (%g)\n", out, tool_matrix_getScaledZeroCentredLog(out, minmax));
  return out;
}

static void exportParameters(GString *data, VisuData *dataObj _U_)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_THRESHOLD);
  g_string_append_printf(data, "%s: %f\n\n", FLAG_PARAMETER_THRESHOLD,
			 threshold);
}
/**
 * tool_matrix_init: (skip)
 *
 * This method is used by V_Sim internally and should not be called.
 *
 * Since: 3.5
 */
void tool_matrix_init(void)
{
  float rg[2] = {G_MINFLOAT, G_MAXFLOAT};
  VisuConfigFileEntry *entry;

  /* Set private variables. */
  entry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_PARAMETER,
                                              FLAG_PARAMETER_THRESHOLD,
                                              DESC_PARAMETER_THRESHOLD,
                                              1, &threshold, rg, FALSE);
  visu_config_file_entry_setVersion(entry, 3.5f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
                                     exportParameters);
}

static gpointer vector_copy(gconstpointer boxed)
{
  gfloat *vector, *orig = (gfloat*)boxed;

  if (!boxed)
    return (gpointer)0;

  vector = g_malloc(sizeof(gfloat) * 3);
  vector[0] = orig[0];
  vector[1] = orig[1];
  vector[2] = orig[2];
  return (gpointer)vector;
}
/**
 * tool_vector_get_type:
 *
 * Create and retrieve a #GType for a #ToolVector object.
 *
 * Since: 3.8
 *
 * Returns: a new type for #ToolVector structures.
 */
GType tool_vector_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id = g_boxed_type_register_static("ToolVector",
                                                    (GBoxedCopyFunc)vector_copy, g_free);
  return g_define_type_id;
}

/**
 * tool_vector_new:
 * @orig: (array fixed-size=3): a vector.
 *
 * Creates a new #ToolVector by copying @orig.
 *
 * Since: 3.8
 *
 * Returns: (transfer full) (type ToolVector):
 **/
float* tool_vector_new(const float orig[3])
{
  return vector_copy(orig);
}

/**
 * tool_vector_set:
 * @dest: a vector.
 * @orig: a vector.
 *
 * Copy @orig into @dest and also test if @dest was already equals to @orig.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @dest is different from @orig.
 **/
gboolean tool_vector_set(float dest[3], const float orig[3])
{
  if (dest[0] == orig[0] && dest[1] == orig[1] && dest[2] == orig[2])
    return FALSE;

  dest[0] = orig[0];
  dest[1] = orig[1];
  dest[2] = orig[2];
  return TRUE;
}

/**
 * tool_vector_spherical:
 * @cart: (array fixed-size=3): a vector in cartesian coordinates.
 * @at: a spherical direction.
 *
 * Convert @cart in spherical coordinates. It is equivalent to
 * tool_matrix_cartesianToSpherical() but returns the value in a given
 * direction only. This is intended for bindings.
 *
 * Since: 3.8
 *
 * Returns: the coodinate in spherical.
 **/
float tool_vector_spherical(const float cart[3], ToolMatrixSphericalCoord at)
{
  float sph[3];

  tool_matrix_cartesianToSpherical(sph, cart);
  return sph[at];
}

static gpointer minmax_copy(gpointer boxed)
{
  gfloat *mm, *orig = (gfloat*)boxed;

  if (!boxed)
    return (gpointer)0;

  mm = g_malloc(sizeof(gfloat) * 2);
  mm[0] = orig[0];
  mm[1] = orig[1];
  return (gpointer)mm;
}
/**
 * tool_minmax_get_type:
 *
 * Create and retrieve a #GType for a #ToolMinmax object.
 *
 * Since: 3.8
 *
 * Returns: a new type for #ToolMinmax structures.
 */
GType tool_minmax_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id = g_boxed_type_register_static("ToolMinmax", minmax_copy, g_free);
  return g_define_type_id;
}

/**
 * tool_minmax:
 * @global: (array fixed-size=2): the global min and max values.
 * @minmax: (array fixed-size=2): some min and max values.
 *
 * Take the minimum and maximum of @global and @minmax and put them
 * into @global. @global should be initialised.
 *
 * Since: 3.8
 **/
void tool_minmax(float global[2], const float minmax[2])
{
  global[0] = MIN(minmax[0], global[0]);
  global[1] = MAX(minmax[1], global[1]);
}
/**
 * tool_minmax_fromDbl:
 * @global: (array fixed-size=2): the global min and max values.
 * @minmax: (array fixed-size=2): some min and max values.
 *
 * Same as tool_minmax() for double inputs.
 *
 * Since: 3.8
 **/
void tool_minmax_fromDbl(float global[2], const double minmax[2])
{
  global[0] = MIN(minmax[0], global[0]);
  global[1] = MAX(minmax[1], global[1]);
}
