简体   繁体   中英

Java awt draw elements around a circle

I'm currently writing a little game and I came over a problem. I need to draw 64 small circles at the border of a big circle. So I want something like this:

64个圆圈围绕一个大圆圈排列

I've already tried many things, but they didn't worked. How can this be done in java, using the java.awt.Component#paint() method and the java.awt.Graphics class?

Thanks.

So, your basic problem comes down to "find a point on a circle based on a given angle"

A quick google will find resources like Find the coordinates of a point on a circle , now, to be frank, I'm an idiot, so I'd narrow my search to include Java, which would give us something like How to calculate the coordinates of points in a circle using Java? - sweet.

So the basic calculation might look something like

double xPosy = Math.cos(rads) * radius);
double yPosy = Math.sin(rads) * radius);

Now, this solves the core aspect of your problem. The rest comes down to simply painting the results. See Performing Custom Painting and Painting in AWT and Swing as a starting point and 2D Graphics for a more detailed look into the API.

Now, taking all of the above, you might end up with a solution looking something like...

在此处输入图像描述

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
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 {

        public TestPane() {
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            int midX = getWidth() / 2;
            int midY = getHeight() / 2;
            Dimension size = new Dimension(4, 4);
            g2d.setColor(Color.BLACK);
            for (int index = 0; index < 64; index++) {
                double angle = (360d / 64d) * index;
                Point2D poc = getPointOnCircle(angle, 100 - 4);
                Rectangle2D box = new Rectangle2D.Double(midX + poc.getX() - 2, midY + poc.getY() - 2, size.width, size.height);
                g2d.draw(box);
            }
            g2d.dispose();
        }

        protected Point2D getPointOnCircle(double degress, double radius) {
            double rads = Math.toRadians(degress - 90); // 0 becomes the top

            return new Point2D.Double(Math.cos(rads) * radius, Math.sin(rads) * radius);
        }
    }
}

So, about now, you should realise that my "squares" are, well, square, not "dimond" shaped like yours. This is where you're going to have to start doing some work.

If I was approaching this problem I might be tempted, to create a custom shape or, apply a 45 degree transformation to the box and the translate it's position to render it or just rotate the whole result by 45 degrees, but this brings a whole bag of other issues depending on what you want to do with it

Here is one way to do it (most of this is boiler plate for setting up the containing frame and panel). It uses the sine and cosine methods to compute the points of a unit circle. These points are then adjusted by first multiplying by the radius of the desired larger circle and then centering it in the panel by adding the center x,y offsets.

The only really special things it contains is 1) that the outer circles are guaranteed to be spaced apart by a distance of one of their diameters. So if the number of circles decreases the size increases. This can be adjusted as you see fit. 2) I used RenderingHints to visually smooth out the curves. And finally 3) I added a simple WheelListener with arbitrary limits so you could see the changes when moving the mouse wheel up or down. This modifies NCIRCLES (something one should not do with constants) and then repaints the panel. It is easily removed.

public class CircleBorder extends JPanel {

    JFrame frame = new JFrame("Circle Border");
    static int BIG_RADIUS = 200;
    static int NCIRCLES = 60;
    static int WIDTH = 500;
    static int HEIGHT = 500;

    public static void main(String[] args ) {
        SwingUtilities.invokeLater(()->new CircleBorder().start());
    }
    public void start() {
     addMouseWheelListener((we)-> {
           int rot = we.getWheelRotation();
           if (NCIRCLES < 70 && rot > 0 || NCIRCLES > 7 && rot < 0) {
               NCIRCLES += rot;
           }
              
           repaint();
       });
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.add(this);
       frame.pack();
       // center on screen
       frame.setLocationRelativeTo(null);
       frame.setVisible(true);
    }
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(WIDTH,HEIGHT);
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        
        int centerX = WIDTH/2;
        int centerY = HEIGHT/2;
        
        double angleIncrement = 2*Math.PI/NCIRCLES;
        g2d.setColor(Color.RED);
        
        // the next two lines adjusts the little radius so that each
        // outer circle will be one diameter apart.
        int bigD = (int)(Math.PI*2*BIG_RADIUS);
        int littleRadius = bigD/(NCIRCLES*4);
        
        // compute the x and y coordinates of the center of the outer circles.
        // and iterate once around the circle based on the computed angle above
        // to draw the circumference.  The little radius is subtracted to ensure
        // the center of those circles lies on the generated outer circle.

        double angle = 0;
        for (int i = 0; i < NCIRCLES; i++) {
              int x = (int)(centerX + Math.cos(angle)*BIG_RADIUS) - littleRadius;
              int y = (int)(centerY + Math.sin(angle)*BIG_RADIUS) - littleRadius;
              g2d.fillOval(x,y,littleRadius*2,littleRadius*2);
              angle += angleIncrement;
        }
        
        g2d.dispose();
    }
}

Thanks to @MadProgrammer for his answer. I combined his code with the suggestion to use RenderingHints by @WJS and my own ideas and the following code worked for me.

package gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

/**
 * Thanks to <a
 * href=https://stackoverflow.com/users/992484/madprogrammer>@MadProgrammer</a>
 * on StackOverflow for the geometry part (<a
 * href=https://stackoverflow.com/questions/70398744/java-awt-draw-elements-around-a-circle>Source
 * code</a>)
 */
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.setExtendedState(Frame.MAXIMIZED_BOTH);
                frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JComponent {

        public TestPane() {
        }

        @Override
        public Dimension getPreferredSize() {
            return getParent().getSize();
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            //Thanks to @WJS for the idea
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            int midX = getWidth() / 2;
            int midY = getHeight() / 2;
            int min = Math.min(getHeight() / 2, getWidth() / 2);
            Dimension size = new Dimension(min / 20, min / 20);
            double minsize = Math.min(size.getHeight(), size.getWidth());
            double radius = min - minsize;

            g2d.setColor(Color.BLACK);
            for (int index = 0; index < 64; index++) {
                double angle = (360d / 64d) * index;
                double rads = Math.toRadians(angle - 90); // 0 becomes the top
                Point2D poc = new Point2D.Double(Math.cos(rads) * radius, Math.sin(rads) * radius);
                g2d.fillOval((int) (midX + poc.getX() - 2), (int) (midY + poc.getY() - 2), size.width, size.height);
            }
            g2d.dispose();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM