#

API Docs for: 0.6.1
Show: Inherited Protected Private Deprecated

# File: src/solver/GSSolver.js

    module.exports = GSSolver;

    var Vec3 = require('../math/Vec3');
    var Quaternion = require('../math/Quaternion');
    var Solver = require('./Solver');

    /**
     * Constraint equation Gauss-Seidel solver.
     * @class GSSolver
     * @constructor
     * @todo The spook parameters should be specified for each constraint, not globally.
     * @author schteppe / https://github.com/schteppe
     * @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf
     * @extends Solver
     */
    function GSSolver(){
        Solver.call(this);

        /**
         * The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations.
         * @property iterations
         * @type {Number}
         * @todo write more about solver and iterations in the wiki
         */
        this.iterations = 10;

        /**
         * When tolerance is reached, the system is assumed to be converged.
         * @property tolerance
         * @type {Number}
         */
        this.tolerance = 1e-7;
    }
    GSSolver.prototype = new Solver();

    var GSSolver_solve_lambda = []; // Just temporary number holders that we want to reuse each solve.
    var GSSolver_solve_invCs = [];
    var GSSolver_solve_Bs = [];
    GSSolver.prototype.solve = function(dt,world){
        var iter = 0,
            maxIter = this.iterations,
            tolSquared = this.tolerance*this.tolerance,
            equations = this.equations,
            Neq = equations.length,
            bodies = world.bodies,
            Nbodies = bodies.length,
            h = dt,
            q, B, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj;

        // Update solve mass
        if(Neq !== 0){
            for(var i=0; i!==Nbodies; i++){
                bodies[i].updateSolveMassProperties();
            }
        }

        // Things that does not change during iteration can be computed once
        var invCs = GSSolver_solve_invCs,
            Bs = GSSolver_solve_Bs,
            lambda = GSSolver_solve_lambda;
        invCs.length = Neq;
        Bs.length = Neq;
        lambda.length = Neq;
        for(var i=0; i!==Neq; i++){
            var c = equations[i];
            lambda[i] = 0.0;
            Bs[i] = c.computeB(h);
            invCs[i] = 1.0 / c.computeC();
        }

        if(Neq !== 0){

            // Reset vlambda
            for(var i=0; i!==Nbodies; i++){
                var b=bodies[i],
                    vlambda=b.vlambda,
                    wlambda=b.wlambda;
                vlambda.set(0,0,0);
                if(wlambda){
                    wlambda.set(0,0,0);
                }
            }

            // Iterate over equations
            for(iter=0; iter!==maxIter; iter++){

                // Accumulate the total error for each iteration.
                deltalambdaTot = 0.0;

                for(var j=0; j!==Neq; j++){

                    var c = equations[j];

                    // Compute iteration
                    B = Bs[j];
                    invC = invCs[j];
                    lambdaj = lambda[j];
                    GWlambda = c.computeGWlambda();
                    deltalambda = invC * ( B - GWlambda - c.eps * lambdaj );

                    // Clamp if we are not within the min/max interval
                    if(lambdaj + deltalambda < c.minForce){
                        deltalambda = c.minForce - lambdaj;
                    } else if(lambdaj + deltalambda > c.maxForce){
                        deltalambda = c.maxForce - lambdaj;
                    }
                    lambda[j] += deltalambda;

                    deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda)

                    c.addToWlambda(deltalambda);
                }

                // If the total error is small enough - stop iterate
                if(deltalambdaTot*deltalambdaTot < tolSquared){
                    break;
                }
            }

            // Add result to velocity
            for(var i=0; i!==Nbodies; i++){
                var b=bodies[i],
                    v=b.velocity,
                    w=b.angularVelocity;
                v.vadd(b.vlambda, v);
                if(w){
                    w.vadd(b.wlambda, w);
                }
            }
        }

        return iter;
    };