Hi,
Basically I want to simulate lots of circles bouncing off of each other in a box.
My current plan is to form a grid with lots of points and give each particle a position and speed which is a multiple of the grid spacings. Then at each time move the particles and see if any intersect. I am fairly new to programming so this way is easier in that respect but it seems like this way will be very computationally intensive though.

Do you think this is a good way to do it or will I be wasting my time?


Thanks.

P.S I may do it in C++ or java I haven't decided yet if that makes any difference. (I am not sure yet how easy it is to display it graphically in java yet).

Thanks, I am not too concerned with the graphics to start with (it is a science thing) that website looks quite useful though.

If you are simulating lots of circles bouncing off of each other, you would have to compute whether or not they are currently touching/adjacent by giving each circle a radius, and keeping the current position of the circle's center. Then, as the circle bounced, you could do a calculation between it and all the other circles to figure out if the circle is currently occupying space which is touching another circle's space. If so, bounce them in different directions. Thats the only way I can think to do this assignment. It would be much more difficult to treat each circle as having "multiple points" and then try to see if any of the points are touching than to use each circle's current center point as a marker for whether or not it is adjacent to any other circles (based on their center, radius, and area formulas).

If you haven't already, you may want to look through the Ellipse2D API. It has methods related to containment and intersection that may prove useful.

Comments
good find, forgot all about this

If you haven't already, you may want to look through the Ellipse2D API. It has methods related to containment and intersection that may prove useful.

Thanks, unfortunately I had already started before I read your post but at least I will think to look in the class file next time. Anyway for a first approximation I thought up a simple way to do it so the physics side is coming along nicely :).

