Jinroh: The metaballs/isosurfaces script requires the newest version of evaldraw. There are some enums that are dependant on enums higher up I think. I've used that feature in my newest script like this
enum{imgW = 180, imgH = 164, tileSiz = 4} // adjust these according to the image size
enum{W=imgW/tileSiz+1, H=imgH/tileSiz+1 };
Saves having to redefine W and H when I change imgW and imgH. Great

For quite some time I've wanted to see how this looks in action:
http://freespace.virgin.net/hugo.elias/graphics/x_warp.htmSat down and converted Hugo's C-code to evaldraw. I've been tweaking it all day!

Variables to tweak:
If you want your own image (CTRL+P) scroll down to // Tweak em! and set useImg = 1;
If you want the screen to be scaled correctly according to the size of your image set
imgW and imgH to the size in pixels of your image. For instance imgW=640, imgH=480
If the code runs to slowly, try increasing the tiled image size, tileSiz = 4. Also, just try adjusting it for fun. If you set tileSiz really big. Like 32,64 or 128 and the spring dampning high, and the elasticity very low, like this:
static springDampning = 1; // but not higher.
static elasticity = 0.0000002; //0.00001;
The program behaves like one of those cheesy "manipulate someones face" programs (pinch, pull the picture with mouse).
Try disabling the mixing of colors. Comment/uncomment mix() The mix() function takes the previous frame, the currently warped frame and the source image and blend/mix them togheter. Try tweaking the mixing factors.
One more thing. Left/right click the image to push/pull the grid nodes that define the flow map. They are springs, so they will bounce back.
This is what it looks like:

And If you don't spec and image with CTRL+P

