1

I have a program that takes information on two planets/bodies in space and animates their orbits around each other realistically. It works, though when I repaint, it clears the screen each time and does not leave a trail.

My problem is that I want to leave a trail, though any answer I can find online only explains how to get rid of a trail. However, I don't have that problem.

On other computers, I can leave out the super.paintComponent() in my paint method and that causes it to leave a trail, but on this computer, it doesn't do that, it seems to clear the screen automatically. So then how can I efficiently draw a trail behind my orbiting planets? My code follows.

JPanel class first:

import javax.swing.*;
import java.awt.*;
/**
 * Created by chris on 3/2/16.
 */
public class SpacePanel2 extends JPanel{
private Body2[] planets;
public static final Dimension SCREENSIZE = Toolkit.getDefaultToolkit().getScreenSize();
public static double scale = 5e6; //m/p
public static Color[] colors = {Color.black, Color.red};

public SpacePanel2(Body2[] planets) {
    this.planets = planets;
    this.setPreferredSize(SCREENSIZE);
}

@Override
public void paint(Graphics g){
    for (int i = 0; i < planets.length; i++) {
        g.setColor(colors[i]);
        int r = planets[i].getPixelRadius()/2;
        int x = planets[i].getPixelX();
        int y = planets[i].getPixelY();
        g.fillOval(x, y, r, r);
    }
}
}

Body class:

/**
  * Created by chris on 3/2/16.
 */
public class Body2 {
private double mass; //in kilograms
private double radius; //in meters
private static final double GRAVITATIONAL_CONSTANT = 6.67408e-11;
private static final double AVERAGE_DENSITY = 5515; //kg/m^3

/**
 * Movement variables
 */
private double dx; //in m/s
private double dy; //in m/s

private double x; //in m
private double y; //in m

public Body2() {
    radius = 1;
    mass = AVERAGE_DENSITY;
    x = 0;
    y = 0;
    dx = 0;
    dy = 0;
}

public double getX() {
    return x;
}

public double getY() {
    return y;
}

public double getMass() {
    return mass;
}

public double getRadius() {
    return radius;
}


public int getPixelX() {
    return (int)((this.x-radius)/SpacePanel2.scale);
}

public int getPixelY() {
    return (int)((this.y-radius)/SpacePanel2.scale);
}
public int getPixelRadius(){
    return (int)(this.radius/SpacePanel2.scale);
}

public void setMass(double mass) {
    this.mass = mass;
}

public void setRadius(double radius) {
    this.radius = radius;
}

public void setDx(double dx) {
    this.dx = dx;
}

public void setDy(double dy) {
    this.dy = dy;
}

public void setX(double x) {
    this.x = x;
}

public void setY(double y) {
    this.y = y;
}


public void exertForce2(double diffY, double diffX, double F){
    double dist = Math.sqrt(diffY*diffY + diffX*diffX);
    double ratio = F / dist;
    this.dy = this.dy + ratio*diffY/this.mass;
    this.dx = this.dx + ratio*diffX/this.mass;
}

public void tick(double timeScale) {
    x+=(dx/1000.0)*timeScale;
    y+=(dy/1000.0)*timeScale;
}

public static double getForce(Body2 a, Body2 b){
    double dX = a.getX() - b.getX();
    double dY = a.getY() - b.getY();
    double distance = Math.sqrt(Math.pow(dX,2)+Math.pow(dY,2));
    return (a.getMass()*b.getMass()*GRAVITATIONAL_CONSTANT)/(distance*distance);
}

public static double getStandardMass(double radius){
    return (4.0/3.0)*Math.pow(radius, 3) * Math.PI;
}

public double getDy() {
    return dy;
}

public double getDx() {
    return dx;
}

public static double predictCentripetalForce(Body2 sun, Body2 planet){
    return Math.sqrt(getForce(planet, sun)*(sun.getY()-planet.getY())/planet.mass);
}
}

