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

  1. import java.awt.*;
  2. import java.applet.*;
  3. import java.awt.Graphics2D;
  4.  
  5. public class VisualPotential extends Applet
  6. {
  7.     private Image buffer;
  8.     private Graphics2D gBuffer;
  9.  
  10.     // number of charges/balls
  11.     private int c_numbers=3;
  12.     // direction array for every charge/ball
  13.     private int[] direction=new int[c_numbers];
  14.     // charge array
  15.     private Charge[] c_array=new Charge[c_numbers];
  16.     // "position change" for ball physics
  17.     private double pos_change=0.01;
  18.     // scale factor (bigger/smaller balls)
  19.     private int scale=40;
  20.     private int size_width,size_height;
  21.  
  22.     // scroller attributes
  23.     private int x_s,y_s=0;
  24.     private int font_width=15;
  25.     private int scroll_speed=2;
  26.     private int scroll_wait=100;
  27.     private String scroller="SCROLLER ALERT! WUB WUB HUE - code by declis @ xdec.de";
  28.  
  29.     public void init()
  30.     {
  31.         size_width=this.getSize().width;
  32.         size_height=this.getSize().height;
  33.         x_s=size_width;
  34.  
  35.         // we are moving in unit square
  36.         // 1.0
  37.         // |
  38.         // |
  39.         // |----- 1.0
  40.  
  41.         c_array[0]=new Charge(0.15,0.35,0.7);
  42.         c_array[1]=new Charge(0.8,0.25,-0.7);
  43.         c_array[2]=new Charge(0.4,0.5,0.5);
  44.  
  45.         direction[0]=4;
  46.         direction[1]=2;
  47.         direction[2]=3;
  48.     }
  49.  
  50.     public void paint(Graphics g)
  51.     {
  52.         // Double-Buffering , THANKS @ JJAM.DE
  53.         if (buffer==null)
  54.         {
  55.             buffer=createImage(size_width,size_height);
  56.             gBuffer=(Graphics2D)buffer.getGraphics();
  57.             gBuffer.setFont(new Font("Arial",Font.BOLD,12));
  58.         }
  59.  
  60.         // calculate potential for every pixel-------------------------------
  61.         for(int h=0;h<size_height;h++)
  62.         {
  63.             for(int w=0;w<size_width;w++)
  64.             {
  65.                 double x_q=(double)w/size_width;
  66.                 double y_q=(double)h/size_height;
  67.                 double potential=0.0;
  68.  
  69.                 for(int i=0;i<c_array.length;i++)
  70.                     potential+=c_array[i].potentialAt(x_q,y_q,scale);
  71.  
  72.                 int red=130+(int)potential;
  73.                 int green=10+(int)potential;
  74.                 int blue=90+(int)potential;
  75.  
  76.                 if(red<0) red=0;        if(red>255) red=255;
  77.                 if(green<0) green=0;    if(green>255) green=255;
  78.                 if(blue<0) blue=0;      if(blue>255) blue=255;
  79.  
  80.                 gBuffer.setColor(new Color(red,green,blue));
  81.                 gBuffer.drawLine(w,h,w,h);
  82.             }
  83.         }
  84.         // ------------------------------------------------------------------
  85.  
  86.         // sine scroller-----------------------------------------------------
  87.         gBuffer.setColor(new Color(255,255,255));
  88.         if(x_s<-(scroller.length()*font_width)-scroll_wait)
  89.             x_s=size_width;     // start again
  90.         for(int i=0;i<scroller.length();i++)
  91.         {
  92.             // check left side of buffer, if char is outside of buffer, next char
  93.             if(x_s+((i+1)*font_width)>0)
  94.             {
  95.                 y_s=(int)(10*Math.sin(0.05*(x_s+(i*font_width)))+20);
  96.                 gBuffer.drawString(""+scroller.charAt(i),x_s+(i*font_width),y_s);
  97.             }
  98.             // check right side of buffer, if char outside, break for()
  99.             if(x_s+((i+1)*font_width)>size_width) i=scroller.length();
  100.         }
  101.         x_s-=scroll_speed;
  102.         // ------------------------------------------------------------------
  103.  
  104.         g.drawImage (buffer,0,0,this);
  105.         try {Thread.sleep(10);}
  106.         catch (InterruptedException e){}
  107.  
  108.         /* ******************************************************************
  109.          * small ball physics for n-balls with different directions
  110.          *
  111.          *  0
  112.          *  |
  113.          *  |   1     2
  114.          *  |       /
  115.          *  |     (b)
  116.          *  |    /
  117.          *  |   3     4
  118.          *  -------------- x=1.0
  119.          * y=1.0
  120.          *
  121.          *  1: upper left
  122.          *  2: upper right
  123.          *  3: bottom left
  124.          *  4: bottom right
  125.          *
  126.          ****************************************************************** */
  127.         for(int i=0;i<c_array.length;i++)
  128.         {
  129.             if(direction[i]==1)     // move to upper left
  130.             {
  131.                 c_array[i].set_y(c_array[i].get_y()-pos_change);
  132.                 c_array[i].set_x(c_array[i].get_x()-pos_change);
  133.             }
  134.             if(direction[i]==2)     // move to upper right
  135.             {
  136.                 c_array[i].set_y(c_array[i].get_y()-pos_change);
  137.                 c_array[i].set_x(c_array[i].get_x()+pos_change);
  138.             }
  139.             if(direction[i]==3)     // move to bottom left
  140.             {
  141.                 c_array[i].set_y(c_array[i].get_y()+pos_change);
  142.                 c_array[i].set_x(c_array[i].get_x()-pos_change);
  143.             }
  144.             if(direction[i]==4)     // move to bottom right
  145.             {
  146.                 c_array[i].set_y(c_array[i].get_y()+pos_change);
  147.                 c_array[i].set_x(c_array[i].get_x()+pos_change);
  148.             }
  149.  
  150.             // for every direction ->2 possible over-/underflows
  151.             if(direction[i]==2&&c_array[i].get_x()>1.0)
  152.                 direction[i]=1;
  153.             if(direction[i]==4&&c_array[i].get_x()>1.0)
  154.                 direction[i]=3;
  155.             if(direction[i]==3&&c_array[i].get_y()>1.0)
  156.                 direction[i]=1;
  157.             if(direction[i]==4&&c_array[i].get_y()>1.0)
  158.                 direction[i]=2;
  159.  
  160.             if(direction[i]==1&&c_array[i].get_x()<0)
  161.                 direction[i]=2;  // ok
  162.             if(direction[i]==3&&c_array[i].get_x()<0)
  163.                 direction[i]=4; // ok
  164.             if(direction[i]==2&&c_array[i].get_y()<0)
  165.                 direction[i]=4;
  166.             if(direction[i]==1&&c_array[i].get_y()<0)
  167.                 direction[i]=3;
  168.         }
  169.  
  170.         repaint();
  171.     }
  172.  
  173.     public void update(Graphics g) {paint(g);}
  174. }
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

  1. public class Charge
  2. {
  3.     private double x,y,charge_load;
  4.  
  5.     public Charge(double x,double y,double charge_load)
  6.     {
  7.         this.x=x;
  8.         this.y=y;
  9.         this.charge_load=charge_load;
  10.     }
  11.  
  12.     // V=kQ/r (electric potential)
  13.     // r = distance between 2 points
  14.     // Q = point charge
  15.     // k = const. = 8.99 * 10^9 N*m^2/C^2
  16.     // x,y = coordinates in unit square
  17.     // k is set to a lower value (for color creation)
  18.  
  19.     public double potentialAt(double x,double y,int k_factor)
  20.     {return (k_factor*charge_load)/(Math.sqrt((this.x-x)*(this.x-x)+(this.y-y)*(this.y-y)));}
  21.  
  22.     public double get_x(){return x;}
  23.     public double get_y(){return y;}
  24.     public double get_q(){return charge_load;}
  25.     public void set_x(double x){this.x=x;}
  26.     public void set_y(double y){this.y=y;}
  27.     public void set_charge_load(double q){this.charge_load=q;}
  28. }
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.

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.