You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
7.8 KiB
335 lines
7.8 KiB
/* Cool physics functions for wet paint */
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <math.h>
|
|
#include "wetpix.h"
|
|
#include "wetphysics.h"
|
|
|
|
/* symmetric combine, i.e. wet mixing.
|
|
|
|
This does not set the dst h field.
|
|
*/
|
|
void wet_pix_combine(WetPixDbl * dst, WetPixDbl * src1, WetPixDbl * src2)
|
|
{
|
|
dst->rd = src1->rd + src2->rd;
|
|
dst->rw = src1->rw + src2->rw;
|
|
#if 0
|
|
g_print("rd %f rw %f\n", dst->rd, dst->rw);
|
|
#endif
|
|
dst->gd = src1->gd + src2->gd;
|
|
dst->gw = src1->gw + src2->gw;
|
|
dst->bd = src1->bd + src2->bd;
|
|
dst->bw = src1->bw + src2->bw;
|
|
dst->w = src1->w + src2->w;
|
|
#if 0
|
|
g_print("%f + %f -> %f\n", src1->w, src2->w, dst->w);
|
|
#endif
|
|
}
|
|
|
|
void wet_pix_dilute(WetPixDbl * dst, WetPix * src, double dilution)
|
|
{
|
|
double scale = dilution * (1.0 / 8192.0);
|
|
|
|
|
|
dst->rd = src->rd * scale;
|
|
#if 0
|
|
g_print("dilution %f scale %f rd %f\n", dilution, scale, dst->rd);
|
|
#endif
|
|
dst->rw = src->rw * scale;
|
|
dst->gd = src->gd * scale;
|
|
dst->gw = src->gw * scale;
|
|
dst->bd = src->bd * scale;
|
|
dst->bw = src->bw * scale;
|
|
dst->w = src->w * (1.0 / 8192.0);
|
|
dst->h = src->h * (1.0 / 8192.0);
|
|
}
|
|
|
|
void wet_pix_reduce(WetPixDbl * dst, WetPix * src, double dilution)
|
|
{
|
|
wet_pix_dilute(dst, src, dilution);
|
|
dst->w *= dilution;
|
|
}
|
|
|
|
/* allows visualization of adsorption by rotating the hue 120 degrees */
|
|
/* layer-merge combining. src1 is the top layer
|
|
|
|
This does not set the dst h or w fields.
|
|
*/
|
|
void
|
|
wet_pix_merge(WetPixDbl * dst, WetPixDbl * src1, double dilution1,
|
|
WetPixDbl * src2)
|
|
{
|
|
double d1, w1, d2, w2;
|
|
double ed1, ed2;
|
|
|
|
if (src1->rd < 1e-4) {
|
|
dst->rd = src2->rd;
|
|
dst->rw = src2->rw;
|
|
} else if (src2->rd < 1e-4) {
|
|
dst->rd = src1->rd * dilution1;
|
|
dst->rw = src1->rw * dilution1;
|
|
} else {
|
|
d1 = src1->rd;
|
|
w1 = src1->rw;
|
|
d2 = src2->rd;
|
|
w2 = src2->rw;
|
|
dst->rd = d1 * dilution1 + d2;
|
|
ed1 = exp(-d1 * dilution1);
|
|
ed2 = exp(-d2);
|
|
dst->rw = dst->rd * ((1 - ed1) * w1 / d1 +
|
|
ed1 * (1 - ed2) * w2 / d2) /
|
|
(1 - ed1 * ed2);
|
|
}
|
|
|
|
if (src1->gd < 1e-4) {
|
|
dst->gd = src2->gd;
|
|
dst->gw = src2->gw;
|
|
} else if (src2->gd < 1e-4) {
|
|
dst->gd = src1->gd * dilution1;
|
|
dst->gw = src1->gw * dilution1;
|
|
} else {
|
|
d1 = src1->gd;
|
|
w1 = src1->gw;
|
|
d2 = src2->gd;
|
|
w2 = src2->gw;
|
|
dst->gd = d1 * dilution1 + d2;
|
|
ed1 = exp(-d1 * dilution1);
|
|
ed2 = exp(-d2);
|
|
dst->gw = dst->gd * ((1 - ed1) * w1 / d1 +
|
|
ed1 * (1 - ed2) * w2 / d2) /
|
|
(1 - ed1 * ed2);
|
|
}
|
|
|
|
if (src1->bd < 1e-4) {
|
|
dst->bd = src2->bd;
|
|
dst->bw = src2->bw;
|
|
} else if (src2->bd < 1e-4) {
|
|
dst->bd = src1->bd * dilution1;
|
|
dst->bw = src1->bw * dilution1;
|
|
} else {
|
|
d1 = src1->bd;
|
|
w1 = src1->bw;
|
|
d2 = src2->bd;
|
|
w2 = src2->bw;
|
|
dst->bd = d1 * dilution1 + d2;
|
|
ed1 = exp(-d1 * dilution1);
|
|
ed2 = exp(-d2);
|
|
dst->bw = dst->bd * ((1 - ed1) * w1 / d1 +
|
|
ed1 * (1 - ed2) * w2 / d2) /
|
|
(1 - ed1 * ed2);
|
|
}
|
|
|
|
}
|
|
|
|
void wet_flow(WetLayer * layer)
|
|
{
|
|
/* XXX: Is this like a convolution operation? BSAR */
|
|
int x, y;
|
|
int width = layer->width;
|
|
int height = layer->height;
|
|
int rs = layer->rowstride;
|
|
double *flow_t, *flow_b, *flow_l, *flow_r;
|
|
double *fluid, *outflow;
|
|
WetPix *wet_line = layer->buf;
|
|
WetPix *wet_old;
|
|
int my_height;
|
|
int ix;
|
|
double ft, fb, fl, fr; /* top, bottom, left, right */
|
|
WetPixDbl wet_mix, wet_tmp;
|
|
|
|
flow_t = g_new(double, width * height);
|
|
flow_b = g_new(double, width * height);
|
|
flow_l = g_new(double, width * height);
|
|
flow_r = g_new(double, width * height);
|
|
fluid = g_new(double, width * height);
|
|
outflow = g_new(double, width * height);
|
|
wet_old = g_new(WetPix, width * height);
|
|
|
|
/* assumes rowstride == width */
|
|
memcpy(wet_old, layer->buf, sizeof(WetPix) * width * height);
|
|
|
|
ix = width + 1;
|
|
for (y = 1; y < height - 1; y++) {
|
|
wet_line += rs;
|
|
for (x = 1; x < width - 1; x++) {
|
|
if (wet_line[x].w > 0) {
|
|
my_height = wet_line[x].h + wet_line[x].w;
|
|
ft = (wet_line[x - rs].h +
|
|
wet_line[x - rs].w) - my_height;
|
|
fb = (wet_line[x + rs].h +
|
|
wet_line[x + rs].w) - my_height;
|
|
fl = (wet_line[x - 1].h +
|
|
wet_line[x - 1].w) - my_height;
|
|
fr = (wet_line[x + 1].h +
|
|
wet_line[x + 1].w) - my_height;
|
|
|
|
fluid[ix] =
|
|
0.4 * sqrt(wet_line[x].w * 1.0 /
|
|
255.0);
|
|
|
|
/* smooth out the flow a bit */
|
|
flow_t[ix] =
|
|
0.1 * (10 + ft * 0.75 - fb * 0.25);
|
|
if (flow_t[ix] > 1)
|
|
flow_t[ix] = 1;
|
|
if (flow_t[ix] < 0)
|
|
flow_t[ix] = 0;
|
|
flow_b[ix] =
|
|
0.1 * (10 + fb * 0.75 - ft * 0.25);
|
|
if (flow_b[ix] > 1)
|
|
flow_b[ix] = 1;
|
|
if (flow_b[ix] < 0)
|
|
flow_b[ix] = 0;
|
|
flow_l[ix] =
|
|
0.1 * (10 + fl * 0.75 - fr * 0.25);
|
|
if (flow_l[ix] > 1)
|
|
flow_l[ix] = 1;
|
|
if (flow_l[ix] < 0)
|
|
flow_l[ix] = 0;
|
|
flow_r[ix] =
|
|
0.1 * (10 + fr * 0.75 - fl * 0.25);
|
|
if (flow_r[ix] > 1)
|
|
flow_r[ix] = 1;
|
|
if (flow_r[ix] < 0)
|
|
flow_r[ix] = 0;
|
|
|
|
outflow[ix] = 0;
|
|
}
|
|
ix++;
|
|
}
|
|
ix += 2;
|
|
}
|
|
|
|
ix = width + 1;
|
|
wet_line = layer->buf;
|
|
for (y = 1; y < height - 1; y++) {
|
|
wet_line += rs;
|
|
for (x = 1; x < width - 1; x++) {
|
|
if (wet_line[x].w > 0) {
|
|
/* reduce flow in dry areas */
|
|
flow_t[ix] *= fluid[ix] * fluid[ix - rs];
|
|
outflow[ix - rs] += flow_t[ix];
|
|
flow_b[ix] *= fluid[ix] * fluid[ix + rs];
|
|
outflow[ix + rs] += flow_b[ix];
|
|
flow_l[ix] *= fluid[ix] * fluid[ix - 1];
|
|
outflow[ix - 1] += flow_l[ix];
|
|
flow_r[ix] *= fluid[ix] * fluid[ix + 1];
|
|
outflow[ix + 1] += flow_r[ix];
|
|
}
|
|
ix++;
|
|
}
|
|
ix += 2;
|
|
}
|
|
|
|
wet_line = layer->buf;
|
|
ix = width + 1;
|
|
for (y = 1; y < height - 1; y++) {
|
|
wet_line += rs;
|
|
for (x = 1; x < width - 1; x++) {
|
|
if (wet_line[x].w > 0) {
|
|
wet_pix_reduce(&wet_mix, &wet_old[ix],
|
|
1 - outflow[ix]);
|
|
|
|
wet_pix_reduce(&wet_tmp, &wet_old[ix - rs],
|
|
flow_t[ix]);
|
|
wet_pix_combine(&wet_mix, &wet_mix,
|
|
&wet_tmp);
|
|
wet_pix_reduce(&wet_tmp, &wet_old[ix + rs],
|
|
flow_b[ix]);
|
|
wet_pix_combine(&wet_mix, &wet_mix,
|
|
&wet_tmp);
|
|
wet_pix_reduce(&wet_tmp, &wet_old[ix - 1],
|
|
flow_l[ix]);
|
|
wet_pix_combine(&wet_mix, &wet_mix,
|
|
&wet_tmp);
|
|
wet_pix_reduce(&wet_tmp, &wet_old[ix + 1],
|
|
flow_r[ix]);
|
|
wet_pix_combine(&wet_mix, &wet_mix,
|
|
&wet_tmp);
|
|
|
|
wet_pix_from_double(&wet_line[x],
|
|
&wet_mix);
|
|
|
|
#if 0
|
|
if (ix % 3201 == 0)
|
|
g_print("%f %f %f %f %f %f\n",
|
|
outflow[ix],
|
|
flow_t[ix],
|
|
flow_b[ix],
|
|
flow_l[ix],
|
|
flow_r[ix], fluid[ix]);
|
|
#endif
|
|
}
|
|
ix++;
|
|
}
|
|
ix += 2;
|
|
}
|
|
|
|
g_free(flow_t);
|
|
g_free(flow_b);
|
|
g_free(flow_l);
|
|
g_free(flow_r);
|
|
g_free(fluid);
|
|
g_free(outflow);
|
|
g_free(wet_old);
|
|
}
|
|
|
|
void wet_dry(WetLayer * layer)
|
|
{
|
|
int x, y;
|
|
WetPix *wet_line = layer->buf;
|
|
int width = layer->width;
|
|
int height = layer->height;
|
|
int rs = layer->rowstride;
|
|
int w;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++) {
|
|
w = wet_line[x].w;
|
|
w -= 1;
|
|
if (w > 0)
|
|
wet_line[x].w = w;
|
|
else
|
|
wet_line[x].w = 0;
|
|
|
|
}
|
|
wet_line += rs;
|
|
}
|
|
}
|
|
|
|
/* Move stuff from the upperlayer to the lower layer. This is filter-level stuff*/
|
|
void wet_adsorb(WetLayer * layer, WetLayer * adsorb)
|
|
{
|
|
int x, y;
|
|
WetPix *wet_line = layer->buf;
|
|
WetPix *ads_line = adsorb->buf;
|
|
int width = layer->width;
|
|
int height = layer->height;
|
|
int rs = layer->rowstride;
|
|
double ads;
|
|
WetPixDbl wet_top;
|
|
WetPixDbl wet_bot;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++) {
|
|
/* do adsorption */
|
|
if (wet_line[x].w == 0)
|
|
continue;
|
|
ads = 0.5 / MAX(wet_line[x].w, 1);
|
|
|
|
wet_pix_to_double(&wet_top, &wet_line[x]);
|
|
wet_pix_to_double(&wet_bot, &ads_line[x]);
|
|
wet_pix_merge(&wet_bot, &wet_top, ads, &wet_bot);
|
|
wet_pix_from_double(&ads_line[x], &wet_bot);
|
|
wet_line[x].rd = wet_line[x].rd * (1 - ads);
|
|
wet_line[x].rw = wet_line[x].rw * (1 - ads);
|
|
wet_line[x].gd = wet_line[x].gd * (1 - ads);
|
|
wet_line[x].gw = wet_line[x].gw * (1 - ads);
|
|
wet_line[x].bd = wet_line[x].bd * (1 - ads);
|
|
wet_line[x].bw = wet_line[x].bw * (1 - ads);
|
|
}
|
|
wet_line += rs;
|
|
ads_line += rs;
|
|
}
|
|
}
|