//
import java.awt.*;
public class VolumeApplet extends PixApplet
{
public double light[] = {1,1,1}, hilite[] = {.5,.5,1};
double T[][], M[][], D[][];
double X, Y, Z, R, C = 1, S = 0;
double xx, yy, zz;
double normal[] = new double[3];
double rgb[] = new double[3];
double zRate = 1;
int skip = 1;
int animationFrame = 0;
public void setBg(double r, double g, double b) {
bg[0] = r; bg[1] = g; bg[2] = b;
}
public void setAmbient(double r, double g, double b) {
ambient[0] = r; ambient[1] = g; ambient[2] = b;
}
public void setDiffuse(double r, double g, double b) {
diffuse[0] = r; diffuse[1] = g; diffuse[2] = b;
}
public void setSpecular(double r, double g, double b, double p) {
specular[0] = r; specular[1] = g; specular[2] = b;
power = p;
}
public void initialize() { }
public void restart() { }
public void animate(int f) { }
public double density(double x, double y, double z) { return 0; }
void lighting(double normal[], double rgb[]) {
double t = Math.max(0, .5 + .3 * (normal[0] + normal[1] + normal[2]));
double s = Math.pow(t, power);
rgb[0] = t * diffuse[0] + s * specular[0];
rgb[1] = t * diffuse[1] + s * specular[1];
rgb[2] = t * diffuse[2] + s * specular[2];
}
public void init() {
super.init();
D = new double[W][H];
M = new double[W][H];
T = new double[W][H];
R = W/2;
initialize();
setToBackground();
}
void setToBackground() {
int rgb = pack(f2i(bg[0]),f2i(bg[1]),f2i(bg[2]));
int i = 0;
for (int y = 0 ; y < H ; y++)
for (int x = 0 ; x < W ; x++)
pix[i++] = rgb;
}
void freshStart() {
frame0 = frame;
initialize();
setToBackground();
restart();
clearRender = true;
}
int frame, frame0 = 0;
public void setPix(int frame) {
this.frame = frame;
if (skip == 3 && frame-frame0 == W/skip) {
skip = 1;
specular[0] *= .5;
specular[1] *= .5;
specular[2] *= .5;
frame0 = frame;
restart();
clearRender = true;
}
if (frame-frame0 == W/skip) {
outputImage();
animate(animationFrame++);
freshStart();
}
if (clearRender) {
for (int x = 0 ; x < W ; x++)
for (int y = 0 ; y < H ; y++) {
D[x][y] = 0;
M[x][y] = 1;
T[x][y] = 1;
}
clearRender = false;
}
damage = true;
for (int x = skip ; x < W ; x += skip)
for (int y = skip ; y < H ; y += skip) {
M[x-skip][y-skip] = M[x][y];
if (isSlice) {
D[x][y] = 0;
M[x][y] = 1; // SHADOW MASKING
T[x][y] = 1; // REMAINING FRONT-TO-BACK TRANSPARENCY
Z = sliceZ;
}
else {
if (T[x][y] < .01)
continue;
Z = (R - skip*zRate*(frame-frame0)) / R;
}
X = (x - R) / R;
Y = (y - R) / R;
double p = 1 + .2*Z;
X /= p;
Y /= p;
xx = X;
//yy = Y*C+Z*S;
//zz = -Y*S+Z*C;
yy = Y;
zz = Z;
double d = Math.max(0, Math.min(.99,
skip*zRate * density(xx, yy, zz)));
if (d == .99)
D[x][y] = 1;
else if (d > .01 && d < .99) {
if (isSlice) {
int c = (int)(255 * d);
pix[xy2i(x,H-y)] = pack(c,c,c);
continue;
}
double dx = D[x-skip][y];
double dy = D[x][y-skip];
double dz = D[x][y];
if (isSlice)
dz = 1;
if (dx > 0 && dx < 1 && dy > 0 && dy < 1 && dz > 0 && dz < 1) {
double transparency = Math.pow(2.71828, -d);
double contrib = T[x][y] * (1 - transparency);
double red = ambient[0];
double grn = ambient[1];
double blu = ambient[2];
if (M[x][y] >= .01) {
normal[0] = dx - d;
normal[1] = dy - d;
normal[2] = d - dz;
if (normal[0]!=0 && normal[1]!=0 && normal[2]!=0)
normalize(normal);
lighting(normal, rgb);
red += M[x][y] * rgb[0];
grn += M[x][y] * rgb[1];
blu += M[x][y] * rgb[2];
}
int i = xy2i(x,H-y);
int rgb = pack(f2i(lerp(contrib, i2f(unpack(pix[i],0)), red)),
f2i(lerp(contrib, i2f(unpack(pix[i],1)), grn)),
f2i(lerp(contrib, i2f(unpack(pix[i],2)), blu)));
pix[i] = rgb;
if (skip > 1)
for (int I = 0 ; I < skip ; I++)
for (int J = 0 ; J < skip ; J++)
pix[i + W*I + J] = rgb;
T[x][y] *= transparency;
M[x-skip][y-skip] *= transparency;
}
D[x][y] = d;
}
}
blur(M);
}
double tmpX[], tmpY[];
void blur(double M[][]) {
if (tmpY == null)
tmpY = new double[H];
for (int x = 0 ; x < W ; x++) {
for (int y = 1 ; y < H-1 ; y++)
tmpY[y] = M[x][y-1]/6 + 2*M[x][y]/3 + M[x][y+1]/6;
for (int y = 1 ; y < H-1 ; y++)
M[x][y] = tmpY[y];
}
if (tmpX == null)
tmpX = new double[W];
for (int y = 0 ; y < H ; y++) {
for (int x = 1 ; x < W-1 ; x++)
tmpX[x] = M[x-1][y]/6 + 2*M[x][y]/3 + M[x+1][y]/6;
for (int x = 1 ; x < W-1 ; x++)
M[x][y] = tmpX[x];
}
}
void normalize(double v[]) {
double t = Math.sqrt(dot(v,v));
v[0] /= t;
v[1] /= t;
v[2] /= t;
}
//////////////// MATH PRIMITIVES //////////////////////////
double dot(double a[], double b[]) { return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; }
double scurve(double t) { return t < 0 ? 0 : t > 1 ? 1 : t*t*(3-t-t); }
double lerp(double t,double a,double b) { return a + t * (b - a); }
double i2f(int i) { return i / 255.; }
int f2i(double f) { return Math.max(0,Math.min(255,(int)(255 * f))); }
double boundary(double t) { return 10 * scurve(t); }
double clip(double lo,double hi,double t) {
return Math.max(lo, Math.min(hi, t));
}
static final double LOG_HALF = Math.log(0.5);
static double gain(double a, double b) {
double p;
if (a<.001)
return 0.;
else if (a>.999)
return 1;
b = (b<.001) ? .0001 : (b>.999) ? .999 : b;
p = Math.log(1. - b) / LOG_HALF;
if (a < 0.5)
return Math.pow(2 * a, p) / 2;
else
return 1. - Math.pow(2 * (1. - a), p) / 2;
}
static double bias(double a, double b) {
if (a < .001)
return 0.;
else if (a > .999)
return 1.;
else if (b < .001)
return 0.;
else if (b > .999)
return 1.;
else
return Math.pow(a, Math.log(b) / LOG_HALF);
}
//////////////// COLORS //////////////////////////
private double ambient[] = {.2,.2,.2};
private double diffuse[] = {.8,.8,.8};
private double specular[] = {0,0,0}, power = 2;
private double bg[] = {.5,.5,.8};
public double fg[] = {1,1,1};
//////////////// GEOMETRIC PRIMITIVES //////////////////////////
public double ball(double cx, double cy, double cz, double r) {
double x = xx - cx, y = yy - cy, z = zz - cz;
double xyz = x*x + y*y + z*z;
return transition(.04 * R * (r*r - xyz) / r);
}
public double superquadric(double cx, double cy, double cz, double r, double p) {
double x = Math.abs(xx - cx), y = Math.abs(yy - cy), z = Math.abs(zz - cz);
if (x > r || y > r || z > r)
return 0;
double xyz = Math.pow(x,p) + Math.pow(y,p) + Math.pow(z,p);
double rr = Math.pow(r,p);
return transition(.1 * R * (rr - xyz) / (p * rr/r));
}
public double transition(double t) {
return clip(0,1,t);
}
///////////////// USER INTERACTION FUNCTIONS //////////////////////
boolean clearRender = true;
boolean isSlice = false;
double sliceZ = 0.0;
public boolean keyUp(Event e, int key) {
switch (key) {
case 's':
isSlice = ! isSlice;
clearRender = true;
break;
default:
break;
}
return true;
}
double theta = 0;
int mx = 0;
public boolean mouseDown(Event e, int x, int y) {
mx = x;
return true;
}
public boolean mouseDrag(Event e, int x, int y) {
if (isSlice) {
sliceZ = 2. * y / H - 1;
clearRender = true;
}
theta += (double)(x - mx) / W;
mx = x;
return true;
}
public boolean mouseUp(Event e, int x, int y) {
if (isSlice) {
sliceZ = 2. * y / H - 1;
clearRender = true;
}
else {
System.out.println(theta);
C = Math.cos(theta);
S = Math.sin(theta);
freshStart();
}
return true;
}
void outputImage() {
System.out.println(animationFrame + " " + W + " " + H);
int i = 0;
for (int y = 0 ; y < H ; y++)
for (int x = 0 ; x < W ; x++) {
System.out.print(int2hex(pix[i++]));
if (i % 10 == 0)
System.out.println();
}
System.out.println();
}
String int2hex(int n) {
return i2h(n >> 16) + i2h(n >> 8) + i2h(n);
}
String i2h(int n) {
n &= 255;
return nibble(n >> 4) + nibble(n);
}
String nibble(int n) {
n &= 15;
return "" + (char)(n < 10 ? '0' + n : 'a' + (n-10));
}
void inputImage() {
}
}