Java-Applet – Metaballs rendern (Animation)

aIn 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

Start Applet

Zwei positive Metaballs treffen aufeinander:

b c d e

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;}
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

This site uses Akismet to reduce spam. Learn how your comment data is processed.