I have to draw a Circular gauge using Java Graphics. Somewhat similar to the attached image, the circular gauge without the black border as in image.
I tried to use two methodologies but none of them is working perfectly
Firstly, I tried to draw everything using Java Graphics and then rotate the image as per the required input
Following is the code for that

public void paint(Graphics g)
    {
        try {
            centerX = this.getWidth() / 2;
            centerY = this.getHeight() / 2;
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setPaint(new Color(74,74,86));
            g2d.fillRect(0, 0, centerX * 2, centerY * 2);
            AffineTransform affTransform = g2d.getTransform();
            AffineTransform transform = (AffineTransform) (affTransform.clone());
            g2d.transform(transform.getRotateInstance(-this.getHeadingValue() / 57.29, centerX, centerY));
            g2d.setStroke(new BasicStroke(2));
            g2d.setPaint(Color.WHITE);
            for(int i=0;i<36;i++)
            {
            AffineTransform affineTransform = g2d.getTransform();
            AffineTransform newTransform = (AffineTransform)(affineTransform.clone());
            g2d.transform(newTransform.getRotateInstance((10*i)/57.29,centerX,centerY));
            g2d.drawLine(centerX,3,centerX,18);
            if(i%9==0)
            {
            g2d.setStroke(new BasicStroke(1));
            g2d.drawLine(centerX,60,centerX,centerY);
            g2d.setStroke(new BasicStroke(2));
            g2d.setFont(new Font("Sanserif",Font.PLAIN,20));
            if(i==0) g2d.drawString("N",this.getWidth()/2-5,40);
            if(i==9) g2d.drawString("E",this.getWidth()/2-5,40);
            if(i==18) g2d.drawString("S",this.getWidth()/2-7,40);
            if(i==27) g2d.drawString("W",this.getWidth()/2-8,40);
            }
            else if(i%3==0)
            {
            g2d.setFont(new Font("Sanserif",Font.PLAIN,16));
            if(i>=10)
            g2d.drawString(Integer.toString(i),this.getWidth()/2-8,40);
            else
            g2d.drawString(Integer.toString(i),this.getWidth()/2-5,40);
            }
            g2d.setTransform(affineTransform);
            }
            g2d.setTransform(affTransform);
            g2d.drawImage(img, centerX - 25, centerY - 25, null);
        } catch (Exception ex) {
            Logger.getLogger(HeadingCircular.class.getName()).log(Level.SEVERE, null, ex);
        }

        
    }

Here

Image img

is a image object which holds the image of plane to be drawn in the center.
The problem with this methodology is that I am getting shaky mater. The digits and ticks seems to be shaking as the gauge rotates . I need the gauge to rotate smoothly

In Second methodology I tried using an Image instead of drawing the ticks and circular scale and rotating the image as per the input. Following is the code for that methodology

public void paint(Graphics g)
    {
        try {
            centerX = this.getWidth() / 2;
            centerY = this.getHeight() / 2;
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setPaint(new Color(74,74,86));
            g2d.fillRect(0, 0, centerX * 2, centerY * 2);
            AffineTransform affTransform = g2d.getTransform();
            AffineTransform transform = (AffineTransform) (affTransform.clone());
            g2d.transform(transform.getRotateInstance(-this.getHeadingValue() / 57.29, centerX, centerY));
            
            Image image = ImageIO.read(getClass().getResource("/images/heading_scale.PNG"));
            g2d.drawImage(image,0,0, null);
            g2d.setTransform(affTransform);
            g2d.drawImage(img, centerX - 25, centerY - 25, null);
        } catch (Exception ex) {
            Logger.getLogger(HeadingCircular.class.getName()).log(Level.SEVERE, null, ex);
        }

        
    }

Now the ticks and labels are not shaking,But the problem in using the image is that now the gauge is Jerky. Instead of running smoothly itn run in jerky motion.

I don't know how should I code so that the gauge runs smoothly without any shakes or jerks. Please guide me through it

Recommended Answers

All 7 Replies

Move as much code as possible out of paint(...) - eg read the image file or create the new Font just once at startup, not on every call to paint(...).
How and how often are you updating? You should be using a javax.swing.Timer to call update the heading then repaint() pretty frequently, eg 50 mSec

