atomorph/src/fluidmodel.cpp

603 lines
15 KiB
C++

/*
* See Copyright Notice in fluidmodel.h
* See Copyright Notice in atomorph.h
*
*/
#include <cmath>
#include <algorithm>
#include "fluidmodel.h"
Material::Material(double m_,
double rd_,
double k_,
double v_,
double d_,
double g_)
:m(m_),
rd(rd_),
k(k_),
v(v_),
d(d_),
g(g_) {
}
Particle::Particle() {
}
Particle::Particle(Material *mat_,
double x_,
double y_,
double u_,
double v_)
:mat(mat_),
x(x_),
y(y_),
u(u_),
v(v_),
dudx(0.0),
dudy(0.0),
dvdx(0.0),
dvdy(0.0),
cx(0.0),
cy(0.0) {
std::fill(px, px + 3, 0.0);
std::fill(gx, gx + 3, 0.0);
std::fill(py, py + 3, 0.0);
std::fill(gy, gy + 3, 0.0);
}
Node::Node()
:m(0.0),
d(0.0),
gx(0.0),
gy(0.0),
u(0.0),
v(0.0),
ax(0.0),
ay(0.0),
active(false) {
}
void Node::clear() {
m = d = gx = gy = u = v = ax = ay = 0.0;
r = 0.0;
g = 0.0;
b = 0.0;
a = 0.0;
weight = 0.0;
active = false;
}
void Particle::clear() {
u = 0.0;
v = 0.0;
dudx = 0.0;
dudy = 0.0;
dvdx = 0.0;
dvdy = 0.0;
cx = 0.0;
cy = 0.0;
strength = 1.0;
std::fill(px, px + 3, 0.0);
std::fill(gx, gx + 3, 0.0);
std::fill(py, py + 3, 0.0);
std::fill(gy, gy + 3, 0.0);
source_owner = false;
}
void Particle::copy_from(Particle *p) {
x = p->x;
y = p->y;
gravity_x = p->gravity_x;
gravity_y = p->gravity_y;
freedom_r = p->freedom_r;
R = p->R; G = p->G; B = p->B; A = p->A;
r = p->r; g = p->g; b = p->b; a = p->a;
}
FluidModel::FluidModel(unsigned gsizeX_,
unsigned gsizeY_,
unsigned particle_count_)
:particles(NULL),
gsizeX(gsizeX_),
gsizeY(gsizeY_),
particle_count(particle_count_),
active(NULL),
activeCount(0),
water(1.0, 1.0, 1.0, 1.0, 1.0, 1.0),
pressed(false),
pressedprev(false),
mx(0.0),
my(0.0),
mxprev(0.0),
myprev(0.0),
grid(NULL),
lines(NULL) {
grid = new Node *[gsizeX];
for (unsigned i = 0; i < gsizeX; ++i) {
grid[i] = new Node[gsizeY];
}
for (size_t j=0; j<gsizeY; ++j) {
for (size_t i=0; i<gsizeX; ++i) {
grid[i][j].pos = (j * gsizeX)+i;
}
}
active = new Node*[gsizeX * gsizeY];
particles = new Particle[particle_count];
for (size_t i=0; i< particle_count; i++) {
particles[i] = Particle(&water, gsizeX/2,
gsizeY/2,
0.0,
0.0);
}
lines = new Line[particle_count];
}
FluidModel::~FluidModel() {
for (unsigned i = 0; i < gsizeX; ++i) {
delete [] grid[i];
}
delete [] grid;
delete [] active;
delete [] particles;
delete [] lines;
}
void FluidModel::step(size_t steps_left, double freedom_radius, [[maybe_unused]]double morph_time) {
bool drag = false;
double mdx = 0.0, mdy = 0.0;
if (pressed and pressedprev) {
drag = true;
mdx = (mx - mxprev);
mdy = (my - myprev);
}
pressedprev = pressed;
mxprev = mx;
myprev = my;
for (unsigned i = 0; i < activeCount; ++i) {
active[i]->clear();
}
activeCount = 0;
double phi;
double x, y;
double fx = 0.0, fy = 0.0;
Particle *p;
Node *n;
for (unsigned i = 0; i < particle_count; ++i) {
p = particles + i;
if (p->active == false) continue; // CUSTOMIZATION
p->cx = static_cast<int>(p->x - 0.5);
p->cy = static_cast<int>(p->y - 0.5);
x = p->cx - p->x;
p->px[0] = (0.5 * x * x + 1.5 * x + 1.125);
p->gx[0] = (x + 1.5);
x += 1.0;
p->px[1] = (-x * x + 0.75);
p->gx[1] = (-2.0 * x);
x += 1.0;
p->px[2] = (0.5 * x * x - 1.5 * x + 1.125);
p->gx[2] = (x - 1.5);
y = p->cy - p->y;
p->py[0] = (0.5 * y * y + 1.5 * y + 1.125);
p->gy[0] = (y + 1.5);
y += 1.0;
p->py[1] = (-y * y + 0.75);
p->gy[1] = (-2.0 * y);
y += 1.0;
p->py[2] = (0.5 * y * y - 1.5 * y + 1.125);
p->gy[2] = (y - 1.5);
for (unsigned i = 0; i < 3; ++i) {
for (unsigned j = 0; j < 3; ++j) {
n = grid[p->cx + i] + p->cy + j;
if (not n->active) {
active[activeCount++] = n;
n->active = true;
}
phi = p->px[i] * p->py[j];
n->m += phi * p->mat->m;
n->d += phi;
n->gx += p->gx[i] * p->py[j];
n->gy += p->px[i] * p->gy[j];
// CUSTOM:
if (p->mature && p->strength > 0.0) {
double new_w = p->strength;
double old_w = n->weight;
double sum_w = new_w + old_w;
n->r = (old_w * n->r + new_w * p->r)/sum_w;
n->g = (old_w * n->g + new_w * p->g)/sum_w;
n->b = (old_w * n->b + new_w * p->b)/sum_w;
n->a = (old_w * n->a + new_w * p->a)/sum_w;
n->weight = sum_w;
}
// END OF CUSTOM
}
}
}
unsigned cx, cy;
unsigned cxi, cyi;
double pdx, pdy;
double C20, C02, C30, C03;
double C21, C31, C12, C13, C11;
double csum1, csum2;
double u, u2, u3;
double v, v2, v3;
double vx, vy;
double density;
double pressure;
double weight;
Node *n01, *n02, *n11, *n12;
for (unsigned i = 0; i < particle_count; ++i) {
p = particles + i;
if (p->active == false) continue; // CUSTOMIZATION
cx = static_cast<int>(p->x);
cy = static_cast<int>(p->y);
cxi = cx + 1;
cyi = cy + 1;
n01 = grid[cx] + cy;
n02 = grid[cx] + cyi;
n11 = grid[cxi] + cy;
n12 = grid[cxi] + cyi;
pdx = n11->d - n01->d;
pdy = n02->d - n01->d;
C20 = 3.0 * pdx - n11->gx - 2.0 * n01->gx;
C02 = 3.0 * pdy - n02->gy - 2.0 * n01->gy;
C30 = -2.0 * pdx + n11->gx + n01->gx;
C03 = -2.0 * pdy + n02->gy + n01->gy;
csum1 = n01->d + n01->gy + C02 + C03;
csum2 = n01->d + n01->gx + C20 + C30;
C21 = 3.0 * n12->d - 2.0 * n02->gx - n12->gx -
3.0 * csum1 - C20;
C31 = -2.0 * n12->d + n02->gx + n12->gx +
2.0 * csum1 - C30;
C12 = 3.0 * n12->d - 2.0 * n11->gy - n12->gy -
3.0 * csum2 - C02;
C13 = -2.0 * n12->d + n11->gy + n12->gy +
2.0 * csum2 - C03;
C11 = n02->gx - C13 - C12 - n01->gx;
u = p->x - cx;
u2 = u * u;
u3 = u * u2;
v = p->y - cy;
v2 = v * v;
v3 = v * v2;
density = n01->d + n01->gx * u +
n01->gy * v + C20 * u2 +
C02 * v2 + C30 * u3 +
C03 * v3 + C21 * u2 * v +
C31 * u3 * v + C12 * u * v2 +
C13 * u * v3 + C11 * u * v;
pressure = density - 1.0;
if (pressure > 2.0) {
pressure = 2.0;
}
fx = fy = 0.0;
if (p->x < 4.0) {
fx += p->mat->m * (4.0 - p->x);
} else if (p->x > gsizeX - 5) {
fx += p->mat->m * (gsizeX - 5 - p->x);
}
if (p->y < 4.0) {
fy += p->mat->m * (4.0 - p->y);
} else if (p->y > gsizeY - 5) {
fy += p->mat->m * (gsizeY - 5 - p->y);
}
if (drag) {
vx = fabs(p->x - mx);
vy = fabs(p->y - my);
if (vx < 10.0 and vy < 10.0) {
weight = p->mat->m * (1.0 - vx * 0.1) *
(1.0 - vy * 0.1);
fx += weight * (mdx - p->u);
fy += weight * (mdy - p->v);
}
}
for (unsigned i = 0; i < 3; ++i) {
for (unsigned j = 0; j < 3; ++j) {
n = grid[p->cx + i] + p->cy + j;
phi = p->px[i] * p->py[j];
n->ax += -((p->gx[i] * p->py[j]) * pressure) + fx * phi;
n->ay += -((p->px[i] * p->gy[j]) * pressure) + fy * phi;
}
}
}
for (unsigned i = 0; i < activeCount; ++i) {
n = active[i];
if (n->m > 0.0) {
n->ax /= n->m;
n->ay /= n->m;
//ORIGINALLY GRAVITY WAS INDICATED BY THESE:
//n->ay += 0.03;
//n->ax += 0.03;
}
}
double mu, mv;
for (unsigned pi = 0; pi < particle_count; ++pi) {
p = particles + pi;
if (p->active == false) continue; // CUSTOMIZATION
for (unsigned i = 0; i < 3; ++i) {
for (unsigned j = 0; j < 3; ++j) {
n = grid[p->cx + i] + p->cy + j;
phi = p->px[i] * p->py[j];
/* CUSTOM STUFF */
double custom_ax=0.0;
double custom_ay=0.0;
{
double x1 = p->x;
double y1 = p->y;
double x2 = p->gravity_x;
double y2 = p->gravity_y;
double a = 0.03;
// Gravitate towards (x2, y2).
double A = std::abs(y1 - y2);
double B = std::abs(x1 - x2);
double C = std::sqrt(A*A + B*B);
if (a >= C) a = C;
if (B <= 0.0) {
if (y2 <= y1) custom_ay -= a;
else custom_ay += a;
}
else if (C > 0.0) {
double dx = (a * B) / C;
double dy = (A *dx) / B;
if (x1 <= x2) custom_ax += dx;
else custom_ax -= dx;
if (y1 <= y2) custom_ay += dy;
else custom_ay -= dy;
}
}
/* END OF CUSTOM STUFF */
p->u += phi * (n->ax+custom_ax);
p->v += phi * (n->ay+custom_ay);
}
}
mu = p->mat->m * p->u;
mv = p->mat->m * p->v;
if (p->mature == false) {
mu *= 0.0;
mv *= 0.0;
}
for (unsigned i = 0; i < 3; ++i) {
for (unsigned j = 0; j < 3; ++j) {
n = grid[p->cx + i] + p->cy + j;
phi = p->px[i] * p->py[j];
n->u += phi * mu;
n->v += phi * mv;
}
}
}
for (unsigned i = 0; i < activeCount; ++i) {
n = active[i];
if (n->m > 0.0) {
n->u /= n->m;
n->v /= n->m;
}
}
double gu, gv;
for (unsigned i = 0; i < particle_count; ++i) {
p = particles + i;
// CUSTOMIZATION:
if (p->active == false) continue;
double nR = 0.0, nG = 0.0, nB = 0.0, nA = 0.0;
double weight = 0.0;
// END OF CUSTOMIZATION
gu = 0.0;
gv = 0.0;
for (unsigned ii = 0; ii < 3; ++ii) {
for (unsigned j = 0; j < 3; ++j) {
n = grid[p->cx + ii] + p->cy + j;
phi = p->px[ii] * p->py[j];
gu += phi * n->u;
gv += phi * n->v;
if (n->weight > 0.0) {
weight += n->weight;
nR += n->r * n->weight;
nG += n->g * n->weight;
nB += n->b * n->weight;
nA += n->a * n->weight;
}
}
}
// CUSTOMIZATION:
if (weight > 0.0) {
double wr = 1.0;
double wg = 1.0;
double wb = 1.0;
double wa = 1.0;
nR /= weight; wr = std::abs(nR - p->R);
nG /= weight; wg = std::abs(nG - p->G);
nB /= weight; wb = std::abs(nB - p->B);
nA /= weight; wa = std::abs(nA - p->A);
if (p->mature == false) {
p->r = nR;
p->g = nG;
p->b = nB;
p->a = nA;
}
else {
p->r = (1.0 - wr) * p->r + wr * nR;
p->g = (1.0 - wg) * p->g + wg * nG;
p->b = (1.0 - wb) * p->b + wb * nB;
p->a = (1.0 - wa) * p->a + wa * nA;
}
}
// END OF CUSTOMIZATION
p->x += gu;
p->y += gv;
/* CUSTOM STUFF */
{
double x1 = p->x;
double y1 = p->y;
double x2 = p->gravity_x;
double y2 = p->gravity_y;
// Gravitate towards (x2, y2) if the distance
// is greater than freedom radius.
double A = std::abs(y1 - y2);
double B = std::abs(x1 - x2);
double C = std::sqrt(A*A + B*B);
double r = freedom_radius * p->freedom_r;
if (C > r) {
double mx = 0.0;
double my = 0.0;
double a = C - r;
if (a >= C) a = C;
if (B <= 0.0) {
if (y2 <= y1) my -= a;
else my += a;
}
else if (C > 0.0) {
double dx = (a * B) / C;
double dy = (A *dx) / B;
if (x1 <= x2) mx += dx;
else mx -= dx;
if (y1 <= y2) my += dy;
else my -= dy;
}
double w = 1.0 / (steps_left + 1);
mx *= w;
my *= w;
p->x += mx;
p->y += my;
}
}
/* END OF CUSTOM STUFF */
p->u += gu - p->u;
p->v += gv - p->v;
if (p->x < 1.0) {
p->x = 1.0 + static_cast<double>(rand()) / RAND_MAX * 0.01;
p->u = 0.0;
} else if (p->x > gsizeX - 2) {
p->x = gsizeX - 2 - static_cast<double>(rand()) / RAND_MAX * 0.01;
p->u = 0.0;
}
if (p->y < 1.0) {
p->y = 1.0 + static_cast<double>(rand()) / RAND_MAX * 0.01;
p->v = 0.0;
} else if (p->y > gsizeY - 2) {
p->y = gsizeY - 2 - static_cast<double>(rand()) / RAND_MAX * 0.01;
p->v = 0.0;
}
}
// ORIGINALLY THIS PIECE OF CODE WAS HERE
// BUT FOR FLUID MORPHING IT IS NOT NEEDED:
/*for (unsigned i = 0; i < particle_count; ++i) {
p = particles + i;
if (p->inactive) continue; // CUSTOMIZATION
lines[i].x1 = p->x;
lines[i].y1 = p->y;
lines[i].x2 = p->x - p->u;
lines[i].y2 = p->y - p->v;
}*/
}
void FluidModel::setPressed(bool b) {
pressed = b;
}
void FluidModel::setMovePos(double x, double y) {
mx = x;
my = y;
}
const Line * FluidModel::getLines() {
return lines;
}
Particle * FluidModel::getParticles() {
return particles;
}
Material * FluidModel::getMaterial() {
return &water;
}