当前位置: 首页 > 面试题库 >

用线连接两个圆圈

龙新荣
2023-03-14
问题内容

我正在JPanel中绘制两个形状(圆形),我需要用一条
线将它们连接起来。我这样做是通过简单地获得圆的中点并
相互连接来实现的。

问题在于,现在我需要制作单向线,该单向线
的末尾带有“箭头”,以指出线的前进方向。所以现在我不能
使用圆的中点,因为我需要从
边界到边界相互连接,因此“箭头”可以正确显示。

在我上一次尝试的结果是,没有任何好处:

PS:在屏幕截图中,我并不是为了看到
直线的确切位置而填充圆圈,而是通常会填充它。

我在计算
开始/结束线条所需的边界的确切位置时遇到了麻烦。有人对如何执行此操作有任何想法吗?

编辑:圆是可移动的,它们可以在任何位置,因此该线
在任何情况下都可以工作。


问题答案:

好的,基本上,我们可以将问题分解为基本问题:

  1. 获取两个圆之间的角度
  2. 从这个圆的圆周到另一个圆的圆周画一条线
    这两个问题都不难解决(而且花在
    互联网上的任何时间都会提供解决方案-因为那是我从中获得的;))

因此,两点之间的角度可以使用类似…的方法来计算

protected double angleBetween(Point2D from, Point2D to) {
    double x = from.getX();
    double y = from.getY();

    // This is the difference between the anchor point
    // and the mouse.  Its important that this is done
    // within the local coordinate space of the component,
    // this means either the MouseMotionListener needs to
    // be registered to the component itself (preferably)
    // or the mouse coordinates need to be converted into
    // local coordinate space
    double deltaX = to.getX() - x;
    double deltaY = to.getY() - y;

    // Calculate the angle...
    // This is our "0" or start angle..
    double rotation = -Math.atan2(deltaX, deltaY);
    rotation = Math.toRadians(Math.toDegrees(rotation) + 180);

    return rotation;
}

And the point on a circle can be calculated using something like…

protected Point2D getPointOnCircle(Point2D center, double radians, double radius) {

    double x = center.getX();
    double y = center.getY();

    radians = radians - Math.toRadians(90.0); // 0 becomes the top
    // Calculate the outter point of the line
    double xPosy = Math.round((float) (x + Math.cos(radians) * radius));
    double yPosy = Math.round((float) (y + Math.sin(radians) * radius));

    return new Point2D.Double(xPosy, yPosy);

}

请注意,对结果进行了一些内部修改,以允许数学解决方案与GraphicsAPI绘制圆圈的方式之间的差异

好的,您说的这么多,对我有什么帮助?好吧,我实际上很棒。

您可以计算到的圆之间的角度(来回之间,也许
可以简单地将一个角度反演,但是我有可用的计算方法,所以我
使用了它)。由此,您可以计算线将
在其上相交的每个圆的点,然后只需要绘制它,就像…

double from = angleBetween(circle1, circle2);
double to = angleBetween(circle2, circle1);

Point2D pointFrom = getPointOnCircle(circle1, from);
Point2D pointTo = getPointOnCircle(circle2, to);

Line2D line = new Line2D.Double(pointFrom, pointTo);
g2d.draw(line);

因为我将许多计算精简为公用属性,所以提供了测试代码作为可运行示例。所有的
计算都是基于动态值,没有什么是真正的硬编码。对于例如,你可以改变大小和圆的位置和计算应该继续工作......

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Ellipse2D circle1;
        private Ellipse2D circle2;

        private Point2D drawTo;

        public TestPane() {
            circle1 = new Ellipse2D.Double(10, 10, 40, 40);
            circle2 = new Ellipse2D.Double(100, 150, 40, 40);

            //addMouseMotionListener(new MouseAdapter() {
            //  @Override
            //  public void mouseMoved(MouseEvent e) {
            //      drawTo = new Point2D.Double(e.getPoint().x, e.getPoint().y);
            //      repaint();
            //  }
            //});
        }

        protected Point2D center(Rectangle2D bounds) {
            return new Point2D.Double(bounds.getCenterX(), bounds.getCenterY());
        }

        protected double angleBetween(Shape from, Shape to) {
            return angleBetween(center(from.getBounds2D()), center(to.getBounds2D()));
        }

        protected double angleBetween(Point2D from, Point2D to) {
            double x = from.getX();
            double y = from.getY();

            // This is the difference between the anchor point
            // and the mouse.  Its important that this is done
            // within the local coordinate space of the component,
            // this means either the MouseMotionListener needs to
            // be registered to the component itself (preferably)
            // or the mouse coordinates need to be converted into
            // local coordinate space
            double deltaX = to.getX() - x;
            double deltaY = to.getY() - y;

            // Calculate the angle...
            // This is our "0" or start angle..
            double rotation = -Math.atan2(deltaX, deltaY);
            rotation = Math.toRadians(Math.toDegrees(rotation) + 180);

            return rotation;
        }

        protected Point2D getPointOnCircle(Shape shape, double radians) {
            Rectangle2D bounds = shape.getBounds();
//          Point2D point = new Point2D.Double(bounds.getX(), bounds.getY());
            Point2D point = center(bounds);
            return getPointOnCircle(point, radians, Math.max(bounds.getWidth(), bounds.getHeight()) / 2d);
        }

        protected Point2D getPointOnCircle(Point2D center, double radians, double radius) {

            double x = center.getX();
            double y = center.getY();

            radians = radians - Math.toRadians(90.0); // 0 becomes th?e top
            // Calculate the outter point of the line
            double xPosy = Math.round((float) (x + Math.cos(radians) * radius));
            double yPosy = Math.round((float) (y + Math.sin(radians) * radius));

            return new Point2D.Double(xPosy, yPosy);

        }

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

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.draw(circle1);
            g2d.draw(circle2);

            // This was used for testing, it will draw a line from circle1 to the
            // drawTo point, which, if enabled, is the last known position of the
            // mouse
            //if (drawTo != null) {
            //  Point2D pointFrom = center(circle1.getBounds2D());
            //  g2d.setColor(Color.RED);
            //  g2d.draw(new Line2D.Double(drawTo, pointFrom));
            //
            //  double from = angleBetween(pointFrom, drawTo);
            //  System.out.println(NumberFormat.getNumberInstance().format(Math.toDegrees(from)));
            //
            //  Point2D poc = getPointOnCircle(circle1, from);
            //  g2d.setColor(Color.BLUE);
            //  g2d.draw(new Line2D.Double(poc, drawTo));
            //}

            double from = angleBetween(circle1, circle2);
            double to = angleBetween(circle2, circle1);

            Point2D pointFrom = getPointOnCircle(circle1, from);
            Point2D pointTo = getPointOnCircle(circle2, to);

            g2d.setColor(Color.RED);
            Line2D line = new Line2D.Double(pointFrom, pointTo);
            g2d.draw(line);
            g2d.dispose();
        }

    }

}

