如何使用Java Swing绘制2d箭头? [英] How to draw 2d arrows with Java Swing?

查看:222
本文介绍了如何使用Java Swing绘制2d箭头?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Swing 2d绘制一些可旋转的箭头,在线有一些示例代码,所以我将它们复制并组合成一个应用程序,但是这三种方法中的每一种都有问题:第一种不是从它的中心旋转,另外2个在箭头中看不正确,有人可以告诉我如何解决它们吗?

I'm trying to draw some rotatable arrows with Swing 2d, there are some sample code online, so I copied and combined them into one app, but there is something wrong with each of the 3 methods : 1st one doesn't rotate from it's center, the other 2 don't look correctly in the arrow head, can someone show me how to fix them ?

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;

public class Arrow_Test extends JPanel implements ChangeListener {
    Path2D.Double arrow = createArrow();
    double theta = 0;

    public void stateChanged(ChangeEvent e) {
        int value = ((JSlider) e.getSource()).getValue();
        theta = Math.toRadians(value);
        repaint();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(6));
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        int cx = getWidth() / 2;
        int cy = getHeight() / 2;
        AffineTransform at = AffineTransform.getTranslateInstance(cx, cy);
        at.rotate(theta);
        at.scale(2.0, 2.0);
        Shape shape = at.createTransformedShape(arrow);
        g2.setPaint(Color.blue);
        g2.draw(shape);

        GeneralPath a = drawArrow(20, 20, 30, 20, 50, 13);
        AffineTransform at1 = AffineTransform.getTranslateInstance(cx, cy);
        at1.rotate(theta);
        at1.scale(2.0, 2.0);
        Shape shape1 = at1.createTransformedShape(a);
        g2.setPaint(Color.blue);
        g2.draw(shape1);

        drawArrow(100, 100, 50, 0, g2);
    }

    private Path2D.Double createArrow() {
        int length = 80;
        int barb = 15;
        double angle = Math.toRadians(20);
        Path2D.Double path = new Path2D.Double();
        path.moveTo(-length / 2, 0);
        path.lineTo(length / 2, 0);
        double x = length / 2 - barb * Math.cos(angle);
        double y = barb * Math.sin(angle);
        path.lineTo(x, y);
        x = length / 2 - barb * Math.cos(-angle);
        y = barb * Math.sin(-angle);
        path.moveTo(length / 2, 0);
        path.lineTo(x, y);
        return path;
    }

    GeneralPath drawArrow(int x1, int y1, int x2, int y2, double length,
            double width) {
        double x, y;
        length = 50;
        width = 5;

        Point2D start = new Point2D.Double(x1, y1);
        Point2D end = new Point2D.Double(x2, y2);
        Point2D base = new Point2D.Double();
        Point2D back1 = new Point2D.Double();
        Point2D back2 = new Point2D.Double();

        length = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

        // Compute normalized line vector
        x = (x2 - x1) / length;
        y = (y2 - y1) / length;
        // Compute points for arrow head
        base.setLocation(x2 - x * length, y2 - y * length);
        back1.setLocation(base.getX() - width * y, base.getY() + width * x);
        back2.setLocation(base.getX() + length * y, base.getY() - width * x);

        Line2D.Double l1 = new Line2D.Double(start, end);
        Line2D.Double l2 = new Line2D.Double(end, back2);
        Line2D.Double l3 = new Line2D.Double(end, back1);

        GeneralPath c = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
        c.append(l1, true);
        c.append(l2, true);
        c.append(l3, true);

        return c;
    }

    private void drawArrow(int x1, int y1, int x2, int y2, Graphics2D g2d) {

        int x[] = { 0, 36, 0 };
        int y[] = { -10, 0, 10 };

        g2d.rotate(theta);
        g2d.drawLine(x1 - 20, y1, x1 + 20, y1);
        // will move the orgin
        g2d.translate(x1, y1);
        double angle = findLineAngle(x1 - 20, y1, x1 + 20, y1);
        System.out.println("angle is===>" + angle);
        g2d.rotate(angle);

        g2d.fillPolygon(new Polygon(x, y, 3));
        // /will restore orgin
        g2d.translate(-x2, -y2);
        g2d.rotate(-angle);
    }

    private double findLineAngle(int x1, int y1, int x2, int y2) {
        if ((x2 - x1) == 0)
            return Math.PI / 2;
        return Math.atan((y2 - y1) / (x2 - x1));
    }

    private JSlider getSlider() {
        JSlider slider = new JSlider(-180, 180, 0);
        slider.addChangeListener(this);
        return slider;
    }

    public static void main(String[] args) {
        Arrow_Test test = new Arrow_Test();
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(test);
        f.add(test.getSlider(), "Last");
        f.setSize(400, 400);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}


