根据角度在圆周上绘制一条线

时间:2016-01-28 23:43:41

标签: java swing line geometry angle

我希望它看起来像:

https://stackoverflow.com/a/25058091/1393766

圆圈沿着中心圆圈的箭头移动。

目前的情况如何:

enter image description here

我想在两个圆圈之间绘制2条线。然而,这些圆圈在屏幕周围移动,我不知道有条不紊地在它们之间画线。例如,我总是有两个圆圈的左上角我想画一条线但是就是这样。我需要帮助在java中绘制一条线,它将根据其位置进行调整,以便在圆圈移动时线条围绕边缘移动

      for (int z = 0; z < lines.size(); z++) {
                    if (lines.get(z).getfState().equals(states.get(a).getText()) && !lines.get(z).getfState().equals(lines.get(z).getnState())) {
                        transition.get(z).setIcon(null);
                        for (int x = 0; x < states.size(); x++) {
                            if (states.get(x).getText().equals(lines.get(z).getnState()) && states.get(a).getText().equals(lines.get(z).getfState())) {
                                int xbegin = (int) states.get(a).getBounds().getX();
                                int ybegin = (int) states.get(a).getBounds().getY();
                                int xend = (int) states.get(x).getBounds().getX();
                                int yend = (int) states.get(x).getBounds().getY();
                                if (xbegin > xend) {
                                    Path2D.Double rect = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend, yend, 10, 7));
                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend + 30, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect.getBounds().x, rect.getBounds().y, rect.getBounds().width + 20, rect.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                } else {
                                    if (xend - xbegin < 75) {
                                        xbegin = xbegin - 20;
                                        xend = xend - 20;
                                    }
                                    xbegin = xbegin + 5;
                                    ybegin = ybegin + 25;
                                    xend = xend + 5;
                                    yend = yend + 25;
                                    Path2D.Double rect = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7));
                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect.getBounds().x, rect.getBounds().y, rect.getBounds().width + 20, rect.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                }

                            }
                        }
                    } else if (lines.get(z).getnState().equals(states.get(a).getText()) && !lines.get(z).getfState().equals(lines.get(z).getnState())) {
                        transition.get(z).setIcon(null);
                        for (int x = 0; x < states.size(); x++) {
                            if (states.get(x).getText().equals(lines.get(z).getfState()) && states.get(a).getText().equals(lines.get(z).getnState())) {
                                int xend = (int) states.get(a).getBounds().getX();
                                int yend = (int) states.get(a).getBounds().getY();
                                int xbegin = (int) states.get(x).getBounds().getX();
                                int ybegin = (int) states.get(x).getBounds().getY();
                                if (xbegin > xend) {
                                    Path2D.Double rect2 = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend, yend, 10, 7));

                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend + 30, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect2.getBounds().x, rect2.getBounds().y, rect2.getBounds().width + 20, rect2.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                } else {
                                    if (xend - xbegin < 75) {
                                        xbegin = xbegin + 20;
                                        xend = xend + 20;
                                    }
                                    xbegin = xbegin + 5;
                                    ybegin = ybegin + 25;
                                    xend = xend + 5;
                                    yend = yend + 25;
                                    Path2D.Double rect2 = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7));
                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect2.getBounds().x, rect2.getBounds().y, rect2.getBounds().width + 20, rect2.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                }
                            }
                        }
          public static Path2D.Double createArrowForLine(
        int fromPointx,
        int fromPointy,
        double rotationDeg,
        double length,
        double wingsAngleDeg) {

    double ax = fromPointx;
    double ay = fromPointy;

    double radB = Math.toRadians(-rotationDeg + wingsAngleDeg);
    double radC = Math.toRadians(-rotationDeg - wingsAngleDeg);

    Path2D resultPath = new Path2D.Double();
    resultPath.moveTo(length * Math.cos(radB) + ax, length * Math.sin(radB) + ay);
    resultPath.lineTo(ax, ay);
    resultPath.lineTo(length * Math.cos(radC) + ax, length * Math.sin(radC) + ay);
    return (Path2D.Double) resultPath;
}

1 个答案:

答案 0 :(得分:4)

虽然问题中有一些小问题,而且到目前为止提供的代码看起来有问题,但现在问题的核心是非常有趣的......

解决这个问题有不同的选择。从您目前提供的图像来看,它们看起来总是具有相同的大小,这使得事情变得更加简单。对于具有不同大小的圆,您必须在所需方向上计算圆的切线,相互考虑另一个圆的半径。当然,这是可能的,但有点不那么重要。

如果你有同样大小的圈子,你可以

  • 计算两个圆心的差异
  • 除以距离,以获得(标准化的)方向
  • 将此方向旋转90°
  • 将旋转的方向矢量按半径
  • 缩放
  • 将缩放和旋转的矢量添加到圆心

这将产生这样一条线的一个端点。大约90°的旋转可以在顺时针方向上进行一次,在逆时针方向上进行一次,以分别获得该线的“上”和“下”端点。

LinesAtCircle

图像已使用EDIT更新,请参阅下文

实际计算是在以下MCVEcomputeLine方法中完成的。请注意,此示例使用“简单”方法,虽然它使用的是大小略有不同的圆圈。效果是,当两个圆的大小之间的差异太大时(基本上与圆之间的距离相比),那么这些线可能与圆相交。但解决方案应该是简单性和普遍适用性之间的合理权衡。特别是对于大小相等的圆圈,根本就没有交叉点。