Main class:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * Created by chris on 3/2/16.
 */
public class MainSpace2 {
static JFrame frame;
static SpacePanel2 panel;

static int fps = 60;
static boolean getLarger = false;
static boolean getSmaller = false;
static Dimension size = Toolkit.getDefaultToolkit().getScreenSize();

public static void main(String[] args) {

    Body2[] test = new Body2[2];

    Body2 sun = new Body2();
    sun.setRadius(696300000);
    sun.setMass(1.989e30);
    sun.setX(getScope('x') / 2);
    sun.setY(getScope('y') / 2);
    sun.setDx(0);
    sun.setDy(0);
    test[0] = sun;
    int literalSizeSun = (int)(sun.getRadius()/SpacePanel2.scale);

    Body2 mercury = new Body2();
    mercury.setRadius(24400000);
    mercury.setMass(Body2.getStandardMass(mercury.getRadius()));
    mercury.setDx(Body2.predictCentripetalForce(sun, mercury)*2);
    mercury.setDy(0);
    mercury.setX(sun.getX());
    mercury.setY(sun.getY() + 2 * sun.getRadius());
    test[1] = mercury;
    int literalSizeMercury = (int)(mercury.getRadius()/SpacePanel2.scale);


    frame = new JFrame();
    frame.setPreferredSize(size);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel = new SpacePanel2(test);

    frame.addKeyListener(new KeyListener() {
        @Override
        public void keyTyped(KeyEvent e) {

        }

        @Override
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyChar()) {
                case '-':
                    getSmaller = true;
                    getLarger = false;
                    break;
                case '=':
                    getLarger = true;
                    getSmaller = false;
                    break;
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
            switch (e.getKeyChar()) {
                case '-':
                    getSmaller = false;
                    break;
                case '=':
                    getLarger = false;
                    break;
            }
        }
    });

    double timeScale = 60*24;
    Timer time = new Timer((int) (1000.0 / fps), new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            double F = Body2.getForce(test[0], test[1]);
            double dY = test[1].getY() - test[0].getY();
            double dX = test[1].getX() - test[0].getX();
            test[0].exertForce2(dY, dX, F);
            test[1].exertForce2(-dY, -dX, F);
            for (int j = 0; j < test.length; j++) {
                test[j].tick(timeScale);
            }
            panel.repaint(sun.getPixelX(), sun.getPixelY(), literalSizeSun, literalSizeSun);
            panel.repaint(mercury.getPixelX(), mercury.getPixelY(), literalSizeMercury, literalSizeMercury);
        }
    });

    frame.add(panel);
    frame.pack();
    frame.setVisible(true);
    time.start();
}

public static double getScope(char k) {
    switch (k) {
        case 'x':
            return size.width * SpacePanel2.scale;
        case 'y':
            return size.height * SpacePanel2.scale;
        default:
            return 0;
    }
}
}
Chris
  • 566
  • 2
  • 7
  • 22

1 Answers1

2
  1. Custom painting is done by overriding the paintComponent() method, not paint().

  2. if you leave a continuous trail, once you do a 360 rotation you won't see any more animation, so I would think you need to clear the screen eventually.

If you want to leave a trail you can keep an ArrayList of Objects you want to paint. Then in the paintComponent() method you can iterate through the List. This will allow you to add/remove Object from the list so you can control the number of Objects you want to paint each animation.

Check out the DrawOnComponent example from Custom Painting Approaches for an example of this approach. So your animation logic would basically add a new Object (and potentially remove one once you reach a certain limit?) to the List. Then you just invoke repaint() on the panel and all the Objects will be painted.

You already have List to paint each planet. So each animation you would need to add the new location of each planet to the List.

Or, the link shows how you can paint to a BufferedImage. But this approach doesn't allow you to remove a painting once it is done. So it depends an your exact requirement which approach you use.

camickr
  • 321,443
  • 19
  • 166
  • 288