使用 arc2d 旋转图形

Rotating a graphic using arc2d

我正在尝试做一个轮盘赌场游戏,所以为此我使用 Arc2D 包制作了我的轮盘。

下面是我的代码

package roulette;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Arc2D;
import java.awt.geom.AffineTransform;
import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class RouletteInterface extends JPanel{
    public int spinValue = 0;
    public void paint(Graphics g){       
        Graphics2D g2d = (Graphics2D)g;
        paintRoulette(g2d);

    }

    public void paintRoulette(Graphics2D g2d) {
        RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHints(hints);
        AffineTransform at = AffineTransform.getTranslateInstance(10, 10);
        at.rotate(spinValue, 10, 10);


        double angle = 360 / 36.9;
        double startAngle = 0;
        int color = 0;
        for(int i = 0; i < 37; i++) {
            if(i == 0) {
                g2d.setColor(Color.GREEN);
            } else {
                if(color == 0) {
                    g2d.setColor(Color.BLACK);
                    color = 1;
                } else {
                    g2d.setColor(Color.RED);
                    color = 0;
                }
            }

            g2d.fill(new Arc2D.Double(100, 100, 300, 300, startAngle, angle, Arc2D.PIE));
            startAngle += angle;
        }

        g2d.transform(at);

        Timer timer = new Timer(5, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                spinValue += 0.01;
                repaint();
            }
        });
        timer.start();
    }
}

简而言之,我没有使用一般路径,因为我想用颜色 red/green 或黑色填充每个圆弧,就像原来的轮盘赌一样,对于旋转,我尝试使用计时器来增加旋转值(这有效对我来说,但是当我对 AfinneTransformation 使用通用路径时,但是当我 运行 代码时,什么也没有发生。它只显示没有动画的轮盘赌。我能做什么?

提前致谢。

一般来说,绘画和图形是相当高级的主题,Java/Swing 很好地将 API "commonalise" 变成了合理易用的东西,但仍然需要时间和精力来学习和完全理解。

我强烈建议标记 Performing Custom Painting, Painting in AWT and Swing and 2D Graphics 和已预订的 JavaDocs,因为您会定期回来查看它们(我仍然这样做)

有很多问题,这些问题加起来让你的生活变得困难。

开始于...

public void paint(Graphics g){       
    Graphics2D g2d = (Graphics2D)g;
    paintRoulette(g2d);

}

你应该倾向于覆盖 paintComponent 而不是 paint,绘画是一个复杂的过程,你需要仔细选择你的入口点。此外,您应该始终调用 paint 方法 super 方法,除非您绝对、积极地准备好自己接管其核心功能。

在您的情况下,您还应该在将 Graphics 上下文传递给 paintRoulette 之前制作一份副本,因为 Graphics 是共享资源并且您正在应用的转换将导致在您的组件之后绘制的任何内容出现问题。

转换...

AffineTransform at = AffineTransform.getTranslateInstance(10, 10);
at.rotate(spinValue, 10, 10);

这有点意思。您正在创建 10x10 的翻译,这将移动 Graphics 上下文的原点。然后应用旋转,锚定到 10x10.

我提到它的原因是因为你然后...

g2d.fill(new Arc2D.Double(100, 100, 300, 300, startAngle, angle, Arc2D.PIE));

这意味着圆弧从组件的角偏移 110x110(添加到您的翻译中)并且您将围绕组件的 [=80] 的点 20x20 旋转=] 角(添加你的翻译)...这对我来说很奇怪,因为轮子的中心实际上在 250x250(从组件的 top/left 角),这将成为一个很奇怪的影响。

最后,在绘画完成后应用转换,然后在绘画方法中创建一个 Timer...

绘画是连续完成的。所以一个操作会影响下一个操作,这意味着您需要在绘制某些东西(您想要转换的)之前应用转换

您还需要了解,您无法控制绘制过程,这意味着您的组件可能会在任何时候出于任何原因在没有您的交互的情况下被绘制。这意味着您可以在很短的时间内进行无限次 Timer

相反,您的定时器应该从 paint 进程外部控制。

另一件让我花了一些时间解决的事情是...

public int spinValue = 0;
//...
Timer timer = new Timer(5, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        spinValue += 0.01;
        repaint();
    }
});

您将 spinValue 声明为 int,但向其添加了一个浮点值,这会导致小数部分被截断,因此该值始终为 0.

此外,AffineTransform#rotate 要求角度以弧度为单位,而不是度数。不确定它是否重要,但你应该知道它。

可运行示例...

好的,所以在应用上面的代码之后,代码 "might" 看起来像...

import java.awt.Color;
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.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
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 RoulettePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class RoulettePane extends JPanel {

        private double spinValue = 0;
        private Timer timer;

        public RoulettePane() {
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    spin();
                }
            });
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            paintRoulette(g2d);
            g2d.dispose();
        }

        protected void spin() {
            if (timer != null && timer.isRunning()) {
                return;
            }
            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    spinValue += 0.01;
                    repaint();
                }
            });
            timer.start();
        }

        protected void paintRoulette(Graphics2D g2d) {
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHints(hints);

            int width = getWidth();
            int height = getHeight();

            int dimeter = Math.min(width, height);

            AffineTransform at = AffineTransform.getRotateInstance(spinValue, dimeter / 2, dimeter / 2);
            g2d.transform(at);

            double angle = 360 / 36.9;
            double startAngle = 0;
            int color = 0;
            for (int i = 0; i < 37; i++) {
                if (i == 0) {
                    g2d.setColor(Color.GREEN);
                } else {
                    if (color == 0) {
                        g2d.setColor(Color.BLACK);
                        color = 1;
                    } else {
                        g2d.setColor(Color.RED);
                        color = 0;
                    }
                }

                g2d.fill(new Arc2D.Double(0, 0, dimeter, dimeter, startAngle, angle, Arc2D.PIE));
                startAngle += angle;
            }
        }
    }
}

nb: 我暂时把翻译去掉了,因为我想专注于根据组件的实际 width/height 使输出更加动态