/* Simple forward Euler method for velocity integration in time */ void euler(double &x, double &y, double timestep, const FluidQuantity &u, const FluidQuantity &v) const { double uVel = u.lerp(x, y)/_hx; double vVel = v.lerp(x, y)/_hx; x -= uVel*timestep; y -= vVel*timestep; }
/* Builds the pressure right hand side as the negative divergence */ void buildRhs() { double scale = 1.0/_hx; for (int y = 0, idx = 0; y < _h; y++) { for (int x = 0; x < _w; x++, idx++) { _r[idx] = -scale*(_u->at(x + 1, y) - _u->at(x, y) + _v->at(x, y + 1) - _v->at(x, y)); } } }
double maxTimestep() { double maxVelocity = 0.0; for (int y = 0; y < _h; y++) { for (int x = 0; x < _w; x++) { double u = _u->lerp(x + 0.5, y + 0.5); double v = _v->lerp(x + 0.5, y + 0.5); double velocity = sqrt(u*u + v*v); maxVelocity = max(maxVelocity, velocity); } } double maxTimestep = 4.0*_hx/maxVelocity; return min(maxTimestep, 1.0); }
/* Returns the maximum allowed timestep. Note that the actual timestep * taken should usually be much below this to ensure accurate * simulation - just never above. */ double maxTimestep() { double maxVelocity = 0.0; for (int y = 0; y < _h; y++) { for (int x = 0; x < _w; x++) { /* Average velocity at grid cell center */ double u = _u->lerp(x + 0.5, y + 0.5); double v = _v->lerp(x + 0.5, y + 0.5); double velocity = sqrt(u*u + v*v); maxVelocity = max(maxVelocity, velocity); } } /* Fluid should not flow more than two grid cells per iteration */ double maxTimestep = 2.0*_hx/maxVelocity; /* Clamp to sensible maximum value in case of very small velocities */ return min(maxTimestep, 1.0); }
/* Convert fluid density to RGBA image */ void toImage(unsigned char *rgba) { for (int i = 0; i < _w*_h; i++) { int shade = (int)((1.0 - _d->src()[i])*255.0); shade = max(min(shade, 255), 0); rgba[i*4 + 0] = shade; rgba[i*4 + 1] = shade; rgba[i*4 + 2] = shade; rgba[i*4 + 3] = 0xFF; } }
void update(double timestep) { buildRhs(); project(600, timestep); applyPressure(timestep); _d->advect(timestep, *_u, *_v); _u->advect(timestep, *_u, *_v); _v->advect(timestep, *_u, *_v); _d->flip(); _u->flip(); _v->flip(); }
void update(double timestep) { buildRhs(); project(600, timestep); applyPressure(timestep); _d->advect(timestep, *_u, *_v); _u->advect(timestep, *_u, *_v); _v->advect(timestep, *_u, *_v); /* Make effect of advection visible, since it's not an in-place operation */ _d->flip(); _u->flip(); _v->flip(); }
/* Applies the computed pressure to the velocity field */ void applyPressure(double timestep) { double scale = timestep/(_density*_hx); for (int y = 0, idx = 0; y < _h; y++) { for (int x = 0; x < _w; x++, idx++) { _u->at(x, y ) -= scale*_p[idx]; _u->at(x + 1, y ) += scale*_p[idx]; _v->at(x, y ) -= scale*_p[idx]; _v->at(x, y + 1) += scale*_p[idx]; } } for (int y = 0; y < _h; y++) _u->at(0, y) = _u->at(_w, y) = 0.0; for (int x = 0; x < _w; x++) _v->at(x, 0) = _v->at(x, _h) = 0.0; }
/* Third order Runge-Kutta for velocity integration in time */ void rungeKutta3(double &x, double &y, double timestep, const FluidQuantity &u, const FluidQuantity &v) const { double firstU = u.lerp(x, y)/_hx; double firstV = v.lerp(x, y)/_hx; double midX = x - 0.5*timestep*firstU; double midY = y - 0.5*timestep*firstV; double midU = u.lerp(midX, midY)/_hx; double midV = v.lerp(midX, midY)/_hx; double lastX = x - 0.75*timestep*midU; double lastY = y - 0.75*timestep*midV; double lastU = u.lerp(lastX, lastY); double lastV = v.lerp(lastX, lastY); x -= timestep*((2.0/9.0)*firstU + (3.0/9.0)*midU + (4.0/9.0)*lastU); y -= timestep*((2.0/9.0)*firstV + (3.0/9.0)*midV + (4.0/9.0)*lastV); }
/* Set density and x/y velocity in given rectangle to d/u/v, respectively */ void addInflow(double x, double y, double w, double h, double d, double u, double v) { _d->addInflow(x, y, x + w, y + h, d); _u->addInflow(x, y, x + w, y + h, u); _v->addInflow(x, y, x + w, y + h, v); }