推荐答案

我真的不想进入为什么,因为你的代码很难阅读。

I really don't want to get into "why", as your code is hard enough to read.

旋转对象时,你应该指定一个锚点(x / y )应围绕该旋转进行旋转。默认情况下,这是当前上下文的 0x0 位置。

When rotating an object, you should specify a anchor (x/y) around which the rotation should take place. By default, this is the 0x0 position of the current context.

当您基于路径的箭头时。 ..有趣的,可能与他们的创作方式有关,但我并没有真正玩弄它们。

When you "path" based arrows look...interesting, could have to do with the way they are created, but I didn't really play around with them.

另外你要小心的是转换是复合的,这是好事和坏事,你只需要小心它们;)

The other thing you need to be careful is, transformations are compounding, this is a good and bad thing, you just need to be careful when them ;)

让我们从一个基本的形状开始......

Lets start with a basic shape...

public class Arrow extends Path2D.Double {

    public Arrow() {
        moveTo(0, 10);
        lineTo(36, 10);
        moveTo(36 - 16, 0);
        lineTo(36, 10);
        moveTo(36 - 16, 20);
        lineTo(36, 10);
    }

}

好的,没什么了不起的,你可以添加宽度/高度参数以使箭头以您想要的方式显示,但这是一个基本的开始。我更喜欢使用基于 Shape 的对象,它们只是更简单地使用旧的 Polygon 样式API。

Okay, nothing impressive, you could add width/height parameters to make the arrow appear the way you want, but this gets a basic start. I prefer to use Shape based objects, they are just simpler to work with then the old Polygon style API.

箭头基本上是三条线,它们在垂直中间和水平线的末端相遇。如果箭头是单行,你可能会得到更好的结果,但我会留给你玩

The Arrow is basically three lines which meet at the middle of the vertical and the end of the horizontal. You might get a better result if the arrow head was a single line, but I'll leave that for you to play with

接下来,我们需要定位和旋转对象(箭头箭头的实例 BTW)

Next, we need to position and rotate the object (arrow is an instance of Arrow BTW)

double x = (getWidth() - arrow.getBounds().getWidth()) / 2d;
double y = (getHeight() - arrow.getBounds().getHeight()) / 2d;

AffineTransform at = new AffineTransform();
at.translate(x, y);
at.rotate(theta, arrow.getBounds().getWidth() / 2d, arrow.getBounds().getHeight() / 2d);
g2d.setTransform(at);

g2d.draw(arrow);

我们首先应用翻译,这使得 Graphics context的 0x0 位置现在是我们指定的 x / y 位置。这使得计算箭头应该围绕其旋转的锚位置变得更加容易......

We apply a translation first, this makes it so that the Graphics context's 0x0 position is now the x/y position we specify. This makes it MUCH easier to calculate the anchor position around which the arrow should rotate...

并且可以将它们全部绑定在一起的可运行示例

And a runnable example to bind it all together

import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Arrow_Test extends JPanel implements ChangeListener {

    double theta = 0;
    Path2D arrow = new Arrow();

    public void stateChanged(ChangeEvent e) {
        int value = ((JSlider) e.getSource()).getValue();
        theta = Math.toRadians(value);
        repaint();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

        g2d.setStroke(new BasicStroke(4));

        double x = (getWidth() - arrow.getBounds().getWidth()) / 2d;
        double y = (getHeight() - arrow.getBounds().getHeight()) / 2d;

        AffineTransform at = new AffineTransform();
        at.translate(x, y);
        at.rotate(theta, arrow.getBounds().getWidth() / 2d, arrow.getBounds().getHeight() / 2d);
        g2d.setTransform(at);

        g2d.draw(arrow);
        g2d.dispose();
    }

    private JSlider getSlider() {
        JSlider slider = new JSlider(-180, 180, 0);
        slider.addChangeListener(this);
        return slider;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Arrow_Test test = new Arrow_Test();
                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(test);
                f.add(test.getSlider(), "Last");
                f.setSize(400, 400);
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }

    public class Arrow extends Path2D.Double {

        public Arrow() {
            moveTo(0, 10);
            lineTo(36, 10);
            moveTo(36 - 16, 0);
            lineTo(36, 10);
            moveTo(36 - 16, 20);
            lineTo(36, 10);
        }

    }
}

这篇关于如何使用Java Swing绘制2d箭头?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