# Source: papier-matrix-utils.js

    /*
    	This file is a part of "Papier" a paper-crafting tool

    Copyright (C) 2018  Saint Pierre Thomas ( s1pierro@protonmail.fr )

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    */
    function  distance (c1, c2)
    {
    	var d = Math.sqrt( (c2[0]-c1[0])*(c2[0]-c1[0])+
    							 (c2[1]-c1[1])*(c2[1]-c1[1])+
    							 (c2[2]-c1[2])*(c2[2]-c1[2]) );
    	return d;
    }
    /** @constructor */
    function Vector(a, b, c) {
        this.o = a;
        this.s = b;
        this.n = c
    }
    /**
    	set the vector from two vertice wher a will be the origin
    	@param {object} a - the first vertice to use to compute the vector object ( will be origin )
    	@param {object} b - the seecond vertice to use to compute the vector object

    */
    Vector.prototype.setFromVertices = function (a, b)
    {
    	if (a[0] == b[0] && a[1] == b[1] && a[2] == b[2])
    	{
    		this.o = [0.0, 0.0, 0.0];
    		this.s = [0.0, 0.0, 0.0];
    		this.n = 0.0;
    	}
    	var c = Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + (b[2] - a[2]) * (b[2] - a[2]));
    	this.o = a;
    	this.s = [(b[0] - a[0]) / c, (b[1] - a[1]) / c, (b[2] - a[2]) / c];
    	this.n = c;
    }
    function vectfromvertices(a, b) {
    	if (a[0] == b[0] && a[1] == b[1] && a[2] == b[2])
    		return new Vector([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], 0.0);
        var c = Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + (b[2] - a[2]) * (b[2] - a[2]));
        return new Vector(a, [(b[0] - a[0]) / c, (b[1] - a[1]) / c, (b[2] - a[2]) / c], c)
    }
    window.vectfromvertices = vectfromvertices;
    function magnitudevertex(a) {
        return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2])
    }
    window.magnitudevertex = magnitudevertex;

    function normalisevertex(a) {
        var b = [0, 0, 0],
            c = magnitudevertex(a);
        b[0] = a[0] / c;
        b[1] = a[1] / c;
        b[2] = a[2] / c;
        return b
    }
    window.normalisevertex = normalisevertex;

    function vectorproduct(a, b) {
        return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]
    }
    window.vectorproduct = vectorproduct;

    function scalarproduct(a, b) {
        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
    }
    window.scalarproduct = scalarproduct;

    function multiplymatrix(a, b) {
        var c = [];
        c.length = 16;
        var d, e, f;
        for (e = 0; 4 > e; e++)
            for (d = 0; 4 > d; d++)
                for (f = c[d + 4 * e] = 0; 4 > f; f++) c[d + 4 * e] += a[f + 4 * e] * b[d + 4 * f];
        return c
    }
    window.multiplymatrix = multiplymatrix;

    function applypersp(a) {
        v1[0] = viewangle / a[2] * a[0];
        v1[1] = viewangle / a[2] * a[1];
        v1[2] = a[2];
        return v1
    }
    window.applypersp = applypersp;

    function applymat(a, b) {

         var c = [];
        c.lenth = 3;
       c[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3];
        c[1] = a[4] * b[0] + a[5] * b[1] + a[6] * b[2] + a[7];
        c[2] = a[8] * b[0] + a[9] * b[1] + a[10] * b[2] + a[11];
        return c
    }
    window.applymat = applymat;

    function applymatNpersp(a, b) {
        var c = [];
        c.lenth = 3;
        c[2] = a[8] * b[0] + a[9] * b[1] + a[10] * b[2] + a[11];
        c[0] = viewangle / c[2] * (a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3]);
        c[1] = viewangle / c[2] * (a[4] * b[0] + a[5] * b[1] + a[6] * b[2] + a[7]);
        return c
    }
    window.applymatNpersp = applymatNpersp;

    function applymatNscale(a, b) {
         var c = [];
        c.lenth = 3;
       c[0] = (a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3])*scaleconst;
        c[1] = (a[4] * b[0] + a[5] * b[1] + a[6] * b[2] + a[7])*scaleconst;
        c[2] = (a[8] * b[0] + a[9] * b[1] + a[10] * b[2] + a[11])*scaleconst;
        return c
    }
    window.applymatNscale = applymatNscale;

    function genimat() {
        var a = [];
        a.length = 16;
        var b, c;
        for (b = 0; 4 > b; b++)
            for (c = 0; 4 > c; c++) a[b + 4 * c] = b == c ? 1 : 0;
        return a
    }
    window.genimat = genimat;

    function gentmat(a, b, c) {
        var d = genimat();
        d[3] = a;
        d[7] = b;
        d[11] = c;
        return d
    }
    window.gentmat = gentmat;
    function gentmatfromvector(vect) {

    	var a = vect.s[0]*vect.n;
    	var b = vect.s[1]*vect.n;
    	var c = vect.s[2]*vect.n;

    	var d = genimat();

    	d[3] = a;
    	d[7] = b;
    	d[11] = c;
    	return d;
    }
    window.gentmatfromvector = gentmatfromvector;

    function genscalemat(scale) {

    	var d = genimat();

    	d[0] = scale;
    	d[5] = scale;
    	d[10] = scale;
    	d[15] = scale;

    	return d;
    }
    window.genscalemat = genscalemat;

    function genrmat(a, b, c) {
        a *= Math.PI / 180;
        var d = Math.PI / 180 * b,
            e = Math.PI / 180 * c;
        c = genimat();
        b = Math.cos(a);
        a = Math.sin(a);
        var f = Math.cos(d);
        d = Math.sin(d);
        var g = Math.cos(e);
        e = Math.sin(e);
        var h = b * d,
            k = a * d;
        c[0] = f * g;
        c[1] = -f * e;
        c[2] = -d;
        c[4] = -k * g + b * e;
        c[5] = k * e + b * g;
        c[6] = -a * f;
        c[8] = h * g + a * e;
        c[9] = -h * e + a * g;
        c[10] = b * f;
        c[3] = c[7] = c[11] = c[12] = c[13] = c[14] = 0;
        c[15] = 1;
        return c
    }
    window.genrmat = genrmat;
    function axe_ang_to_mat ( axe, ang )
    {
    	var matrix = genimat();
    	var rcos = Math.cos( ang );
    	var rsin = Math.sin( ang );
    	matrix[0] =           rcos + axe[0]*axe[0]*(1-rcos);
    	matrix[4] =  axe[2] * rsin + axe[1]*axe[0]*(1-rcos);
    	matrix[8] = -axe[1] * rsin + axe[2]*axe[0]*(1-rcos);
    	matrix[1] = -axe[2] * rsin + axe[0]*axe[1]*(1-rcos);
    	matrix[5] =           rcos + axe[1]*axe[1]*(1-rcos);
    	matrix[9] =  axe[0] * rsin + axe[2]*axe[1]*(1-rcos);
    	matrix[2] =  axe[1] * rsin + axe[0]*axe[2]*(1-rcos);
    	matrix[6] = -axe[0] * rsin + axe[1]*axe[2]*(1-rcos);
    	matrix[10] =          rcos + axe[2]*axe[2]*(1-rcos);
    	matrix[3] =  matrix[7] = matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0;
    	matrix[15] =  1;
    	return matrix;
    }
    window.axe_ang_to_mat = axe_ang_to_mat;
    function geninterpmat (vs, ve)
    {
    	l(' ## interpolation', 'lg');
    	var scal, ang;
    	var frmat = genimat ();
    	var m = new THREE.Matrix4();

    	// matrice de translation :
    	//var translatevector = vectfromvertices( ve.o, vs.o );

    	var a = ve.o[0] - vs.o[0];
    	var b = ve.o[1] - vs.o[1];
    	var c = ve.o[2] - vs.o[2];
    	var ftmat = gentmat(a, b, c);

    	// verrification du cas d'alignement des vecteurs :
    	var aligntestvector = vectfromvertices( vs.s, ve.s );

    	if ( aligntestvector.n == 0 )
    	{
    		// les vecteurs sont de meme sens, une matrice d'identté suffit
    		// pour la rotation
    		frmat = genimat ();
    	}
    	else if ( aligntestvector.n == 2 )
    	{
    		fl('OPPOSED TARGET');
    	//TODO /!\ IMPORTANT
    	// Les vecteur sont de sens opposés, un vecteur aligné a leurs plan normal
    	// doit etre defini pour effectuer une rotation de 180°
    	}
    	else
    	{

    		var a = vs.s;
    		var b = ve.s;

    		var vp = vectorproduct ( a, b);
    		vp = normalisevertex (vp);
    		var sp = scalarproduct ( a, b );
    		// FREAK solution
    		if ( sp > 1.0 && sp < 1.000001) sp = 1.0;
    		if ( sp < -1.0 && sp > -1.000001) sp = 1.0;

          var ang = Math.acos (sp);

    		frmat = axe_ang_to_mat (vp , ang );
    	}
    	var mat = gentmat( -vs.o[0], -vs.o[1], -vs.o[2]);
    	var mat3 = gentmat( ve.o[0], ve.o[1], ve.o[2]);
    	var mat2 = multiplymatrix (frmat, mat);

    	var finalmat = multiplymatrix (mat3, mat2);

    	return finalmat;

    }
    window.geninterpmat = geninterpmat;