enum{imgW = 180, imgH = 164, tileSiz = 4} // adjust these according to the image size
enum{W=imgW/tileSiz+1, H=imgH/tileSiz+1 };
static offsX[W][H];
static offsY[W][H];
static offsXV[W][H];
static offsYV[W][H];
static offsXINT[W][H];
static offsYINT[W][H];
static offsXacc[W][H];
static offsYacc[W][H];
static offsC[W][H];
/*
static norms[3][3] =
{
0, 16, 32,
32, 16*1.414214, 16*2.236068,
32, 16*2.236068, 32*1.414214
};
*/
static norms[3][3] =
{
0, tileSiz, tileSiz*2,
tileSiz*2, tileSiz*1.414214, tileSiz*2.236068,
tileSiz*2, tileSiz*2.236068, tileSiz*2*1.414214
};
static pal[255]; // palette for funky texture.
static img1[imgW][imgH]; // original
static img2[imgW][imgH]; // temp
static texture[imgW][imgH];
// Tweak em!
static useImg = 0; // use pic or generated texture
static randNodeStart = 0;
static springDampning = 0.9999;
static elasticity = 0.000001; //0.00001;
()
{
cls(); clz(1e32);
if(numframes==0){
init();
if(useImg==1)
{
for(x=0; x<imgW; x++)
for(y=0; y<imgH; y++)
{
texture[x][y] = pic(x,y);//pic("flower.jpg",x,y);
img1[x][y] = texture[x][y];
}
}
else
{
DrawTexture(tileSiz,tileSiz);
}
}
//for(i=0; i<10; i++)
Relax();
TextureWarp(); // read img1, store in img2
mix(); // try turning on/off
static screen[imgh][imgw]; // contains texture to fill screen.
for(x=0; x<imgw; x++)
for(y=0; y<imgh; y++)
{
//setcol( result[x][y] ); setpix(x,y);
screen[y][x] = img2[x][y]; //swap x,y
}
t=glsettex(screen,imgw,imgh);
//
//drawspr(xres/2, yres/2,imgw,0);
//texquad(0,0,xres,yres);
setcol(0xffffff); // full brightness
texquad(0,0,xres,yres);
glremovetex(t);
agitateGrid();
//drawGrid();
}
texquad(x,y,width,height)
{
glBegin(GL_COMPLEX);
glTexCoord(0,0);glVertex(x,y);
glTexCoord(1,0);glVertex(x+width,y);
glTexCoord(1,1);glVertex(x+width,y+height);
glTexCoord(0,1);glVertex(x,y+height);
glEnd();
}
agitateGrid(){
direction = 0;
if(bstatus == 1)
{
direction = .5;
}
else if(bstatus == 2)
{
direction = -.5;
}
if(direction != 0)
for(x=0; x<W; x++)
for(y=0; y<H; y++)
{
if( (x==0) || (x==W-1) || (y==0) || (y==H-1) )
{
// dont disturb edge nodes
}
else{
mx = mousx*W/xres;
my = mousy*H/yres;
drawsph(mx,my,1);
xp = x;
yp = y;
//if(xp < mx+30 && xp > mx-30)
//if(yp < my+30 && yp > my-30)
distToPoint = abs( (mx-xp)^2 + (my-yp)^2 );
if(distToPoint < 4)
{
d = atan2(yp-my, xp-mx);
//dist = ( (xp-mx)^2 + (yp-my)^2);
//offsX[x][y] += direction*cos(d) * min(.5, max(0,dist) );
//offsY[x][y] += direction*sin(d) * min(.5, max(0,dist) );
offsX[x][y] += direction*cos(d);
offsY[x][y] += direction*sin(d);
}
}
}
}
drawGrid(){
xscale = xres/(W*tileSiz);
yscale = yres/(H*tileSiz);
for(x=0; x<W; x++)
for(y=0; y<H; y++)
{
//c = int(rnd+.2)*255;
c=255; setcol(c,c,c);
drawsph( offsX[x][y]*xscale, offsY[x][y]*yscale, 2);
}
}
mix()
{
for(x=0; x<imgW; x++)
for(y=0; y<imgH; y++){
rn=0; gn=0; bn=0; // new RGB
ro=0; go=0; bo=0; // old RGB
rs=0; gs=0; bs=0; // src RGB
hex2rgb(img2[x][y],rn,gn,bn); // newly warped
hex2rgb(img1[x][y],ro,go,bo); // previous frame
//hex2rgb(pic("flower.jpg",x,y),rs,gs,bs); // src
hex2rgb(texture[x][y],rs,gs,bs); // src
new = 75; // Tweak this. from 1-10 f.ex. Tweak everything :)
old = 15;
src = 100-old-new;
new /= 100;
old /= 100;
src /= 100;
red = rn*new + ro*old + rs*src;
green = gn*new + go*old + gs*src;
blue = bn*new + bo*old + bs*src;
img1[x][y] = rgb(red,green,blue);
}
}
TextureWarp()
{
for(y=0; y<H-1; y++)
for(x=0; x<W-1; x++)
{
TextureBlock(x,y);
}
}
// img1 -> img2
TextureBlock(xo,yo)
{
xi = xo * tileSiz-1;
yi = yo * tileSiz-1;
Ax=offsXint[xo][yo];
Ay=offsYint[xo][yo];
Bx=offsXint[xo+1][yo];
By=offsYint[xo+1][yo];
Cx=offsXint[xo][yo+1];
Cy=offsYint[xo][yo+1];
Dx=offsXint[xo+1][yo+1];
Dy=offsYint[xo+1][yo+1];
// Image corners, Left, right
// A B
// C D
TX1=0, TY1=0, TX2=0, TY2=0; tx=0; ty=0;
VLDx = (Cx-Ax)/tileSiz; // Rate of change X down left
VRDx = (Dx-Bx)/tileSiz; // X down right
VLDy = (Cy-Ay)/tileSiz; // Y down left
VRDy = (Dy-By)/tileSiz; // Y down right
TX1 = Ax;
TY1 = Ay;
TX2 = Bx;
TY2 = By;
for(y=yi; y<yi+tileSiz; y++)
{
HDx = (TX2-TX1) / tileSiz; // Rate of change across polygon
HDy = (TY2-TY1) / tileSiz; // Rate of change across polygon
tx = TX1;
ty = TY1;
for(x=xi; x<xi+tileSiz; x++)
{
img2[x][y] = img1[ tx ][ ty ];
tx += HDx;
ty += HDy;
}
TX1 += VLDx;
TY1 += VLDy;
TX2 += VRDx;
TY2 += VRDy;
}
}
// Weird texture!
DrawTexture(xs,ys)
{
VLDc=0; VRDc=0; HDc=0;
C1=0; C2=0; c=0;
x=0; y=0; xi=0; yi=0;
for(yi=0; yi<H-1; yi++)
for(xi=0; xi<W-1; xi++)
{
VLDc = (offsC[xi][yi+1] - offsC[xi][yi])/ys;
VRDc = (offsC[xi+1][yi+1] - offsC[xi+1][yi])/ys;
C1 = offsC[xi][yi];
C2 = offsC[xi+1][yi];
for(y=yi*ys; y<(yi+1)*ys; y++)
{
HDc = (C2-C1) / ys;
c = C1;
for(x=xi*xs; x<(xi+1)*xs; x++)
{
img1[x][y] = 4*pal[c];
texture[x][y] = img1[x][y];
c += HDc;
}
C1 += VLDc;
C2 += VRDc;
}
}
}
Init()
{
// setup garish palette
for(i=0; i<64; i++)
{
pal[i] = rgb(i, 0, 63-i);
pal[i+64] = rgb(63-i,i, 0);
pal[i+128] = rgb(0, 63-i, i);
pal[i+192] = rgb(i, 0, 63-i);
}
for(y=0; y<H; y++)
for(x=0; x<W; x++)
{
if( (x==0) || (x==W-1) || (y==0) || (y==H-1) )
{
offsX[x][y] = x*tileSiz;;
offsY[x][y] = y*tileSiz;;
offsXv[x][y] = 0;
offsYv[x][y] = 0;
}
else
{
offsX[x][y] = x*tileSiz;
offsY[x][y] = y*tileSiz;
if(randNodeStart==1)
{
offsX[x][y] += nrnd*2;
offsY[x][y] += nrnd*2;
}
offsXv[x][y] = 0;
offsYv[x][y] = 0;
}
offsXint[x][y] = offsX[x][y];
offsYint[x][y] = offsY[x][y];
offsXacc[x][y] = 0;
offsYacc[x][y] = 0;
offsC[x][y] = int(rnd*255);
}
}
Relax()
{
for(y=1; y<H-1; y++)
{
yh = y+2;
yl = y-2;
if(yl<0) yl=0;
if(yh>H-1) yh = H-1;
for(x=1; x<W-1; x++)
{
xh = x+2;
xl = x-2;
if(xl<0) xl=0;
if(xh>W-1) xh=W-1;
for(yi=yl; yi<=yh; yi++)
{
for(xi=xl; xi<=xh; xi++)
{
if( (xi != x) || (yi != y) )
{
xspring = offsX[xi][yi] - offsX[x][y];
yspring = offsY[xi][yi] - offsY[x][y];
norm = norms[abs(xi-x)][abs(yi-y)];
length = sqrt(xspring^2 + yspring^2);
scaler = (norm-length) * elasticity; //tweak
xspring *= scaler;
yspring *= scaler;
offsXv[xi][yi] += xspring;
offsYv[xi][yi] += yspring;
offsXv[xi][yi] *= springDampning;
offsYv[xi][yi] *= springDampning;
}
}
}
}
}
for(y=1; y<H-1; y++)
for(x=1; x<W-1; x++)
{
if( (x==0) || (x==W-1) || (y==0) || (y==H-1) )
{
}
else
{
offsX[x][y] += offsXv[x][y];
offsY[x][y] += offsYv[x][y];
offsXv[x][y] *= 0.9999;
offsYv[x][y] *= 0.9999;
}
// need to trunc this to integer?
offsXint[x][y] = ( offsX[x][y] );
offsYint[x][y] = ( offsY[x][y] );
/*
// allows it to handle non-integer values of points on the grid
offsXacc[x][y] += offsX[x][y];
if(offsXacc[x][y] > 1)
{
offsXacc[x][y] -= 1;
offsXint[x][y] += 1;
}
if(offsXacc[x][y] < 1)
{
offsXacc[x][y] += 1;
offsXint[x][y] -= 1;
}
offsYacc[x][y] += offsY[x][y];
if(offsYacc[x][y] > 1)
{
offsYacc[x][y] -= 1;
offsYint[x][y] += 1;
}
if(offsYacc[x][y] < 1)
{
offsYacc[x][y] += 1;
offsYint[x][y] -= 1;
}
*/
}
// make sure points on the grid never stray outside screen
for(x=0; x<W-1; x++)
{
if( offsYint[x][1] < 0 ) offsYint[x][1] = 0;
if( offsYint[x][H-2] > imgH ) offsYint[x][H-2] = imgH;
}
for(y=0; y<H-1; y++)
{
if( offsXint[1][y] < 0 ) offsXint[1][y] = 0;
if( offsXint[W-2][y] > imgW ) offsXint[W-2][y] = imgW;
}
}
hex2rgb(mrgb,&r,&g,&b)
{
r = (mrgb / 2^16);
g = (mrgb / 2^8) % 256;
b = mrgb % 256;
}