如何围绕JPanel中的点旋转圆?

问题描述

我正在尝试绕程序中的单独点旋转一个圆。现在,我可以使圆旋转,但是它慢慢开始越来越接近旋转的起点。我正在尝试使用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,width,height);
}    

解决方法

使用AffineTransform的旋转实例。有关详细信息,请参见getRotateInstance(theta,anchorx,anchory)

返回一个变换,该变换绕着锚点旋转坐标。此操作等效于平移坐标,使锚点位于原点(S1),然后围绕新原点旋转它们(S2),最后平移,以便将中间原点恢复到原始锚点的坐标(S3)。

,

如何围绕JPanel中的点旋转圆?

这是我绕JPanel中的点旋转圆的方法。

Rotate Circle GUI

我不知道如何制作动画GIF。想象一下,蓝色圆圈围绕图形JPanel的中心顺时针旋转。

因此,让我们从头开始。基本上,我有一个在另一个圆的圆周上旋转的圆。因此,我用纯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;
    }
    
}

该类包含基本的getter和setter。我将半径和颜色设为final,因为它们不会更改此Java应用程序中的值。

calculateCircumferencePoint方法是唯一有趣的方法。它以度数为int角,并计算该角所代表的圆周上的点,并四舍五入到最接近的X和Y整数点。

接下来,我们创建两个Circle实例,一个内圈和一个外圈。这是类构造函数,用于设置绘图区域,内圆和外圆的首选大小。我们以零度(向右)开始外圆;

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

现在,我们可以开始编写GUI了。首先,我们通过调用SwingUtilities invokeLater方法来启动Java应用程序。此方法可确保我们在Event Dispatch Thread上创建并执行Swing组件。

接下来,我们定义JFrame。这是到目前为止的代码。

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

必须按一定顺序调用JFrame方法。这是我大多数SWwing应用程序使用的顺序。

我打包了JFrame。我没有设置JFrame的大小。我让Swing layout managers设置了JFrame的大小。 JFrame内容窗格的默认布局是BorderLayout。我将绘图JPanel放在了BorderLayout的中心。

接下来,我创建图形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);
    }
    
}

JPanel所做的所有绘制都是绘制Circle对象。很简单。

fillOval方法从左上角绘制一个椭圆形。我们从中心点计算左上角点。

计算和更新外圆中心点的职责属于我的控制器类Animation。我使用一个简单的循环来更新theta角度,计算新的外圆中心点,绘制外圆并等待一段时间。

这是代码。

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

Animation repaint方法在另一个JPanel repaint方法内调用图形SwingUtilities invokeLater方法。此方法可确保绘图发生在事件调度线程上。

最后,这是完整的,可运行的示例。我使用了内部类,因此我可以将代码作为一个块发布,并且可以将这些代码作为一个块复制并运行。通常,类应放在单独的文件中,而对于更复杂的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,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;
        }
        
    }

}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...