Java awt 围绕一个圆圈绘制元素

Java awt draw elements around a circle

我正在写一个小游戏,遇到了一个问题。我需要在一个大圆的边界处画 64 个小圆。所以我想要这样的东西:

我已经尝试了很多东西,但都没有用。如何在 java 中使用 java.awt.Component#paint() 方法和 java.awt.Graphics class?

完成此操作

谢谢。

因此,您的基本问题归结为“根据给定角度在圆上找到一个点”

快速 google 会找到像 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 - sweet.

这样的资源

所以基本计算可能类似于

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

现在,这解决了您问题的核心方面。剩下的就是简单地绘制结果。有关 API.

的更详细信息,请参阅 Performing Custom Painting and Painting in AWT and Swing as a starting point and 2D Graphics

现在,考虑到以上所有内容,您最终可能会得到一个类似于...

的解决方案

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);
        }
    }
}

所以,大约现在,您应该意识到我的“正方形”是正方形,而不是像您的“菱形”形状。这是您必须开始做一些工作的地方。

如果我正在处理这个问题,我可能会想创建一个自定义形状,或者对 box 应用 45 度变换并平移它的位置以渲染它,或者只是将整个结果旋转45 度,但这会带来一大堆其他问题,具体取决于您要用它做什么

这是一种方法(其中大部分是用于设置包含框架和面板的样板)。它使用 sine and cosine 方法计算 unit 圆的点。然后通过首先乘以所需较大圆的 radius 然后通过添加 center x,y 偏移量将其置于面板中心来调整这些点。

它包含的唯一真正特别的东西是

  1. 保证外圆之间的距离为其直径之一。因此,如果圆圈数量减少,则尺寸会增加。这可以根据您的需要进行调整。
  2. 我使用 RenderingHints 在视觉上平滑了曲线。最后
  3. 我添加了一个具有任意限制的简单 WheelListener,这样您可以在上下移动鼠标滚轮时看到变化。这会修改 NCIRCLES(不应该用常量做的事情)然后重新绘制面板。它很容易删除。
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. Then iterate once around the circle based on the
        // computed angle above to draw the circumference. The little
        // radius is subtracted to ensure those circles are
        // centered on the large circles circumference.

        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();
    }
}

感谢@MadProgrammer 的回答。我将他的代码与@WJS 提出的使用 RenderingHints 的建议和我自己的想法结合起来,下面的代码对我有用。

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://whosebug.com/users/992484/madprogrammer>@MadProgrammer</a>
 * on Whosebug for the geometry part (<a
 * href=
 * 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();
        }
    }
}