Search code examples
javajava-2d

Java2D: how to draw the slice of a circle with GeneralPath


I want to create a wheel of fortune game for practice. I created the wheel slices using GeneralPath, but I'm having issues finding out what is supposed to be the beizer point to have a perfect circle. Take a look at the screenshot and you'll see the issue.

enter image description here

I don't know the formula I should use to get the proper value, now I'm basically just guessing with:

        path.moveTo(x, y); // the center

        this.firstPointX = x + wSize * Math.cos(angle*i);
        this.firstPointY = y + wSize * Math.sin(angle*i);

        path.lineTo(this.firstPointX, this.firstPointY);

        this.secondPointX = x + wSize * Math.cos(angle*(i+1));
        this.secondPointY = y + wSize * Math.sin(angle*(i+1));

        path.moveTo(x, y); // back to the center
        path.lineTo(this.secondPointX, this.secondPointY);

        this.beizerX = x + (THIS IS THE VALUE I NEED) * Math.cos((angle*i+angle/2));
        this.beizerY = y + (THIS IS THE VALUE I NEED) * Math.sin((angle*i+angle/2));

        path.curveTo(this.secondPointX, this.secondPointY, this.beizerX, this.beizerY, this.firstPointX, this.firstPointY);

        path.closePath();

Solution

  • Why not just use Arc2D or simply draw a circle and 6 lines? Then the basic problem is how to determine two points on a circle (start point and end point, which is 180 degrees away)

    Maybe something like...

    Spin me

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.GeneralPath;
    import java.awt.geom.Point2D;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private GeneralPath gp = new GeneralPath();
    
            public TestPane() {
    
                //GeneralPath gp = new GeneralPath();
                gp.append(new Ellipse2D.Double(0, 0, 200, 200), true);
    
                for (double angle = 0; angle < 180; angle += 30) {
                    Point2D startPoint = pointOnCircle(angle, 100);
                    Point2D endPoint = pointOnCircle(angle + 180, 100);
    
                    gp.moveTo(startPoint.getX(), startPoint.getY());
                    gp.lineTo(endPoint.getX(), endPoint.getY());
                }
    
            }
    
            protected Point2D pointOnCircle(double degrees, double radius) {
                double origin = radius;
                double rads = Math.toRadians(degrees);
                double x = origin + (Math.cos(rads) * radius);
                double y = origin + (Math.sin(rads) * radius);
    
                return new Point2D.Double(x, y);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(300, 300);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHints(hints);
                g2d.translate(50, 50);
                g2d.draw(gp);
                g2d.dispose();
            }
        }
    
    }
    

    Arc2D

    Because, you know, you can

    Spin me some more

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.geom.Arc2D;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.GeneralPath;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private GeneralPath gp = new GeneralPath();
    
            public TestPane() {
                gp.append(new Ellipse2D.Double(0, 0, 200, 200), true);
    
                for (double angle = 0; angle < 360; angle += 30) {
                    Arc2D arc = new Arc2D.Double(0, 0, 200, 200,
                            angle,
                            30,
                            Arc2D.PIE);
                    gp.append(arc, false);
                }
    
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(300, 300);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHints(hints);
                g2d.translate(50, 50);
                g2d.draw(gp);
                g2d.dispose();
            }
        }
    
    }
    

    I tried to use arches but I had trouble rotating them.

    Easy as Pi

    Easy as Pi

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Arc2D;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.GeneralPath;
    import java.awt.geom.Rectangle2D;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private GeneralPath gp = new GeneralPath();
            private double spinValue = 0;
    
            public TestPane() {
                gp.append(new Ellipse2D.Double(0, 0, 200, 200), true);
    
                for (double angle = 0; angle < 360; angle += 30) {
                    Arc2D arc = new Arc2D.Double(0, 0, 200, 200,
                            angle,
                            30,
                            Arc2D.PIE);
                    gp.append(arc, false);
                }
    
                Timer timer = new Timer(5, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        spinValue += 0.01;
                        repaint();
                    }
                });
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(300, 300);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHints(hints);
    
                Rectangle2D bounds = gp.getBounds2D();
                double x = (getWidth() - bounds.getWidth()) / 2d;
                double y = (getHeight() - bounds.getHeight()) / 2d;
    
                AffineTransform at = AffineTransform.getTranslateInstance(x, y);
                at.rotate(spinValue, bounds.getCenterX(), bounds.getCenterY());
    
                g2d.transform(at);
                g2d.draw(gp);
                g2d.dispose();
            }
        }
    
    }
    

    I also would like to fill the single slices with gradients and animate indipendently with rotations and such

    Then you can't use a single Path, as it's treated as a single graphical element, you will need to use individual slices, so I would highly recommend using Arc2D as it would be simple to maintain a list of them and simply transform the Graphics context as needed