In den letzten Tagen habe ich ein kleines Java-Applet geschrieben, um Metaballs darzustellen bzw. zu rendern. Metaballs gehören zu den klassischen Effekten in der Demo-Szene (Realtime-Berechnungen). Auf Wikipedia gibt es mehr Informationen zu Metaballs. In dem Java-Applet werden 2D-Metaballs dargstellt. Im Vorschaubild sind 3 Metaballs zu sehen: 2 positive und 1 negativer Ball (schwarz).
Applet
Zwei positive Metaballs treffen aufeinander:
Source-Code:
File: VisualPotential.class
import java.awt.*;
import java.applet.*;
import java.awt.Graphics2D;
public class VisualPotential extends Applet
{
private Image buffer;
private Graphics2D gBuffer;
// number of charges/balls
private int c_numbers=3;
// direction array for every charge/ball
private int[] direction=new int[c_numbers];
// charge array
private Charge[] c_array=new Charge[c_numbers];
// "position change" for ball physics
private double pos_change=0.01;
// scale factor (bigger/smaller balls)
private int scale=40;
private int size_width,size_height;
// scroller attributes
private int x_s,y_s=0;
private int font_width=15;
private int scroll_speed=2;
private int scroll_wait=100;
private String scroller="SCROLLER ALERT! WUB WUB HUE - code by declis @ xdec.de";
public void init()
{
size_width=this.getSize().width;
size_height=this.getSize().height;
x_s=size_width;
// we are moving in unit square
// 1.0
// |
// |
// |----- 1.0
c_array[0]=new Charge(0.15,0.35,0.7);
c_array[1]=new Charge(0.8,0.25,-0.7);
c_array[2]=new Charge(0.4,0.5,0.5);
direction[0]=4;
direction[1]=2;
direction[2]=3;
}
public void paint(Graphics g)
{
// Double-Buffering , THANKS @ JJAM.DE
if (buffer==null)
{
buffer=createImage(size_width,size_height);
gBuffer=(Graphics2D)buffer.getGraphics();
gBuffer.setFont(new Font("Arial",Font.BOLD,12));
}
// calculate potential for every pixel-------------------------------
for(int h=0;h<size_height;h++)
{
for(int w=0;w<size_width;w++)
{
double x_q=(double)w/size_width;
double y_q=(double)h/size_height;
double potential=0.0;
for(int i=0;i<c_array.length;i++)
potential+=c_array[i].potentialAt(x_q,y_q,scale);
int red=130+(int)potential;
int green=10+(int)potential;
int blue=90+(int)potential;
if(red<0) red=0; if(red>255) red=255;
if(green<0) green=0; if(green>255) green=255;
if(blue<0) blue=0; if(blue>255) blue=255;
gBuffer.setColor(new Color(red,green,blue));
gBuffer.drawLine(w,h,w,h);
}
}
// ------------------------------------------------------------------
// sine scroller-----------------------------------------------------
gBuffer.setColor(new Color(255,255,255));
if(x_s<-(scroller.length()*font_width)-scroll_wait)
x_s=size_width; // start again
for(int i=0;i<scroller.length();i++)
{
// check left side of buffer, if char is outside of buffer, next char
if(x_s+((i+1)*font_width)>0)
{
y_s=(int)(10*Math.sin(0.05*(x_s+(i*font_width)))+20);
gBuffer.drawString(""+scroller.charAt(i),x_s+(i*font_width),y_s);
}
// check right side of buffer, if char outside, break for()
if(x_s+((i+1)*font_width)>size_width) i=scroller.length();
}
x_s-=scroll_speed;
// ------------------------------------------------------------------
g.drawImage (buffer,0,0,this);
try {Thread.sleep(10);}
catch (InterruptedException e){}
/* ******************************************************************
* small ball physics for n-balls with different directions
*
* 0
* |
* | 1 2
* | /
* | (b)
* | /
* | 3 4
* -------------- x=1.0
* y=1.0
*
* 1: upper left
* 2: upper right
* 3: bottom left
* 4: bottom right
*
****************************************************************** */
for(int i=0;i<c_array.length;i++)
{
if(direction[i]==1) // move to upper left
{
c_array[i].set_y(c_array[i].get_y()-pos_change);
c_array[i].set_x(c_array[i].get_x()-pos_change);
}
if(direction[i]==2) // move to upper right
{
c_array[i].set_y(c_array[i].get_y()-pos_change);
c_array[i].set_x(c_array[i].get_x()+pos_change);
}
if(direction[i]==3) // move to bottom left
{
c_array[i].set_y(c_array[i].get_y()+pos_change);
c_array[i].set_x(c_array[i].get_x()-pos_change);
}
if(direction[i]==4) // move to bottom right
{
c_array[i].set_y(c_array[i].get_y()+pos_change);
c_array[i].set_x(c_array[i].get_x()+pos_change);
}
// for every direction ->2 possible over-/underflows
if(direction[i]==2&&c_array[i].get_x()>1.0)
direction[i]=1;
if(direction[i]==4&&c_array[i].get_x()>1.0)
direction[i]=3;
if(direction[i]==3&&c_array[i].get_y()>1.0)
direction[i]=1;
if(direction[i]==4&&c_array[i].get_y()>1.0)
direction[i]=2;
if(direction[i]==1&&c_array[i].get_x()<0)
direction[i]=2; // ok
if(direction[i]==3&&c_array[i].get_x()<0)
direction[i]=4; // ok
if(direction[i]==2&&c_array[i].get_y()<0)
direction[i]=4;
if(direction[i]==1&&c_array[i].get_y()<0)
direction[i]=3;
}
repaint();
}
public void update(Graphics g) {paint(g);}
}
File: Charge.class
public class Charge
{
private double x,y,charge_load;
public Charge(double x,double y,double charge_load)
{
this.x=x;
this.y=y;
this.charge_load=charge_load;
}
// V=kQ/r (electric potential)
// r = distance between 2 points
// Q = point charge
// k = const. = 8.99 * 10^9 N*m^2/C^2
// x,y = coordinates in unit square
// k is set to a lower value (for color creation)
public double potentialAt(double x,double y,int k_factor)
{return (k_factor*charge_load)/(Math.sqrt((this.x-x)*(this.x-x)+(this.y-y)*(this.y-y)));}
public double get_x(){return x;}
public double get_y(){return y;}
public double get_q(){return charge_load;}
public void set_x(double x){this.x=x;}
public void set_y(double y){this.y=y;}
public void set_charge_load(double q){this.charge_load=q;}
}