I'm trying to make an orbit simulator, and I've run into this problem. I've checked all over Stack Overflow, but I can't find the solution. I'm just trying to draw manually to a JPanel
, but it's not showing up on it. I've set the layout to null, made it visible, added it to the JFrame
, added the Body to the Plane
, and everything you would normally do.
Here's the Body class:
package viperlordx.orbitsimulator;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JLabel;
@SuppressWarnings("serial")
public class Body extends JLabel {
private double mass;
private Point location;
private Vector velocity;
private Color color;
private Plane plane;
public Body(double mass, Point location, Vector velocity, Color color) {
this.mass = mass;
this.location = location;
this.velocity = velocity;
this.color = color;
this.setVisible(true);
this.setBounds(location.x, location.y, 100, 100);
}
public void moveTick() {
velocity.addTo(location);
}
public void setPlane(Plane plane) {
this.plane = plane;
}
public Plane getPlane() {
return plane;
}
@Override
public void paintComponent(Graphics g) {
System.out.println("Painting");
super.paintComponent(g);
if (plane != null && g != null) {
g.setColor(color);
g.fillOval(location.x, location.y, getWidth(), getHeight());
}
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
public Vector getVelocity() {
return velocity;
}
public void setVelocity(Vector vector) {
velocity = vector;
}
}
And the Plane class:
package viperlordx.orbitsimulator;
import java.awt.Graphics;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class Plane extends JPanel {
private HashSet<Body> bodies = new HashSet<Body>();
public void addBody(Body body) {
this.add(body);
bodies.add(body);
}
public Plane() {
this.setLayout(null);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bodies != null) {
for (Body body : bodies) {
body.paintComponent(g);
}
}
}
public Set<Body> getBodies() {
return bodies;
}
}
Now the main class:
package viperlordx.orbitsimulator;
import java.awt.Color;
import java.awt.Point;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1000, 1000);
frame.setLocationRelativeTo(null);
Plane plane = new Plane();
frame.add(plane);
plane.setVisible(true);
frame.setLayout(null);
plane.setBounds(0, 0, 1000, 1000);
plane.setBackground(Color.WHITE);
plane.addBody(new Body(10.0, new Point(10, 10), new Vector(0, 0), Color.GREEN));
frame.setVisible(true);
frame.setTitle("Orbit");
plane.repaint();
}
}
You are mixing up drawing shapes on a component and adding components to a container. Your Body
should not be a component, simply a class that holds the data of the object to be drawn. This data is used to draw a corresponding shape on the "target" component - the Plane
in your case.
I removed irrelevant code and changed it according to my explanation above:
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
Plane plane = new Plane();
frame.add(plane);
plane.setBackground(Color.WHITE);
plane.addBody(new Body(10.0, new Point(10, 10), new Vector(0, 0), Color.GREEN));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Orbit");
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
@SuppressWarnings("serial")
class Plane extends JPanel {
private HashSet<Body> bodies = new HashSet<>();
public void addBody(Body body) {
bodies.add(body);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Body body : bodies) {
// Might want to call all needed accessors before getting to work.
g.setColor(body.getColor());
g.fillOval(body.getLocation().x, body.getLocation().y, getWidth(), getHeight());
}
}
@Override
public Dimension getPreferredSize() {
// Calculate the size
return new Dimension(1000, 500);
}
}
class Body {
private Point location;
private Vector velocity;
private Color color;
public Body(double mass, Point location, Vector velocity, Color color) {
this.location = location;
this.velocity = velocity;
this.color = color;
}
public Point getLocation() {
return location;
}
public Color getColor() {
return color;
}
}
Notes:
@Override getPreferredSize()
appropriately. This means you will need to perform a calculation based on the things you draw on it.setVisible(true)
should be the last thing you do. No need to repaint after it.HashSet<Body> bodies = new HashSet<>()
without specifying the generic type in the RHS.Vector
. In fact, no reason to use this class at all. A Set
or List
will usually do better. If they hold numbers used in calculation then their generic type should be Float
or Double
probably.