Einfache Partikelsimulation

Eine einfache Partikelsimulation.

Achtung: Das ist wirklich eine SEHR einfache Implementierung! Es wurde nicht viel Wert auf Effizienz gelegt, sondern darauf, leicht nachvollziehbar zu machen, wie der Ablauf der Simulation ist. (Ja, dafür würden Kommentare sicher helfen :o vielleicht füge ich die bei Gelegenheit noch ein)

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
 
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
 
public class ParticleSimulation
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
   
    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(600,600);
       
        final ParticleSystem particleSystem = new ParticleSystem();
        final ParticleSystemPanel particleSystemPanel = new ParticleSystemPanel(particleSystem);
       
        Thread thread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                long previousNS = System.nanoTime();
               
                while (true)
                {
                    long currentNS = System.nanoTime();
                    double seconds = (currentNS - previousNS) / 1e9;
                    previousNS = currentNS;
                    particleSystem.doStep(seconds);
                    particleSystemPanel.repaint();
                    try
                    {
                        Thread.sleep(10);
                    }
                    catch (InterruptedException e)
                    {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
       
       
        f.getContentPane().add(particleSystemPanel);
        f.setVisible(true);
    }
}
 
class ParticleSystemPanel extends JPanel
{
    private final ParticleSystem particleSystem;
   
    public ParticleSystemPanel(ParticleSystem particleSystem)
    {
        this.particleSystem = particleSystem;
    }
   
    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.setColor(Color.RED);
        for (Particle particle : particleSystem.getParticles())
        {
            Point2D position = particle.getPosition();
            int x = (int)(position.getX()*getWidth());
            int y = getHeight() - (int)(position.getY()*getHeight());
            g.fillOval(x-5, y-5, 10, 10);
        }
    }
}
 
 
interface Force
{
    Point2D computeAcceleration(Particle particle);
}
 
class Gravity implements Force
{
    @Override
    public Point2D computeAcceleration(Particle particle)
    {
        return new Point2D.Double(0, -9.81*0.1);
    }
}
 
class ParticleSystem
{
    private static final Random random = new Random(0);
    private final List<Particle> particles;
    private final List<Force> forces;
    private double totalTime = 0;
   
    public ParticleSystem()
    {
        particles = new CopyOnWriteArrayList<Particle>();
        forces = new ArrayList<Force>();
       
        forces.add(new Gravity());
    }
   
    public List<Particle> getParticles()
    {
        return Collections.unmodifiableList(particles);
    }
   
    public void doStep(double t)
    {
        removeOld();
        emitNew();
        updateAccelerations();
        updateVelocities(t);
        updatePositions(t);
        totalTime += t;
        //System.out.println("After "+totalTime+": "+particles);
    }
 
    private void removeOld()
    {
        List<Particle> toRemove = new ArrayList<Particle>();
        for (Particle particle : particles)
        {
            Point2D position = particle.getPosition();
            if (position.getY() < 0)
            {
                toRemove.add(particle);
            }
        }
        particles.removeAll(toRemove);
    }
 
    private void emitNew()
    {
        List<Particle> toAdd = new ArrayList<Particle>();
        for (int i=0; i<2; i++)
        {
            Particle particle = new Particle();
            particle.setPosition(new Point2D.Double(0.5,0));
            double vx = -0.5+random.nextDouble();
            double vy = 1.5*random.nextDouble();
            particle.setVelocity(new Point2D.Double(vx,vy));
            toAdd.add(particle);
        }
        particles.addAll(toAdd);
    }
 
    private void updateAccelerations()
    {
        for (Particle particle : particles)
        {
            Point2D totalAcceleration = new Point2D.Double();
            for (Force force : forces)
            {
                Point2D acceleration = force.computeAcceleration(particle);
                addAssign(totalAcceleration, acceleration);
            }
            particle.setAcceleration(totalAcceleration);
            //System.out.println("acc "+particle.getAcceleration());
        }
    }
   
    private void updateVelocities(double t)
    {
        for (Particle particle : particles)
        {
            Point2D velocity = particle.getVelocity();
            Point2D acceleration = particle.getAcceleration();
            scaleAddAssign(velocity, t, acceleration);
            particle.setVelocity(velocity);
            //System.out.println("vel "+particle.getVelocity());
        }
    }
   
    private void updatePositions(double t)
    {
        for (Particle particle : particles)
        {
            Point2D position = particle.getPosition();
            Point2D velocity = particle.getVelocity();
            scaleAddAssign(position, t, velocity);
            particle.setPosition(position);
            //System.out.println("pos "+particle.getPosition());
        }
    }
   
    private static void addAssign(Point2D result, Point2D addend)
    {
        double x = result.getX() + addend.getX();
        double y = result.getY() + addend.getY();
        result.setLocation(x, y);
    }
   
    private static void scaleAddAssign(Point2D result, double factor, Point2D addend)
    {
        double x = result.getX() + factor * addend.getX();
        double y = result.getY() + factor * addend.getY();
        result.setLocation(x, y);
    }
   
}
 
 
class Particle
{
    private final Point2D position;
    private final Point2D velocity;
    private final Point2D acceleration;
    private final double mass;
   
    public Particle()
    {
        position = new Point2D.Double();
        velocity = new Point2D.Double();
        acceleration = new Point2D.Double();
        mass = 1;
    }
 
    public Point2D getPosition()
    {
        return new Point2D.Double(position.getX(), position.getY());
    }
    public void setPosition(Point2D point)
    {
        position.setLocation(point);
    }
   
    public Point2D getVelocity()
    {
        return new Point2D.Double(velocity.getX(), velocity.getY());
    }
    public void setVelocity(Point2D point)
    {
        velocity.setLocation(point);
    }
 
    public Point2D getAcceleration()
    {
        return new Point2D.Double(acceleration.getX(), acceleration.getY());
    }
    public void setAcceleration(Point2D point)
    {
        acceleration.setLocation(point);
    }
   
    public double getMass()
    {
        return mass;
    }
   
    @Override
    public String toString()
    {
        return String.format("Particle[p=(%.2f,%.2f),v=(%.2f,%.2f),a=(%.2f,%.2f)]",
            position.getX(), position.getY(),
            velocity.getX(), velocity.getY(),
            acceleration.getX(), acceleration.getY());
    }
   
}

Kleiner Bug in Zeile 282. Das erste muss getX sein :wink: