简体   繁体   English

你如何在JPanel中围绕一个点旋转一个圆?

[英]How do you rotate a circle about a point in JPanel?

I am trying to rotate a circle around a separate point in a program.我试图在程序中围绕一个单独的点旋转一个圆圈。 right now I can get the circle to rotate but it slowly starts getting closer and closer to the point it's rotating from.现在我可以让圆圈旋转,但它慢慢地开始越来越接近它旋转的起点。 I am trying to do this using JPanel and implementing it as a rectangle.我正在尝试使用 JPanel 并将其实现为矩形。

package WoffindenZone;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.event.*;
import java.lang.Math;
public class Protector extends Rectangle{
double Velocity;
int speed = 3;
Protector(int x, int y, int PROTECTOR_DIAMETER){
    super(x,y,PROTECTOR_DIAMETER,PROTECTOR_DIAMETER);
}

public void keyPressed(KeyEvent e){
    if(e.getKeyCode()==KeyEvent.VK_A) {
        setDirection(speed);
        move();
    }
    if(e.getKeyCode()==KeyEvent.VK_D) {
        setDirection(speed);
        move();
    }
}
public void keyReleased(KeyEvent e){
    if(e.getKeyCode()==KeyEvent.VK_A) {
        setDirection(0);
        move();
    }
    if(e.getKeyCode()==KeyEvent.VK_D) {
        setDirection(0);
        move();
    }
}
public void setDirection(int Direction){
    Velocity = Direction*Math.PI/180;
}

public void move(){
    x = (int)Math.round(500 + Math.cos(Velocity) * (x-500) - Math.sin(Velocity) * (y-((1000*0.5555)/2)));
    y = (int)Math.round(((1000*0.5555)/2) + Math.sin(Velocity) * (x-500) + Math.cos(Velocity) * (y-((1000*0.5555)/2)));
    System.out.println(x);
    System.out.println(y);
}
public void draw(Graphics g){
    g.setColor(Color.blue);
    g.fillOval(x,y,width,height);
}    

Use a rotation instance of an AffineTransform .使用AffineTransform的旋转实例。 See getRotateInstance(theta,anchorx,anchory) for details.有关详细信息getRotateInstance(theta,anchorx,anchory)请参阅getRotateInstance(theta,anchorx,anchory)

Returns a transform that rotates coordinates around an anchor point.返回围绕锚点旋转坐标的变换。 This operation is equivalent to translating the coordinates so that the anchor point is at the origin (S1), then rotating them about the new origin (S2), and finally translating so that the intermediate origin is restored to the coordinates of the original anchor point (S3).这个操作相当于平移坐标,使锚点在原点(S1),然后绕新原点(S2)旋转,最后平移,使中间原点恢复到原锚点的坐标(S3)。

How do you rotate a circle about a point in JPanel?你如何在JPanel中围绕一个点旋转一个圆?

Here's how I rotate a circle about a point in a JPanel .这是我如何围绕JPanel一个点旋转一个圆。

旋转圆图形用户界面

I don't know how to make an animated GIF.我不知道如何制作动画 GIF。 Just imagine the blue circle rotating clockwise around the center of the drawing JPanel .想象一下蓝色圆圈围绕绘图JPanel的中心顺时针旋转。

So, let's start at the beginning.所以,让我们从头开始。 Basically, I have a circle rotating on the circumference of another circle.基本上,我有一个圆在另一个圆的圆周上旋转。 So, I create a Circle model class from plain Java.因此,我从纯 Java 创建了一个Circle模型类。

public class Circle {
    
    private final int radius;
    
    private final Color color;
    
    private Point center;
    
    public Circle(int radius, Color color) {
        this.radius = radius;
        this.color = color;
    }
    
    public Point calculateCircumferencePoint(int theta) {
        double radians = Math.toRadians(theta);
        int x = center.x + (int) Math.round(Math.cos(radians) * radius);
        int y = center.y + (int) Math.round(Math.sin(radians) * radius);
        return new Point(x, y);
    }
    
    public void setCenter(int x, int y) {
        this.center = new Point(x, y);
    }

    public void setCenter(Point center) {
        this.center = center;
    }

    public int getRadius() {
        return radius;
    }

    public Color getColor() {
        return color;
    }

