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;
- };
-
-