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

Edited 5 Years Ago by gunjannigam: n/a

Attachments heading_indicator[1].gif 32.1 KB

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.

Edited 5 Years Ago by Ezzaral: n/a

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

Edited 5 Years Ago by gunjannigam: n/a

Attachments 1.gif 754.52 KB
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Gunjan Nigam
 */
public class HeadingCircular extends JPanel{

    
    private float headingValue;
    int centerX,centerY;
    Image img;
    JLabel label;
    Font font16,font20;
    BasicStroke stroke1,stroke2;
    public HeadingCircular(int size,String imagename)
    {
        super(null);
        try {
            this.setSize(size, size);
            img = ImageIO.read(getClass().getResource(imagename));
            centerX = size / 2;
            centerY = size / 2;
            setOpaque(false);
            font16 = new Font("Sanserif",Font.PLAIN,16);
            font20 = new Font("Sanserif",Font.PLAIN,20);
            stroke1 = new BasicStroke(1);
            stroke2 = new BasicStroke(2);
           
        } catch (IOException ex) {
            Logger.getLogger(HeadingCircular.class.getName()).log(Level.SEVERE, null, ex);
        }
      
    }
    public HeadingCircular(String imagename)
    {
       this(300,imagename);
    }
    @Override
    public void paintComponent(Graphics g)
    {
        
        try {
            centerX = this.getWidth() / 2;
            centerY = this.getHeight() / 2;
            Graphics2D g2d = (Graphics2D) g;
           
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.setPaint(new Color(74,74,86));
            g2d.fillRect(0, 0, centerX * 2, centerY * 2);
           
            g2d.setStroke(stroke2);
            g2d.setPaint(Color.WHITE);
           
            AffineTransform affTransform = g2d.getTransform();
            g2d.transform(affTransform.getRotateInstance(-this.getHeadingValue() / 57.29, centerX, centerY));
         
            for(int i=0;i<36;i++)
            {
                AffineTransform affineTransform = g2d.getTransform();

                g2d.transform(affineTransform.getRotateInstance((10*i)/57.29,centerX,centerY));
                g2d.drawLine(centerX,3,centerX,18);
                if(i%9==0)
                {
                    g2d.setStroke(stroke1);
                    g2d.drawLine(centerX,60,centerX,centerY);
                    g2d.setStroke(stroke2);
                    g2d.setFont(font20);
                    if(i==0) g2d.drawString("N",centerX-5,40);
                    if(i==9) g2d.drawString("E",centerX-5,40);
                    if(i==18) g2d.drawString("S",centerX-7,40);
                    if(i==27) g2d.drawString("W",centerX-8,40);
                }
                else if(i%3==0)
                {
                g2d.setFont(font16);
                if(i>=10)
                    g2d.drawString(Integer.toString(i),centerX-8,40);
                else
                    g2d.drawString(Integer.toString(i),centerX-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);
        }

        
    }

    /**
     * @return the headingValue
     */
    public float getHeadingValue() {
        return headingValue;
    }

    /**
     * @param headingValue the headingValue to set
     */
    public void setHeadingValue(float headingValue) {
        this.headingValue = headingValue;
    }


            

}
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Gunjan Nigam
 */
public class Main extends JPanel{
    DatagramSocket dsocket;
    float[] pack_rec = new float[25];
	byte[] buffer = new byte[256];
	byte[] read_float = new byte[4];
    HeadingCircular hc;
    public Main()
    {
        super(null);

        JFrame frame = new JFrame();
        frame.setSize(1024,768);
        JMenuBar menuBar = new JMenuBar();

        JMenu menu = new JMenu("check");
        menuBar.add(menu);
        frame.setJMenuBar(menuBar);
        frame.getContentPane().add(this);

        hc= new HeadingCircular("/images/plane.png");
        this.add(hc);
        hc.setBounds(0,300,300,300);     
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.setVisible(true);
        try {
            dsocket = new DatagramSocket(49001);
            dsocket.setReceiveBufferSize(1);
        } catch (SocketException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        Timer time = new Timer();
        time.scheduleAtFixedRate(new dataReceive(), 0, 25);
    }
    public static void main(String[] args)
    {
        new Main();
    }
    public class dataReceive extends TimerTask
    {
        long time = System.currentTimeMillis();
        public void run() {
            long diff= System.currentTimeMillis()-time;
            time = System.currentTimeMillis();
           UDP();
           hc.repaint();
           
          System.out.println(diff);
          
        }

    }
    public void UDP() 
	{
		try
		{
			DatagramPacket	packet = new DatagramPacket(buffer, buffer.length);
            dsocket.setSoTimeout(1000*60);
            dsocket.receive(packet);
           
            DataInputStream data_stream = new DataInputStream(new ByteArrayInputStream(packet.getData()));
            for(int i=0;i<25;i++)
            {
                read_float[3]=data_stream.readByte();
                read_float[2]=data_stream.readByte();
                read_float[1]=data_stream.readByte();
                read_float[0]=data_stream.readByte();
                DataInputStream data_stream1 = new DataInputStream(new ByteArrayInputStream(read_float));
                pack_rec[i]=data_stream1.readFloat();
            }
         
		hc.setHeadingValue(pack_rec[2]*57.32f);
       

		}catch(SocketTimeoutException e){JOptionPane.showMessageDialog(null, "Sorry Communication Failed and No Data Received");}
        catch (SocketException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
	}

   

}
plane.PNG 1.64 KB
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

class UDPClient
{
   public UDPClient()
   {
       java.util.Timer time= new java.util.Timer();
       time.scheduleAtFixedRate(new DataGenerator(), 0, 25);
   }
   public static void main(String args[]) throws Exception
   {
       new  UDPClient();
   }
   class DataGenerator extends TimerTask
    {
        long time;
        DatagramSocket clientSocket;
        InetAddress IPAddress;
        int port;
        float[] data = new float[25];
        byte[] sendData;
        public DataGenerator()
        {
            try {
                time = System.currentTimeMillis();
                clientSocket = new DatagramSocket();
                byte[] serverAddress = {(byte) 192, (byte) 168, (byte) 1, (byte) 48};
                IPAddress = InetAddress.getByAddress(serverAddress);
                port = 49001;
                data[0]=-180;
                data[1]=-30/57.32f;
                
                data[3]=-300;
                data[4]=-300;
                data[5]=-300;
                sendData= new byte[100];
            } catch (UnknownHostException ex) {
                Logger.getLogger(UDPClient.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SocketException ex) {
                Logger.getLogger(UDPClient.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

        public void run() {
            try {
                //System.out.println(data[1]+"\t"+data[2]);
                long diff = System.currentTimeMillis() - time;
                time = System.currentTimeMillis();
                data[0] = data[0]+0.05f;
                data[1] = data[1]+(0.025f/57.32f);
                data[2] = (data[2] + (0.1f/57.32f)) % (360/57.32f);
                data[3] += 0.2f;
                data[4] += 0.1f;
                data[5] += 0.1f;
                data[6] += 0.02f;
                data[7] += 0.06f;
                data[8] += 0.04f;
                data[9] = (data[9] + 1.2f) % 1000;
                data[10] = (data[10] + 0.7f) % 150;
                for (int i = 11; i < 25; i++) {
                    data[i] = (float) Math.random();
                }
                if (data[0] > 180) {
                    data[0] = -180;
                }
                if(data[1]>(30/57.32f))
                    data[1] = -(30/57.32f);
                if (data[3] > 300) {
                    data[3] = -300;
                }
                if (data[4] > 300) {
                    data[4] = -300;
                }
                if (data[5] > 300) {
                    data[5] = -300;
                }
                if (data[6] > 1.7) {
                    data[6] = -1.7f;
                }
                if (data[7] > 1.7) {
                    data[7] = -1.7f;
                }
                if (data[8] > 1.7f) {
                    data[8] = -1.7f;
                }
                
                for (int i = 0; i < 25; i++) {
                    byte[] byteData = toByta(data[i]);
                    sendData[4 * i + 0] = byteData[3];
                    sendData[4 * i + 1] = byteData[2];
                    sendData[4 * i + 2] = byteData[1];
                    sendData[4 * i + 3] = byteData[0];
                }
                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port);
                clientSocket.send(sendPacket);
                //System.out.println(diff);
            } catch (IOException ex) {
                Logger.getLogger(UDPClient.class.getName()).log(Level.SEVERE, null, ex);
            }
           
        }
        public byte[] toByta(int data) {
    return new byte[] {
        (byte)((data >> 24) & 0xff),
        (byte)((data >> 16) & 0xff),
        (byte)((data >> 8) & 0xff),
        (byte)((data >> 0) & 0xff),
    };
}
        public byte[] toByta(float data) {
    return toByta(Float.floatToRawIntBits(data));
}

    }
}

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.

Edited 5 Years Ago by JamesCherrill: n/a

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

Attachments 2.gif 576.5 KB
This question has already been answered. Start a new discussion instead.