I have done a program that numerically solves a set of differential equations which describes how an "arbitrary" illness move in an isolated and constant population, it was a programming assignment from a class I took a while ago. What I've done to extend it is to add some graphical components that can pause, reset and "play" the simulation, as well as some components that allows me to change some constants in the equations.
All this was an exercise in programming as I find it to be fun and exciting and want to become better.
However, at the moment I'm stuck, what I want to do now is to make a very simple form of animation of it. I want to visualize the data I get for the number of infected, susceptibles and resistants in a grid as points. I managed to create the grid and have an idea of how to place the dots.
The problem I have is how to draw the dots as the program is working, I can draw one dot in the grid but only as the grid is created, that's it. I need to be able to create a dot at a specific place in the grid, this goes on until the number of dots reaches a finite number, say 30. At that points I want to have the first dot, the one the left, removed, all the dots shifted to the left and place the new dot at the furthest right of the grid, the whole thing is then repeated.
I think I will be able to figure it out with some help/hints about the paintComponent()
method and whether I need to use repaint() method at all, I can't get my head around these for some reason. I've read through my course literature on Java, but despite the extensive sections where he explains most of the different graphical components he does not say that much about those methods, only that you don't call for the paintComponent()
method, it is done automatically.
If there is something unclear let me know and I'll try to clarify it.
Thanks in advance.
// Fox Mulder
I think I will be able to figure it out with some help/hints about the
paintComponent()
method and whether I need to userepaint()
method at all, I can't get my head around these for some reason.
Basically, say you create a custom component by extending JPanel
. When you @Override
the paintComponent()
method, it get implicitly called for you, so you never have to call it. So what ever you paint inside the method, gets drawn on your surface. For example
public class DrawingPanel extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
}
}
When you call repaint()
you are basically causing the paintComponent
method to be call implicitly. So to answer your question, Yes you will need to call it if you want to animate, as you will need to update some kind of variable (like the x and y) in the paintComponent() method, to see any change in the drawing.
You can see more at Performing Custom Painting
Not to handle the actual animation, you'll want to use a javax.swing.Timer
. You can see more at How to use Swing Timers. Here's the basic construct
Timer ( int delayInMillis, ActionListener listener )
where delayInMillis
is the time to delay between ticks(in this case animations) and the ActionListener
listens for "ticks". Each tick, the actionPerformed
of the ActionListener
is called. There, you can put the code to update any variables you use for animation.
So for example you update the x and y, in the actionPerformed
, then call repaint()
public class DrawingPanel extends JPanel {
int x = 50;
int y = 50;
public DrawingPanel() {
Timer timer = new Timer(40, new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
}
}
Now this was just a simple example. But in your case you want to animate a scatter plot. So what you can do is have a list of Point
s and in the actionPerformed
you can add pull points from that list and push them into another list that is to be drawn. So say you have this
List<Point> originalPoints;
List<Point> pointsToDraw;
...
@Override
protected void paintComponent(Grapchics g) {
super.paintComponent(g);
for (Point point : pointsToDraw) {
g.fillOval(point.x - 5, point.y - 5, 10, 10);
}
}
Basically all the points in pointsToDraw
list will be drawn. Initially it will be empty. And in the timer, you can add to the list, until the originalPoints
list is exhausted. For example.
List<Point> originalPoints;
List<point> pointsToDraw;
private int currentIndex = 0;
public DrawingPanel(List<Point> originalPoints) {
this.originalPoints = originalPoints;
pointsToDraw = new ArrayList<>();
Timer timer = new Timer(40, new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
if (currentIndex == originalPoints.size() - 1) {
((Timer)e.getSource()).stop();
} else {
pointsToDraw.add(originalPoints.get(currentIndex));
currentIndex++;
}
repaint();
}
});
timer.start();
}
So basicall you just keep a current index. When the index reaches the size of the original list, you stop the timer. Otherwise you just pop from the originalPoints
and push to the pointsToDraw
. For every point you add the pointsToDraw
, a repaint()
is called, and there will be another point for the paintComponent
to draw a circle with.
The END
UDPATE
I just reread your question, and I think I have have misunderstood it. If you want all the points drawn, then basically just have one list. And draw all the points initially. with each tick, just remove the first index, advance all the rest up an index, and add a new one to the end. Though this is the implementation of a LinkedList
so you may just want to use that