UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

134 lines (113 loc) 4.05 kB
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; };