使用EDIT更新了代码,见下文

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

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

public class LinesAtCirclesTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {

            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel linesAtCirclesTestPanel = new LinesAtCirclesTestPanel(); 
        f.getContentPane().add(linesAtCirclesTestPanel);
        f.setSize(400,400);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class LinesAtCirclesTestPanel extends JPanel 
    implements MouseListener, MouseMotionListener
{
    private Point2D draggedCenter;
    private List<Point2D> centers = new ArrayList<Point2D>();
    private List<Double> radii = new ArrayList<Double>();

    public LinesAtCirclesTestPanel()
    {
        addMouseListener(this);
        addMouseMotionListener(this);

        addCircle(100, 100, 30);
        addCircle(200, 300, 50);
        addCircle(300, 200, 40);
    }

    private void addCircle(double x, double y, double radius)
    {
        centers.add(new Point2D.Double(x,y));
        radii.add(radius);
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);

        for (int i=0; i<centers.size(); i++)
        {
            Point2D center0 = centers.get(i);
            double radius0 = radii.get(i);

            Shape ellipse = new Ellipse2D.Double(
                center0.getX() - radius0, center0.getY() - radius0, 
                radius0 + radius0, radius0 + radius0);

            g.setColor(Color.LIGHT_GRAY);
            g.fill(ellipse);
            g.setColor(Color.BLACK);
            g.draw(ellipse);
        }

        g.setColor(Color.RED);
        for (int i=0; i<centers.size() - 1; i++)
        {
            Point2D center0 = centers.get(i);
            double radius0 = radii.get(i);
            Point2D center1 = centers.get(i+1);
            double radius1 = radii.get(i+1);

            g.draw(createArrow(computeLine(center0, radius0, center1, radius1, true)));
            g.draw(createArrow(computeLine(center0, radius0, center1, radius1, false)));
        }
    }

    private static Shape createArrow(Line2D line)
    {
        double dx = line.getX2() - line.getX1();
        double dy = line.getY2() - line.getY1();
        double angleToX = Math.atan2(dy, dx);
        final double angleRad = Math.toRadians(30);
        final double headLength = 20.0f;
        double dxL = Math.cos(Math.PI + angleToX + angleRad) * headLength;
        double dyL = Math.sin(Math.PI + angleToX + angleRad) * headLength;
        double dxR = Math.cos(Math.PI + angleToX - angleRad) * headLength;
        double dyR = Math.sin(Math.PI + angleToX - angleRad) * headLength;
        Path2D arrow = new Path2D.Double();
        arrow.moveTo(line.getX1(), line.getY1());
        arrow.lineTo(line.getX2(), line.getY2());
        arrow.lineTo(line.getX2() + dxL, line.getY2() + dyL);
        arrow.moveTo(line.getX2(), line.getY2());
        arrow.lineTo(line.getX2() + dxR, line.getY2() + dyR);
        return arrow;
    }

    private static Line2D computeLine(
        Point2D center0, double radius0,
        Point2D center1, double radius1, 
        boolean upper)
    {
        double dx = center1.getX() - center0.getX();
        double dy = center1.getY() - center0.getY();
        double invLength = 1.0 / Math.hypot(dx, dy);
        double dirX = dx * invLength;
        double dirY = dy * invLength;

        double rotDirX = dirY;
        double rotDirY = -dirX;
        if (upper)
        {
            rotDirX = -dirY;
            rotDirY = dirX;
        }

        double x0 = center0.getX() + rotDirX * radius0;
        double y0 = center0.getY() + rotDirY * radius0;

        double x1 = center1.getX() + rotDirX * radius1;
        double y1 = center1.getY() + rotDirY * radius1;

        if (upper)
        {
            return new Line2D.Double(x1, y1, x0, y0);
        }
        return new Line2D.Double(x0, y0, x1, y1);
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        draggedCenter = null;
        for (int i=0; i<centers.size(); i++)
        {
            Point2D center = centers.get(i);
            double radius = radii.get(i);
            if (e.getPoint().distance(center) < radius)
            {
                draggedCenter = center;
            }
        }
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        draggedCenter = null;
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
        if (draggedCenter == null)
        {
            return;
        }
        draggedCenter.setLocation(e.getPoint());
        repaint();
    }



    @Override
    public void mouseMoved(MouseEvent e)
    {
        // Not used
    }

    @Override
    public void mouseClicked(MouseEvent e)
    {
        // Not used
    }

    @Override
    public void mouseEntered(MouseEvent e)
    {
        // Not used
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
        // Not used
    }
}
  

编辑回应评论:

原始代码计算Line2D个对象。在最简单的情况下,从一条线创建箭头基本上是通过一些三角函数完成的,并且在Web上存在许多资源。

在回应评论时,我将示例扩展为显示简单的箭头,如上图所示。

然而,当仔细研究这个时,可能会注意到这样一个箭头的几个自由度:

  • 头部长度应该是绝对的还是相对于箭头?
  • 头部宽度应该是绝对的还是相对于箭头?
  • (或者:箭头的角度应该是多少?)
  • 箭头应该填充还是由线组成?
  • 箭头的“主干”应该是单线还是轮廓形状?
  • 行李箱的宽度应该是多少?
  • ...

为了涵盖其中一些自由度,我前段时间创建了一个ArrowCreator课程,并且还有一个sample显示了如何使用它。