Search code examples
javaswingjpanelpaintgraphics2d

How do I increase the frequency JPanel is redrawn?


I have a simple program that draws the trajectory of a particle launched from the origin at a certain speed and angle. I created a subclass of JPanel to handle the drawing of this. My everytime my subclass is redrawn it takes the difference between the current time and the initial time(both in milliseconds), converts this to seconds, then finds the x and y coordinate of where the particle should be at that point in time, and finally takes those x and y coordinates and draws them on the screen. My problem is that my subclass seems to be redrawn at interval that seem long because there are only a few dots that are shown.

My drawing method:

private void doDrawing(Graphics g) {

    Dimension size = getSize();
    Insets insets = getInsets();
    int w = size.width - insets.left - insets.right;
    int h = size.height - insets.top - insets.bottom;

    Graphics2D g2d = (Graphics2D) g;
    g.drawString("Acceleration: -9.8m/s i", 0, 20);
    StringBuilder b = new StringBuilder();
    b.append("Current Velocity: ");
    b.append(String.valueOf(sim.getVector(tickSpeed
            * ((System.currentTimeMillis() - initTime) / 1000)).getMagnitude()));
    b.append(" m/s at ");
    b.append(String.valueOf(sim.getVector(tickSpeed
            * ((System.currentTimeMillis() - initTime) / 1000)).getDirection().getDirectionDeg()));
    b.append(" degrees");
    g.drawString(b.toString(), 0, 30);

    drawPreviousPoints(g2d);

    drawCurrentPointAndAppend(g2d, w, h);

    repaint();

}

private void drawCurrentPointAndAppend(Graphics2D g2d, int w, int h) {
    g2d.setColor(Color.red);
    double height = (length / w) * h;
    Vector2D c = sim.getVector(tickSpeed
            * ((System.currentTimeMillis() - initTime) / 1000));
    double currentX = w
            * ((sim.getX(tickSpeed
                    * ((System.currentTimeMillis() - initTime) / 1000))) / length);
    double currentY = h
            * (1 - ((sim.getY(tickSpeed
                    * ((System.currentTimeMillis() - initTime) / 1000))) / height));

    g2d.drawLine((int) currentX, (int) currentY, (int) currentX,
            (int) currentY);
    g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
            BasicStroke.JOIN_MITER));
    g2d.drawLine((int) currentX, (int) (currentY),
            (int) (currentX + w * (c.getX() / length)),
            (int) (currentY + (h *  -(c.getY() / height))));
    xList.add(currentX);
    yList.add(currentY);

}

private void drawPreviousPoints(Graphics2D g2d) {
    g2d.setColor(Color.blue);
    g2d.setStroke(new BasicStroke(7, BasicStroke.CAP_ROUND,
            BasicStroke.JOIN_ROUND));
    if (!xList.isEmpty()) {
        for (int i = 0; i < xList.size(); i++) {
            g2d.drawLine(xList.get(i).intValue(), yList.get(i).intValue(),
                    xList.get(i).intValue(), yList.get(i).intValue());
        }
    }

}

tickSpeed is just a variable that I use to speed up or slow down the particle. It runs fine; however, the animation seems very choppy.

  • How do I fix this choppiness(make everything seem more "fluid")
  • Where should I call repaint()? Because I feel like calling it at the end of my drawing method isn't right.

Solution

  • An important rule of Swing- You don't control the paint process...

    Don't perform these calculations within the paintComponent. The paintComponent is meant to paint the current state of the UI and may be called at any time for many reasons, most of which are outside your control.

    Instead, consider using a javax.swing.Timer set to repeat at a regular interval (40ms is 25 ticks a second).

    Set up a model which keeps track of the particles current been processed. When the timer ticks, calculate your particle positions and update them, then call repaint.

    Within your paintComponent, simply paint the current state of your model.

    Have a look at Concurrency in Swing and How to use Swing Timers for more details