    public Point getCenter() {
        return center;
    }
    
}

The class consists of basic getters and setters.该类由基本的 getter 和 setter 组成。 I make the radius and color final because they don't change value in this Java application.我将半径和颜色final值,因为它们不会更改此 Java 应用程序中的值。

The calculateCircumferencePoint method is the only interesting method. calculateCircumferencePoint方法是唯一有趣的方法。 It takes an int angle in degrees, and calculates the point on the circumference represented by that angle, rounded to the nearest X and Y integer points.它需要一个以度为单位的int角度,并计算该角度表示的圆周上的点,四舍五入到最近的 X 和 Y 整数点。

Next, we create two Circle instances, an inner circle and an outer circle.接下来,我们创建两个Circle实例,一个内圆和一个外圆。 Here's the class constructor setting the preferred size of the drawing area, the inner circle, and the outer circle.这是设置绘图区域、内圆和外圆的首选大小的类构造函数。 We start the outer circle at zero degrees (to the right);我们从零度(向右)开始外圆;

private Circle innerCircle;
private Circle outerCircle;

private Dimension drawingPanelSize;

public RotateCircle() {
    this.drawingPanelSize = new Dimension(400, 400);
    int innerCircleRadius = drawingPanelSize.width / 4;
    int centerX = drawingPanelSize.width / 2;
    int centerY = drawingPanelSize.height / 2;
    int outerCircleRadius = drawingPanelSize.width / 10;
    
    this.innerCircle = new Circle(innerCircleRadius, null);
    this.innerCircle.setCenter(centerX, centerY);
    this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
    Point point = innerCircle.calculateCircumferencePoint(0);
    this.outerCircle.setCenter(point);
}

Now, we can start coding the GUI.现在,我们可以开始编写 GUI。 First, we start the Java application by calling the SwingUtilities invokeLater method.首先,我们通过调用SwingUtilities invokeLater方法启动 Java 应用程序。 This method ensures that we create and execute the Swing components on the Event Dispatch Thread .此方法确保我们在Event Dispatch Thread上创建和执行 Swing 组件。

Next, we define the JFrame .接下来,我们定义JFrame Here's the code we have so far.这是我们到目前为止的代码。

public static void main(String[] args) {
    SwingUtilities.invokeLater(new RotateCircle());
}

private Animation animation;

private Circle innerCircle;
private Circle outerCircle;

private DrawingPanel drawingPanel;

private Dimension drawingPanelSize;

public RotateCircle() {
    this.drawingPanelSize = new Dimension(400, 400);
    int innerCircleRadius = drawingPanelSize.width / 4;
    int centerX = drawingPanelSize.width / 2;
    int centerY = drawingPanelSize.height / 2;
    int outerCircleRadius = drawingPanelSize.width / 10;
    
    this.innerCircle = new Circle(innerCircleRadius, null);
    this.innerCircle.setCenter(centerX, centerY);
    this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
    Point point = innerCircle.calculateCircumferencePoint(0);
    this.outerCircle.setCenter(point);
}

@Override
public void run() {
    JFrame frame = new JFrame("Rotate Circle");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    drawingPanel = new DrawingPanel(drawingPanelSize, 
            outerCircle);
    frame.add(drawingPanel, BorderLayout.CENTER);
    
    frame.pack();
    frame.setLocationByPlatform(true);
    frame.setVisible(true);
    
    animation = new Animation(0);
    new Thread(animation).start();
}

The JFrame methods must be called in a certain order. JFrame方法必须按特定顺序调用。 This is the order I use for most of my SWwing applications.这是我用于大多数 SWwing 应用程序的顺序。

I pack the JFrame .我打包JFrame I don't set a JFrame size.我没有设置JFrame大小。 I let the Swing layout managers set the size of my JFrame .我让Swing 布局管理器设置我的JFrame的大小。 The default layout of a JFrame content pane is a BorderLayout . JFrame内容窗格的默认布局是BorderLayout I put my drawing JPanel in the center of the BorderLayout .我将我的绘图JPanel放在BorderLayout的中心。

Next, I create the drawing JPanel .接下来,我创建绘图JPanel

public class DrawingPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    
    private Circle circle;
    
    public DrawingPanel(Dimension size, Circle circle) {
        this.circle = circle;
        this.setBackground(Color.WHITE);
        this.setPreferredSize(size);
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        
        Point center = circle.getCenter();
        int radius = circle.getRadius();
        int diameter = radius + radius;
        g2d.setColor(circle.getColor());
        g2d.fillOval(center.x - radius, center.y - radius, 
                diameter, diameter);
    }
    
}

All the drawing JPanel does is paint a Circle object. JPanel所做的所有绘图都是绘制一个Circle对象。 Pretty straightforward.很简单。

The fillOval method paints an oval from the upper left hand corner. fillOval方法从左上角绘制一个椭圆。 We calculate the upper left hand point from the center point.我们从中心点计算左上角。

