Code
float tolerance = 0.2;
float cellDim = 3.0;
int poly_render_step = 3;
//TODO: crash condition in the diagonal cases
Metaball[] meta_list = new Metaball[4];
float[] vx = new float[4];
float[] vy = new float[4];
void setup()
{
size(400,400);
ellipseMode(CENTER);
meta_list[0] = new Metaball();
meta_list[1] = new Metaball();
meta_list[2] = new Metaball();
meta_list[3] = new Metaball();
meta_list[0].x = 65;
meta_list[0].y = 215;
meta_list[0].r = 150;
meta_list[1].x = 200;
meta_list[1].y = 20;
meta_list[1].r = 100;
meta_list[2].x = 300;
meta_list[2].y = 250;
meta_list[2].r = 50;
meta_list[3].x = 200;
meta_list[3].y = 300;
meta_list[3].r = 120;
vx[0] = 1;
vx[1] = -1;
vx[2] = 1;
vx[3] = -1;
vy[0] = -1;
vy[1] = 1;
vy[2] = -1;
vy[3] = 1;
}
void draw()
{
background(155,155,255);
Polygon[] p = marching_squares(meta_list);
stroke(255,255,100);
fill(195,195,255);
strokeWeight(3);
for (int i=0; i<p.length; i++)
p[i].draw();
for (int i=0; i<4; i++)
{
meta_list[i].x += vx[i];
meta_list[i].y += vy[i];
if (meta_list[i].x <= 0.0 || meta_list[i].x >= 400.0)
vx[i] *= -1;
if (meta_list[i].y <= 0.0 || meta_list[i].y >= 400.0)
vy[i] *= -1;
}
}
public class Metaball
{
//public variables
public float x=0, y=0, r=0;
float testPoint(float tx, float ty)
{
float tr = dist(x,y,tx,ty);
if (tr < r) return density_function(tr/r);
else return 0.0;
}
float density_function(float t)
{
return (1.0f-t)*(1.0f-t);
}
}
public class Polygon
{
//consider all these read-only
float[] xpts = new float[5];
float[] ypts = new float[5];
int length = 0;
int capacity = 5;
void addPoint(float x, float y)
{
if (length == capacity)
{
capacity *= 2;
float[] nx = new float[capacity];
float[] ny = new float[capacity];
arraycopy(xpts,0,nx,0,length);
arraycopy(ypts,0,ny,0,length);
xpts = nx;
ypts = ny;
}
xpts[length] = x;
ypts[length] = y;
length++;
}
void draw()
{
beginShape(POLYGON);
for (int p=0; p<length; p+=poly_render_step)
vertex(xpts[p],ypts[p]);
endShape();
}
}
Polygon[] marching_squares(Metaball[] cv)
{
Polygon[] r_poly = new Polygon[cv.length];
int x,y;
boolean still_inside, tl, tr, bl, br, union_flag;
int initial_edge_x, initial_edge_y, r_val, num_polys=0;
clear_cache();
clear_visits();
noStroke();
fill(0,255,255);
for (int i=0; i<cv.length; i++)
{
//start at the cell containing the center
//march right until you hit the edge
x = floor(cv[i].x/cellDim);
y = floor(cv[i].y/cellDim);
union_flag = false;
do
{
if (search_visits(x,y))
{
union_flag = true;
break;
}
//debug_rendering
//rect(x*cellDim, y*cellDim, cellDim, cellDim);
//top-left corner
r_val = search_cache(x,y);
if (r_val == -1)
{
tl = inside_test(x,y,cv);
cache_computation(x,y,tl);
}
else tl = (r_val==1);
//bottom-left corner
r_val = search_cache(x,y+1);
if (r_val == -1)
{
bl = inside_test(x,y+1,cv);
cache_computation(x,y+1,bl);
}
else bl = (r_val==1);
//top-right corner
r_val = search_cache(x+1,y);
if (r_val == -1)
{
tr = inside_test(x+1,y,cv);
cache_computation(x+1,y,tr);
}
else tr = (r_val==1);
//bottom-right corner
r_val = search_cache(x+1,y+1);
if (r_val == -1)
{
br = inside_test(x+1,y+1,cv);
cache_computation(x+1,y+1,br);
}
else br = (r_val==1);
visit_cell(x,y);
//Are we still in the metaball?
if (still_inside = (tl && bl && tr && br))
x ++;
}
while(still_inside);
if(union_flag)
continue;
initial_edge_x = x;
initial_edge_y = y;
r_poly[num_polys] = new Polygon();
do
{
visit_cell(x,y);
//top-left corner
r_val = search_cache(x,y);
if (r_val == -1)
{
tl = inside_test(x,y,cv);
cache_computation(x,y,tl);
}
else tl = (r_val==1);
//bottom-left corner
r_val = search_cache(x,y+1);
if (r_val == -1)
{
bl = inside_test(x,y+1,cv);
cache_computation(x,y+1,bl);
}
else bl = (r_val==1);
//top-right corner
r_val = search_cache(x+1,y);
if (r_val == -1)
{
tr = inside_test(x+1,y,cv);
cache_computation(x+1,y,tr);
}
else tr = (r_val==1);
//bottom-right corner
r_val = search_cache(x+1,y+1);
if (r_val == -1)
{
br = inside_test(x+1,y+1,cv);
cache_computation(x+1,y+1,br);
}
else br = (r_val==1);
//now march clockwise around edge, building a polygon
if (!tl && !tr && bl && !br) //CASE 1
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+1.0)*cellDim);
y+=1.0;
}
else if (!tl && !tr && !bl && br) //CASE 2
{
r_poly[num_polys].addPoint((x+1.0)*cellDim, (y+0.5)*cellDim);
x+=1.0;
}
else if (!tl && !tr && bl && br) //CASE 3
{
r_poly[num_polys].addPoint((x+1.0)*cellDim, (y+0.5)*cellDim);
x+=1.0;
}
else if (!tl && tr && !bl && !br) //CASE 4
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+0.0)*cellDim);
y-=1.0;
}
else if (!tl && tr && bl && !br) //CASE 5
{
//let's just call them attached
//POSSIBLE BUG! FIX
if (r_poly[num_polys].xpts[r_poly[num_polys].length-1] == (x+1.0)*cellDim)
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+1.0)*cellDim);
y+=1.0;
}
else
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+0.0)*cellDim);
y-=1.0;
}
}
else if (!tl && tr && !bl && br) //CASE 6
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+0.0)*cellDim);
y-=1.0;
}
else if (!tl && tr && bl && br) //CASE 7
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+0.0)*cellDim);
y-=1.0;
}
else if (tl && !tr && !bl && !br) //CASE 8
{
r_poly[num_polys].addPoint((x+0.0)*cellDim, (y+0.5)*cellDim);
x-=1.0;
}
else if (tl && !tr && bl && !br) //CASE 9
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+1.0)*cellDim);
y+=1.0;
}
else if (tl && !tr && !bl && br) //CASE 10
{
//let's just call them attached
//POSSIBLE BUG! FIX!
if (r_poly[num_polys].ypts[r_poly[num_polys].length-1] == (y+0.0)*cellDim)
{
r_poly[num_polys].addPoint((x+1.0)*cellDim, (y+0.5)*cellDim);
x+=1.0;
}
else
{
r_poly[num_polys].addPoint((x+0.0)*cellDim, (y+0.5)*cellDim);
x-=1.0;
}
}
else if (tl && !tr && bl && br) //CASE 11
{
r_poly[num_polys].addPoint((x+1.0)*cellDim, (y+0.5)*cellDim);
x+=1.0;
}
else if (tl && tr && !bl && !br) //CASE 12
{
r_poly[num_polys].addPoint((x+0.0)*cellDim, (y+0.5)*cellDim);
x-=1.0;
}
else if (tl && tr && bl && !br) //CASE 13
{
r_poly[num_polys].addPoint((x+0.5)*cellDim, (y+1.0)*cellDim);
y+=1.0;
}
else if (tl && tr && !bl && br) //CASE 14
{
r_poly[num_polys].addPoint((x+0.0)*cellDim, (y+0.5)*cellDim);
x-=1.0;
}
else
{
println("OH SHIT!");
}
//debug rendering
//rect(x*cellDim, y*cellDim, cellDim, cellDim);
}
while(!(x == initial_edge_x && y == initial_edge_y));
num_polys++;
}
if (num_polys < r_poly.length)
{
Polygon[] new_poly = new Polygon[num_polys];
arraycopy(r_poly,0,new_poly,0,num_polys);
r_poly = new_poly;
}
return r_poly;
}
CacheNode cacheHead;
VisitNode visitHead;
boolean inside_test(int x, int y, Metaball[] cv)
{
float sum = 0.0;
for (int i=0; i<cv.length; i++)
{
sum += cv[i].testPoint(x*cellDim,y*cellDim);
}
return (sum > tolerance);
}
void clear_visits()
{
visitHead = null;
}
void visit_cell(int x, int y)
{
VisitNode newNode = new VisitNode(x,y);
newNode.next = visitHead;
visitHead = newNode;
}
boolean search_visits(int x, int y)
{
for (VisitNode c = visitHead; c!=null; c=c.next)
if (c.x == x && c.y == y)
return true;
return false;
}
void clear_cache()
{
cacheHead = null;
}
void cache_computation(int x, int y, boolean flag)
{
CacheNode newNode = new CacheNode(x,y,flag);
newNode.next = cacheHead;
cacheHead = newNode;
}
int search_cache(int x, int y)
{
for (CacheNode c = cacheHead; c!=null; c=c.next)
if (c.x == x && c.y == y)
{
if (c.flag)
return 1;
else
return 0;
}
return -1;
}
class VisitNode
{
int x,y;
VisitNode next = null;
VisitNode(int u, int v)
{
x = u;
y = v;
}
}
class CacheNode
{
int x,y;
boolean flag;
CacheNode next = null;
CacheNode(int u, int v, boolean f)
{
x=u;
y=v;
flag=f;
}
}
031 -- Experimenting with Curves: Due 10/11
Statement:Select a curve from the
Mathworld curves site.
I recommend selecting curves whose equations take the standard explicit
form y = f(x), or curves which take the parametric form y = f(t),
x = g(t). See this example for
some code that deals with Polar curves in the form r = f(theta). (For some simple parametric curves to get started with, consider the Spirograph-like roulette curves.) Create a
simple interaction in which the mouseX and mouseY are used to continuously govern two parameters of the curve. You may also want to look at
these interactive applets for some more inspiration.
Connect the shape's internal parameters to the mouse in such a way as to develop a satisfying exploration of the graphic and dynamic possibilities of this shape.
Metaballs!