that so hard to speak about that, if you really needed some hepl, then you have to send runnable example,

shots to the dark, common mistakes

paintCOmponent instead of paint

setOpaque(false)

I would agree with the above suggestions to get the ImageIO.read() out of the paint() method and override paintComponent() instead of paint() unless this is an AWT Applet.

I'd also like to mention that you don't necessarily need to create another rotated instance of AffineTransform here

AffineTransform transform = (AffineTransform) (affTransform.clone());
            g2d.transform(transform.getRotateInstance(-this.getHeadingValue() / 57.29, centerX, centerY));

You should be able to simply call

g2d.rotate(-this.getHeadingValue() / 57.29, centerX, centerY)

and draw the image. Since you're restoring the original transform anyway, you can operate directly with the current transform of the graphics context.

Move as much code as possible out of paint(...) - eg read the image file or create the new Font just once at startup, not on every call to paint(...).
How and how often are you updating? You should be using a javax.swing.Timer to call update the heading then repaint() pretty frequently, eg 50 mSec

I am updating at a higher rate 25msec. I am using TimerTask instead of javax.swing.Timer to schedule my task

that so hard to speak about that, if you really needed some hepl, then you have to send runnable example,

shots to the dark, common mistakes

paintCOmponent instead of paint

setOpaque(false)

I have tried using the paintComponent and set as dark example. I am attaching three codes with this. Main,java draws the current component. UDPClient sends the data to Main.java through UDP protocol. I have to finally update my data using UDP protocl

I would agree with the above suggestions to get the ImageIO.read() out of the paint() method and override paintComponent() instead of paint() unless this is an AWT Applet.

I'd also like to mention that you don't necessarily need to create another rotated instance of AffineTransform here

AffineTransform transform = (AffineTransform) (affTransform.clone());
            g2d.transform(transform.getRotateInstance(-this.getHeadingValue() / 57.29, centerX, centerY));

You should be able to simply call

g2d.rotate(-this.getHeadingValue() / 57.29, centerX, centerY)

and draw the image. Since you're restoring the original transform anyway, you can operate directly with the current transform of the graphics context.

I have tried using

AffineTransform transform = (AffineTransform) (affTransform.clone());
            g2d.transform(transform.getRotateInstance(-this.getHeadingValue() / 57.29, centerX, centerY));

but still no change

I have also attached a GIF which shows my current output. You can clearly see the text strings are shaking. What should I do to make them smooth rotating

Main,java draws the current component. UDPClient sends the data to Main.java through UDP protocol. I have to finally update my data using UDP protocl

Does the program have to perform the UDP data transfer for every paint? If so, is this what is limiting your re-draw rate?
What Thread is the UDP activity happening on - is it called directly or indirectly from paint or paintComponent? If so, you may be blocking Swing's event dispatch thread which will cause jerky screen refreshes.

I never seen good graphics outPut, sure excepts Assemblers hardCoded, but

if you are using java.util.Timer, then all your threads would be better
- invoke single thread from Executor and with implements Runnable too,
- outPut from any backGround threads to GUI must be packed into invokeLater

Does the program have to perform the UDP data transfer for every paint? If so, is this what is limiting your re-draw rate?
What Thread is the UDP activity happening on - is it called directly or indirectly from paint or paintComponent? If so, you may be blocking Swing's event dispatch thread which will cause jerky screen refreshes.

Yes the program does need to perform UDP data transfer for every paint.
I have a TimerTask which is called to first listen data by UDP and then paintComponent is called from the same object
As I have mentioned earlier I have implemented the code using two ways, firstly by drawing the Ticks and Text by Java Graphics. In this method I am getting shaky texts as I have shown in the above attached image. The texts are shaking their as they rotate
For solving the shaking problem I tried to use a second methodology in which I was drawing image, which consists of Tick and text labels. Since it was a image the text labels were not shaking but in that case I was getting jerky motion but now after using your suggestion of taking ImageIO.read the in second method I am getting a smooth motion
Thanks for your help

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.