Arrow head

The intention is to treat the arrow head as a separate entity. The reason is
because it’s just simpler that way, you also get a more consistent result
regardless of the distance between the objects.

So, to start with, I define a new Shape

public class ArrowHead extends Path2D.Double {

    public ArrowHead() {
        int size = 10;
        moveTo(0, size);
        lineTo(size / 2, 0);
        lineTo(size, size);
    }

}

Pretty simple really. It just creates two lines, which point up, meeting in
the middle of the available space.

Then in the paintComponent method, we perform some AffineTransform magic
using the available information we already have, namely

  • The point on our target circles circumference
  • The angle to our target circle

And transform the ArrowHead shape…

g2d.setColor(Color.MAGENTA);
ArrowHead arrowHead = new ArrowHead();
AffineTransform at = AffineTransform.getTranslateInstance(
                pointTo.getX() - (arrowHead.getBounds2D().getWidth() / 2d), 
                pointTo.getY());
at.rotate(from, arrowHead.getBounds2D().getCenterX(), 0);
arrowHead.transform(at);
g2d.draw(arrowHead);

Pointy

Now, because I’m crazy, I also tested the code by drawing an arrow pointing at
our source circle, just to prove that the calculations would work…

// This just proofs that the previous calculations weren't a fluke
// and that the arrow can be painted pointing to the source object as well
g2d.setColor(Color.GREEN);
arrowHead = new ArrowHead();
at = AffineTransform.getTranslateInstance(
                pointFrom.getX() - (arrowHead.getBounds2D().getWidth() / 2d), 
                pointFrom.getY());
at.rotate(to, arrowHead.getBounds2D().getCenterX(), 0);
arrowHead.transform(at);
g2d.draw(arrowHead);


 类似资料:
  • 问题内容: 我必须在CSS中实现以下圆圈和线条组合,而且我正在寻找有关如何有效实现这一点的指针。圆和线应如下所示: 我能够像这样实现圈子: 但是这些线条对我来说很难理解。 圆的大小根据是否为活动步而变化,连接圆的线的颜色也根据状态而变化。我将如何完成? 问题答案: 您可以使用伪元素和相邻的同级选择器(~)来实现此效果,而无需额外的标记:

  • 问题内容: 有简单的解决方案,可通过串联两个或java 。由于是经常使用的。是否有任何简单的方法来连接两个? 这是我的想法: 它可以工作,但实际上可以转换为,然后再次转换回。 问题答案: 您可以使用协力让这件事没有任何自动装箱拆箱或完成。这是它的外观。 请注意,返回,然后将其与另一个串联,然后再收集到数组中。 这是输出。 [1、34、3、1、5]

  • 我试图加入2 KTables。 合并功能非常简单,我只是将值从一个bean复制到另一个bean。 但由于某些原因,join函数在单个生成的记录上调用了两次。请参阅下面的流媒体/制作人配置 生产者配置- 接下来,我将提交每个流的单个记录。两个记录具有相同的密钥。我希望收到一条记录作为输出。 但是ValueJoiner触发了2次,我得到了2条相同的输出记录,而不是一条。在触发时间内-两个流中的值都存在

  • 问题内容: 我有两个表,如下所示: 我想列出参加活动17的所有人(包括学生和教师)的名字。无论如何,我可以获得以下结果: 无需创建新表(仅使用表达式或派生关系的嵌套)? 在actid上加入JOIN会得到如下结果: 我想我需要一种串联形式? 问题答案: 您可能(或可能不需要)对ID不唯一的内容进行处理,例如

  • EasyReact 的重点就是让节点之间的数据流动起来,所以连接节点是很重要的。 如何连接两个节点 两个节点是通过变换来连接的,在源码目录 EasyReact/Classes/Core/NodeTransforms 中我们默认实现了了很多的变换,你也可以通过继承 EZRTransform 类来实现自己的变换,一旦我们创建好一个变换后,就可以通过如下方式进行连接了: EZRMutableNode<N

  • 下面的代码片段是从JoinedStreams的javadoc复制的 这两个流仅基于一个键(通过< code>t =计算)进行连接 我会问我如何基于多个键进行连接,例如,one.a = two.a和