1

I am trying to draw a ring/partial ring using arc's.

But I need the middle of the partial ring/ring to be transparent and show whats behind it. Since I am using an arc and spinning it, is there a way to subtract away the inner portion of each arc and leave only the end at the desired thickness which will be spun to create the ring?

This is my code so far: I have the arcs working, but I am only simulating the ring by layering over each one with a couple circles, I need to actually subtract out the area of each circle from each arc.

Not sure how to do that to achieve transparency in the center. If there is a better way to do this please let me know, this is going to be a custom progress bar.

public class JCustomProgressBar extends JComponent{

private final Dimension SIZE = new Dimension( 50, 50 );

public JCustomProgressBar() {
    super();
    this.setVisible(true);
    System.out.println("CAlled");

}

int progress = 1;

public void updateProgress (int progress){
    this.progress = progress;
}


 @Override
 public void paintComponent (Graphics g){
   super.paintComponent(g);
   System.out.println("called");
   Graphics2D g2D = (Graphics2D) g.create();
   g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

   g2D.translate(this.getWidth()/2, this.getHeight()/2);
   g2D.rotate(Math.toRadians(270));
   Arc2D.Float arc = new Arc2D.Float (Arc2D.PIE);
   Ellipse2D circle = new Ellipse2D.Float(0, 0, 80, 80);
   arc.setFrameFromCenter (new Point(0,0), new Point (90, 90));
   circle.setFrameFromCenter(new Point(0,0), new Point (80, 80));
   arc.setAngleExtent(-progress*360/100);
   g2D.setColor(new Color(120,192,0));
   g2D.draw(arc);
   g2D.fill(arc);

   g2D.setColor(this.getParent().getBackground());
   g2D.draw(circle);
   g2D.fill(circle);

   arc.setFrameFromCenter (new Point(0,0), new Point (75, 75));
   arc.setAngleExtent(-90*360/100);
   g2D.setColor(new Color(197,228,146));
   g2D.draw(arc);
   g2D.fill(arc);

   circle.setFrameFromCenter(new Point(0,0), new Point (70, 70));
   g2D.setColor(this.getParent().getBackground());
   g2D.draw(circle);
   g2D.fill(circle);

   circle.setFrameFromCenter(new Point(0,0), new Point (60, 60));
   g2D.setColor(new Color(245, 245, 245));
   g2D.draw(circle);
   g2D.fill(circle);



   g2D.setColor(Color.black);
   g2D.rotate(Math.toRadians(90));
   g2D.setFont(new Font("Verdana", Font.PLAIN, 30));
   FontMetrics fm = g2D.getFontMetrics();
   Rectangle2D r2D = fm.getStringBounds(progress + "%", g);
   int x = (0 - (int) r2D.getWidth())/2;
   int y = (0 - (int) r2D.getHeight())/2 +fm.getAscent();
   g2D.drawString(progress + "%", x, y-10);

  //Rectangle2D r2d = fm.getStringBounds(progress + "", g);
  // g2D.setFont(new Font("Verdana", Font.PLAIN, 22));
  // g2D.drawString("%", x + 40, y-10);

   g2D.setFont(new Font("Verdana", Font.PLAIN, 15));
   g2D.drawString("Progress", -35, y+5);
   g2D.dispose();

}

}

Thornack
  • 51
  • 1
  • 6
  • Use a coupe Area’s which can be subtracted from each other – MadProgrammer Jan 04 '19 at 22:19
  • Alternatively, you could use a BasicStroke a simply stroke the path – MadProgrammer Jan 04 '19 at 22:27
  • Im not sure how to do that exactly, could you give an example – Thornack Jan 04 '19 at 22:53
  • Maybe instead of "asking" for an example, you could crack open Google and do some research into the two classes I've mentioned - at the very least, you might stumble across [Stroking and Filling Graphics Primitives](https://docs.oracle.com/javase/tutorial/2d/geometry/strokeandfill.html) or the [dozens of available examples](https://stackoverflow.com/search?q=user%3A992484+area+subtract) already avaliable, because there's nothing like self discovery to improve your skills – MadProgrammer Jan 04 '19 at 23:00

1 Answers1

2

There are a number of ways you "might" do this, but the simplest might be to just use BasicStroke and simply draw the arcs (and not fill anything at all)

This example deliberately sets the background color so you can see that it's transparent.

Stroked

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.Arc2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JCustomProgressBar pb = new JCustomProgressBar();
                pb.setProgress(25);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(pb);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class JCustomProgressBar extends JPanel {

        private final Dimension SIZE = new Dimension(200, 200);

        public JCustomProgressBar() {
            super();
            setBackground(Color.RED);
            // Uncomment this to make it transparent
            //setOpaque(false);
        }

        @Override
        public Dimension getPreferredSize() {
            return SIZE;
        }

        int progress = 1;

        public void setProgress(int progress) {
            this.progress = progress;
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g.create();
            g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            g2D.translate(this.getWidth() / 2, this.getHeight() / 2);

            BasicStroke bs = new BasicStroke(8, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            Arc2D.Float arc = new Arc2D.Float(Arc2D.OPEN);
            arc.setAngleStart(90);
            arc.setFrameFromCenter(new Point(0, 0), new Point(90, 90));
            arc.setAngleExtent(-((progress / 100d) * 360));
            g2D.setStroke(bs);
            g2D.setColor(new Color(120, 192, 0));
            g2D.draw(arc);

            bs = new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            arc.setFrameFromCenter(new Point(0, 0), new Point(75, 75));
            arc.setAngleExtent(-(((100 - progress) / 100d) * 360));
            g2D.setStroke(bs);
            g2D.setColor(new Color(197, 228, 146));
            g2D.draw(arc);

            g2D.setColor(Color.black);
            g2D.setFont(new Font("Verdana", Font.PLAIN, 30));
            FontMetrics fm = g2D.getFontMetrics();
            Rectangle2D r2D = fm.getStringBounds(progress + "%", g);
            int x = (0 - (int) r2D.getWidth()) / 2;
            int y = (0 - (int) r2D.getHeight()) / 2 + fm.getAscent();
            g2D.drawString(progress + "%", x, y - 10);

            g2D.setFont(new Font("Verdana", Font.PLAIN, 15));
            g2D.drawString("Progress", -35, y + 5);
            g2D.dispose();
        }

    }

}

You can take a look at Stroking and Filling Graphics Primitives for more details.

Before you tell me how you "don't want the ends rounded" (because I like it that way), make sure you take the time to read the BasicStroke JavaDocs

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366