I'm creating a graphical front-end for a JBox2D simulation. The simulation runs incrementally, and in between the updates, the contents of the simulation are supposed to be drawn. Similar to a game except without input.
I only need geometric primitives to draw a JBox2D simulation. This API seemed like the simplest choice, but its design is a bit confusing.
Currently I have one class called Window
extending JFrame
, that contains as a member another class called Renderer
. The Window
class only initializes itself and provides an updateDisplay()
method (that is called by the main loop), that calls updateDisplay(objects)
method on the Renderer
. I made these two methods myself and their only purpose is to call repaint()
on the Renderer
.
Is the JPanel
supposed to be used that way? Or am I supposed to use some more sophisticated method for animation (such that involves events and/or time intervals in some back-end thread)?
For a tightly coupled simulation,
javax.swing.Timer
is a good choice. Let the timer's listener invoke your implementation ofpaintComponent()
, as shown here and in the example cited here.For a loosely coupled simulation, let the model evolve in the background thread of a
SwingWorker
, as shown here. Invokepublish()
when apropos to you simulation.The choice is dictated in part by the nature of the simulation and the duty cycle of the model.
If you are wanting to schedule the updates at a set interval,
javax.swing.Timer
provides a Swing-integrated service for it.Timer
runs its task on the EDT periodically, without having an explicit loop. (An explicit loop would block the EDT from processing events, which would freeze the UI. I explained this more in-depth here.)Ultimately doing any kind of painting in Swing you'll still be doing two things:
paintComponent
to do your drawing.repaint
as-needed to request that your drawing be made visible. (Swing normally only repaints when it's needed, for example when some other program's window passes over top of a Swing component.)If you're doing those two things you're probably doing it right. Swing doesn't really have a high-level API for animation. It's designed primarily with drawing GUI components in mind. It can certainly do some good stuff, but you will have to write a component mostly from scratch, like you're doing.
Painting in AWT and Swing covers some of the 'behind the scenes' stuff if you do not have it bookmarked.
You might look in to JavaFX. I don't know that much about it personally, but it's supposed to be more geared towards animation.
As somewhat of an optimization, one thing that can be done is to paint on a separate image and then paint the image on to the panel in
paintComponent
. This is especially useful if the painting is long: repaints can be scheduled by the system so this keeps when it happens more under control.If you aren't drawing to an image, then you'd need to build a model with objects, and paint all of them every time inside
paintComponent
.Here's an example of drawing to an image:
If the routine is long-running and repaints could happen concurrently, double buffering can also be used. Drawing is done to an image which is separate from the one being shown. Then, when the drawing routine is done, the image references are swapped so the update is seamless.
You should typically use double buffering for a game, for example. Double buffering prevents the image from being shown in a partial state. This could happen if, for example, you were using a background thread for the game loop (instead of a
Timer
) and a repaint happened the game was doing the painting. Without double buffering, this kind of situation would result in flickering or tearing.Swing components are double buffered by default, so if all of your drawing is happening on the EDT you don't need to write double buffering logic yourself. Swing already does it.
Here is a somewhat more complicated example which shows a long-running task and a buffer swap:
The painting routine is just intended draw garbage which takes a long time:
Why not just use stuff from the testbed? It already does everything. Just take the JPanel, controller, and debug draw. It uses Java 2D drawing.
See here for the JPanel that does the buffered rendering: https://github.com/dmurph/jbox2d/blob/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework/j2d/TestPanelJ2D.java
and here for the debug draw: https://github.com/dmurph/jbox2d/blob/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework/j2d/DebugDrawJ2D.java
See the TestbedMain.java file to see how the normal testbed is launched, and rip out what you don't need :)
Edits: Disclaimer: I maintain jbox2d
Here is the package for the testbed framework: https://github.com/dmurph/jbox2d/tree/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework
TestbedMain.java is in the j2d folder, here: https://github.com/dmurph/jbox2d/tree/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework/j2d