The responsibility for calculating and updating the outer circle center point falls to my controller class, the Animation class.计算和更新外圆中心点的责任落在我的控制器类, Animation类。 I use a simple loop to update the theta angle, calculate the new outer circle center point, paint the outer circle, and wait a period of time.我用一个简单的循环更新theta角,计算新的外圆中心点,画外圆,等待一段时间。

Here's that code.这是那个代码。

public class Animation implements Runnable {
    
    private int theta;
    
    public Animation(int theta) {
        this.theta = theta;
    }
    
    @Override
    public void run() {
        while (true) {
            theta++;
            theta = (theta >= 360) ? 0 : theta;
            
            Point center = innerCircle.calculateCircumferencePoint(theta);
            outerCircle.setCenter(center);
            repaint();
            sleep(30L);
        }
    }
    
    private void repaint() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                drawingPanel.repaint();
            }
        });
    }
    
    private void sleep(long duration) {
        try {
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}

The Animation repaint method makes the call to the drawing JPanel repaint method inside another SwingUtilities invokeLater method. Animation repaint方法调用另一个SwingUtilities invokeLater方法中的绘图JPanel repaint方法。 This method ensures that the drawing happens on the Event Dispatch Thread.此方法确保绘制发生在事件调度线程上。

Finally, here's the complete, runnable, example.最后,这是完整的、可运行的示例。 I used inner classes so I could post the code as one block, and you can copy and run this code as one block.我使用了内部类,因此我可以将代码作为一个块发布,您可以将这些代码作为一个块复制和运行。 Generally, classes should be in separate files and for a more complex GUI, separate packages.通常,类应该在单独的文件中,对于更复杂的 GUI,应该在单独的包中。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class RotateCircle implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new RotateCircle());
    }
    
    private Animation animation;
    
    private Circle innerCircle;
    private Circle outerCircle;
    
    private DrawingPanel drawingPanel;
    
    private Dimension drawingPanelSize;
    
    public RotateCircle() {
        this.drawingPanelSize = new Dimension(400, 400);
        int innerCircleRadius = drawingPanelSize.width / 4;
        int centerX = drawingPanelSize.width / 2;
        int centerY = drawingPanelSize.height / 2;
        int outerCircleRadius = drawingPanelSize.width / 10;
        
        this.innerCircle = new Circle(innerCircleRadius, null);
        this.innerCircle.setCenter(centerX, centerY);
        this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
        Point point = innerCircle.calculateCircumferencePoint(0);
        this.outerCircle.setCenter(point);
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Rotate Circle");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        drawingPanel = new DrawingPanel(drawingPanelSize, 
                outerCircle);
        frame.add(drawingPanel, BorderLayout.CENTER);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
        
        animation = new Animation(0);
        new Thread(animation).start();
    }
    
    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        
        private Circle circle;
        
        public DrawingPanel(Dimension size, Circle circle) {
            this.circle = circle;
            this.setBackground(Color.WHITE);
            this.setPreferredSize(size);
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            
            Point center = circle.getCenter();
            int radius = circle.getRadius();
            int diameter = radius + radius;
            g2d.setColor(circle.getColor());
            g2d.fillOval(center.x - radius, center.y - radius, 
                    diameter, diameter);
        }
        
    }
    
    public class Animation implements Runnable {
        
        private int theta;
        
        public Animation(int theta) {
            this.theta = theta;
        }
        
        @Override
        public void run() {
            while (true) {
                theta++;
                theta = (theta >= 360) ? 0 : theta;
                
                Point center = innerCircle.calculateCircumferencePoint(theta);
                outerCircle.setCenter(center);
                repaint();
                sleep(30L);
            }
        }
        
        private void repaint() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    drawingPanel.repaint();
                }
            });
        }
        
        private void sleep(long duration) {
            try {
                Thread.sleep(duration);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    public class Circle {
        
        private final int radius;
        
        private final Color color;
        
        private Point center;
        
        public Circle(int radius, Color color) {
            this.radius = radius;
            this.color = color;
        }
        
        public Point calculateCircumferencePoint(int theta) {
            double radians = Math.toRadians(theta);
            int x = center.x + (int) Math.round(Math.cos(radians) * radius);
            int y = center.y + (int) Math.round(Math.sin(radians) * radius);
            return new Point(x, y);
        }
        
        public void setCenter(int x, int y) {
            this.center = new Point(x, y);
        }

        public void setCenter(Point center) {
            this.center = center;
        }

        public int getRadius() {
            return radius;
        }

        public Color getColor() {
            return color;
        }

        public Point getCenter() {
            return center;
        }
        
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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