Not sure if I should post in the same thread or not but I have (stupidly) decided to try and animate using the ActionListener (I can't figure out the Threads and Runnable stuff, at least not without more time anyway). I have a couple of problems though if someone could point me in the right direction I would be very grateful.

Firstly when I repaint() my blob doesn't come back :'( . Does repaint() simply call the paintcomponent function again? I can't figure out why it isn't working. I pretty much 'stole' the graphics part of my program from the net and my textbook so it might be (hopefully) an obvious problem.

Here is my program with the irrelevant physics taken out.

public class Brownain
{ 
  public static void main(String[] args)
  {
	    JFrame box = new JFrame();
	    Container background = box.getContentPane();
	    background.setBackground(new Color(255,250,202));
	    box.setSize(700, 700);
	    box.setTitle("Browniand Motion.");
	    box.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	    
	    
	    Particle particle = new Particle();
	    box.add(particle);
	    box.setVisible(true);
	 
	    ActionListener listener = new Particle(); Does this mean it doesn't update the particle I made earlier but this 'listener' particle?
	 
	   
	    Timer mover = new Timer(25, listener);
	 
	    mover.start();
	 
	  }
	  
} 


  
class Particle extends JComponent implements ActionListener
{
	public Particle()
	{
		rand = new Random();
		xcentre = rand.nextDouble()*600;
		ycentre = rand.nextDouble()*600;
		System.out.println(xcentre);
		particle = new Ellipse2D.Double(xcentre, ycentre, 30, 30);
	}
	
	public double getx()
	{
		return xcentre;
		
	}
	
	public double gety()
	{
		return ycentre;
	}
	
	public double getRadius()
	{
		return radius;
	}
	
	public void actionPerformed(ActionEvent event)
    {
	
		xcentre+=rand.nextGaussian()*10;
		ycentre+=rand.nextGaussian()*10;
		
		particle.setFrame(xcentre,ycentre,radius,radius);
		
		repaint();
	}
	
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		
		Graphics2D g2 = (Graphics2D) g;
		g2.setPaint(Color.PINK);
		
		g2.fill(particle);
		g2.draw(particle);
	}
	
	private double xcentre;
	private double ycentre;
	private double radius;
	private double mass;
	private double xvel;
	private double yvel;
	private Random rand;
	private Ellipse2D.Double particle;
	

}

Also eventually I want to have many particles on the screen at once and have all there positions updating with the ActionListener but when I add

for(int i=0; i<50; i++)
	    {
	    	molecule[i] = new Particle();
	    	box.add(molecule[i]);
	    }

Only 1 extra particle appears which I find very strange.

Anyway sorry about the long post hopefully it won't get lost hidden in my old thread and someone can help me out :icon_biggrin:

sorry not able to confirm this and test your code right now, but try adding validate() before your repaint()

>Does this mean it doesn't update the particle I made earlier but this 'listener' particle?
Yes. Add the one that you already created, since that is the one you want the timer to call on.

I just ran the code here. You also need to set 'radius'. It's currently zero. I changed this in your constructor

public Particle()
{
    rand = new Random();
    xcentre = rand.nextDouble()*600;
    ycentre = rand.nextDouble()*600;
    radius = 15;
    System.out.println(xcentre);
    particle = new Ellipse2D.Double(xcentre, ycentre, radius*2, radius*2);
}

and it works fine (assuming you changed the timer listener parameter to 'particle')

I think some slight modifications to your design could make things much easier. You may want to consider something like this as a starting point

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Brownian {

    public static void main(String[] args) {
        JFrame box = new JFrame();
        Container background = box.getContentPane();
        background.setBackground(new Color(255, 250, 202));
        box.setSize(700, 700);
        box.setTitle("Browniand Motion.");
        box.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        ParticlePanel particlePanel = new ParticlePanel();
        box.add(particlePanel);
        box.setVisible(true);

        Timer mover = new Timer(25, particlePanel);

        mover.start();

    }
}

/** This panel serves as the container for your Particle objects */
class ParticlePanel extends JPanel implements ActionListener {

    List<Particle> particles = new ArrayList<Particle>();

    public ParticlePanel() {
        super();
        // add a particle
        particles.add(new Particle());
        // this is all you need to add another
        particles.add(new Particle());
    }

    /** invoked by the Timer callback */
    public void actionPerformed(ActionEvent event) {
        for (Particle p : particles) {
            p.updatePosition();
        }
        repaint();
    }

    public void paintComponent(Graphics g) {
        // clear the panel
        g.clearRect(0, 0, getWidth(), getHeight());
        // render each particle
        for (Particle p : particles) {
            p.render(g);
        }
    }
}

/** This class now just deals with the specifics of 
 * a particle itself. It doesn't need to know anything 
 * about listeners or the container.
 */
class Particle {

    public Particle() {
        rand = new Random();
        // You could pass these as parameters here if you want to
        // randomize the location and size when you create them.
        // That could be more helpful since this class doesn't really
        // have any knowledge of their container at this point.
        xcentre = rand.nextDouble() * 600;
        ycentre = rand.nextDouble() * 600;
        radius = 15;
        System.out.println(xcentre);
        particle = new Ellipse2D.Double(xcentre, ycentre, radius * 2, radius * 2);
    }

    public double getx() {
        return xcentre;
    }

    public double gety() {
        return ycentre;
    }

    public double getRadius() {
        return radius;
    }

    public void updatePosition() {
        xcentre += rand.nextGaussian() * 10;
        ycentre += rand.nextGaussian() * 10;

        // needs radius*2 because the frame is a bounding box
        particle.setFrame(xcentre, ycentre, radius * 2, radius * 2);
    }

    public void render(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g2.setPaint(Color.PINK);
        g2.fill(particle);
        g2.draw(particle);
    }
    
    /** checks for collision between this and another Particle */
    public boolean collidesWith(Particle p){
        // hint: true if distance between centers is less than 
        // the sum of their radii
        return false;
    }
    
    /** handles the result of a collision with Particle p */
    protected void processCollision(Particle p){
        
    }
    
    private double xcentre;
    private double ycentre;
    private double radius;
    private double mass;
    private double xvel;
    private double yvel;
    private Random rand;
    private Ellipse2D.Double particle;
}
Comments
Thanks very nice of you!

I think some slight modifications to your design could make things much easier. You may want to consider something like this as a starting point

Thanks a lot, that was way more than I expected, really nice of you!
I think putting the listener in its own class will stop getting me so confused. I don't understand why you have radius*2 on line 83 and 103 though?

Anyway thanks again will go and type away!

The size parameters for Ellipse2D specify the bounding box by width(w) and height(h), thus they are twice the radius of your circle.

Hey, I know you have given me lots of help already but I am still having problems. Basically only the particle added when the constructor of particle panel is called shows up on the screen.

(By the way I know the physics isn't working properly yet and it isn't bouncing off walls but once I can see what is happening I can probably sort it out).

import java.util.*;
import java.util.List;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.geom.*;
import java.awt.Color;
import java.util.Random.*;
import java.applet.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
import java.awt.Container;
import java.lang.Math;
import java.io.*;



public class RanWalk
{ 
  public static void main(String[] args) 
  {
	    JFrame box = new JFrame();
	    Container background = box.getContentPane();
	    background.setBackground(new Color(223,255,231));
	    box.setSize(700, 700);
	    box.setTitle("Brownian Motion.");
	    box.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	    
	    Random rand = new Random();
	    
	    ParticlePanel particlePanel = new ParticlePanel(rand);
	    box.add(particlePanel);
	    box.setVisible(true);
	    
	    for(int i = 0; i<50; i++)
	    {
	     particlePanel.add(new Particle(5,3E-26,rand));
	   
	    }
    
	    Timer mover = new Timer(50, particlePanel); //delta t is 0.05 seconds
	    mover.start();
	    
	   /* FileOutputStream out;
	    PrintStream p;
	  
        out = new FileOutputStream("javamotion.csv");
        p = new PrintStream( out );
       
	       p.println(t + "," + particle.getx() + "," + particle.gety());
	        p.close();
		   
	   
	   */
  }
	    
 }

 class Collisions
{
	 Collisions() {}

	public boolean collisioncheck(Particle m, Particle p)
	{
		
		posx = p.getx();
		posy = p.gety();
		posx2 = m.getx();
		posy2 = m.gety();
		distance = Math.sqrt((posx-posx2)*(posx-posx2) + (posy-posy2)*(posy-posy2));
		if(distance <= 5+20) 
			{
			check = true;
			return check;
			}
	    return check;	
	}


public void conservemomentum(Particle m, Particle p)
{	
	mass = p.getMass();
	mass2 = m.getMass();
	
	xvel = p.getXVel();
	yvel = p.getYVel();
	xvel2 = m.getXVel();
	yvel2 = m.getYVel();
	
	nextxvel = ((mass-mass2)/(mass+mass2))*xvel + (2*mass2/(mass+mass2))*xvel2;
	nextyvel = ((mass-mass2)/(mass+mass2))*yvel + (2*mass2/(mass+mass2))*yvel2;
	
	nextxvel2 = ((mass2-mass)/(mass+mass2))*xvel2 + (2*mass/(mass+mass2))*xvel;
	nextyvel2 = ((mass2-mass)/(mass+mass2))*yvel2 + (2*mass/(mass+mass2))*yvel;
	
	p.setXVel(nextxvel);
	p.setYVel(nextyvel);
	m.setXVel(nextxvel2);
	m.setYVel(nextyvel2);
}
	private double mass;
	private double mass2;
	private double xvel;
	private double yvel;
	private double xvel2;
	private double yvel2;
	private double nextxvel;
	private double nextyvel;
	private double nextxvel2;
	private double nextyvel2;
	private double posx;
	private double posy;
	private double posx2;
	private double posy2;
	private double distance;
	private boolean check = false;

}
	
  
class Particle extends JComponent
{
	
	public Particle(double rad, double m, Random r)
	{
	
	    mass = m;
		radius = rad;
		xcentre = r.nextDouble()*600;
		ycentre = r.nextDouble()*600;
		xvel = r.nextGaussian()*10;
		yvel =r.nextGaussian()*10;
		particle = new Ellipse2D.Double(xcentre, ycentre, radius*2, radius*2);
		
	}
	
	public void updateposition(double t)
	{
		xcentre+= t*xvel;
		ycentre+= t*yvel;
		
		if(xcentre < 0) xcentre = -xcentre;
		if(xcentre > 700) xcentre = 1400-xcentre;
		
		if(ycentre < 0) xcentre = -xcentre;
		if(ycentre > 700) xcentre = 1400-xcentre;
	}
			
		
	
	
	public double getx()
	{
		return xcentre;
		
	}
	
	public double gety()
	{
		return ycentre;
	}
	
	public double getRadius()
	{
		return radius;
	}

	public double getMass()
	{
		return mass;
	}
	
	public double getXVel()
	{
		return xvel;
	}
	
	public double getYVel()
	{
		return yvel;
	}
	
	public void setXVel(double v)
	{
		xvel = v;
	}
	
	public void setYVel(double v)
	{
		yvel = v;
	}
	
	private double xcentre;
	private double ycentre;
	private double radius;
	private double mass;
	private double xvel;
	private double yvel;
	private Random rand;
	private Ellipse2D.Double particle;
	
}

class ParticlePanel extends JPanel implements ActionListener 
{
	public double DeltaT = 0.05;

    List<Particle> particles = new ArrayList<Particle>();

    public ParticlePanel(Random r) 
    {
         super();
         particles.add(new Particle(20,5E-15,r));
    }

  
    public void actionPerformed(ActionEvent event)
    {
    	Collisions tool = new Collisions();
    	boolean collision = false;
    	for(int i=0; i <51; i++)
    	{
    
    	collision = tool.collisioncheck(particles.get(i),particles.get(0));  //Particles 0 is the unique particle
    	if(collision) 
    		{
    			tool.conservemomentum(particles.get(i), particles.get(0));
    			i = 51;
    		}
    	}
    	for(Particle p : particles)
    	{
    		p.updateposition(DeltaT);
    	}  
  
        repaint();
    }
    
    public void render(Graphics g, Particle p)
    {
    	super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;
		g2.setPaint(new Color(252,18,235));
		Ellipse2D.Double blob = new Ellipse2D.Double(p.getx(), p.gety(), p.getRadius()*2,p.getRadius()*2);
		g2.fill(blob);
		g2.draw(blob);
    }

    public void paintComponent(Graphics g) 
    {
        
        g.clearRect(0, 0,700, 700);
		
		for (Particle p : particles)
        {
            render(g,p);
        }
    }
}

Your add() call here

particlePanel.add(new Particle(5,3E-26,rand));

is adding a Particle component to the panel itself. It actually needs to add that Particle to the "particles" list which the panel uses to manage the particles. Add a method

public void addParticle(Particle p )

to the ParticlePanel class, which adds that particle to the list.

edit: Also, Particle doesn't really need to extend JComponent. It doesn't depend on any of that functionality and it's just adding baggage to your Particle class.

This article has been dead for over six months. Start a new discussion instead.