【问题标题】:Render only the segment/area of a circle that intersects the main circle仅渲染与主圆相交的圆的段/区域
【发布时间】:2011-02-16 09:30:05
【问题描述】:

我绝对喜欢数学(或者你们大多数人会说的“数学”!),但我还没有做到知道这个问题的答案的程度。我有一个主圆,它可以在显示器上的任何 x 和 y 处有一个中心点。其他圆圈将随意在显示器周围移动,但在任何给定的渲染方法调用中,我不仅要渲染与主圆圈相交的圆圈,而且只渲染在主圆圈内可见的圆圈部分。一个类比是投射在现实生活对象上的阴影,我只想画出该对象被“照亮”的部分。

我希望最好在 Java 中执行此操作,但如果您有原始公式,将不胜感激。我想知道如何绘制形状并在 Java 中填充它,我确定折线上一定有一些变化,带有弧或其他东西?

非常感谢

【问题讨论】:

  • 你想如何渲染它?用点云或代表弧线的对象?您是否需要一个将候选人的坐标与参数联系起来的方程式?还是圆弧方程?
  • 只是片段的轻量级表示,因此本身不是可用的对象,但更多的是向用户显示一个圆圈位于主圆圈内。理想情况下,我想要测试列表中每个周围圆圈的方法,以查看它们是否首先与主圆圈重叠(布尔返回),然后返回实际 2d 段形状的方法会很棒,然后可以直接呈现。一厢情愿?!

标签: java intersection geometry


【解决方案1】:

AB成为2个intersection points(没有或者1个拦截点时可以忽略)。

然后计算circular line segmentAB之间的长度。

有了这些信息,你应该可以使用Graphics' drawArc(...) 方法绘制圆弧(如果我没记错的话……)。

编辑

嗯,你甚至不需要圆形线段的长度。我有线交点代码,所以我围绕它构建了一个小 GUI,您可以如何绘制/查看此类相交圆的 ARC(代码中有一些 cmets):

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

/**
 * @author: Bart Kiers
 */
public class GUI extends JFrame {

    private GUI() {
        super("Circle Intersection Demo");
        initGUI();
    }

    private void initGUI() {
        super.setSize(600, 640);
        super.setDefaultCloseOperation(EXIT_ON_CLOSE);
        super.setLayout(new BorderLayout(5, 5));

        final Grid grid = new Grid();

        grid.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                Point p = new Point(e.getX(), e.getY()).toCartesianPoint(grid.getWidth(), grid.getHeight());
                grid.showDraggedCircle(p);
            }
        });

        grid.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                Point p = new Point(e.getX(), e.getY()).toCartesianPoint(grid.getWidth(), grid.getHeight());
                grid.released(p);
            }

            @Override
            public void mousePressed(MouseEvent e) {
                Point p = new Point(e.getX(), e.getY()).toCartesianPoint(grid.getWidth(), grid.getHeight());
                grid.pressed(p);
            }
        });

        super.add(grid, BorderLayout.CENTER);
        super.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GUI();
            }
        });
    }

    private static class Grid extends JPanel {

        private Circle c1 = null;
        private Circle c2 = null;
        private Point screenClick = null;
        private Point currentPosition = null;

        public void released(Point p) {
            if (c1 == null || c2 != null) {
                c1 = new Circle(screenClick, screenClick.distance(p));
                c2 = null;
            } else {
                c2 = new Circle(screenClick, screenClick.distance(p));
            }
            screenClick = null;
            repaint();
        }

        public void pressed(Point p) {
            if(c1 != null && c2 != null) {
                c1 = null;
                c2 = null;
            }
            screenClick = p;
            repaint();
        }

        @Override
        public void paintComponent(Graphics g) {

            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, super.getWidth(), super.getHeight());

            final int W = super.getWidth();
            final int H = super.getHeight();
            g2d.setColor(Color.LIGHT_GRAY);
            g2d.drawLine(0, H / 2, W, H / 2); // x-axis
            g2d.drawLine(W / 2, 0, W / 2, H); // y-axis

            if (c1 != null) {
                g2d.setColor(Color.RED);
                c1.drawOn(g2d, W, H);
            }

            if (c2 != null) {
                g2d.setColor(Color.ORANGE);
                c2.drawOn(g2d, W, H);
            }

            if (screenClick != null && currentPosition != null) {
                g2d.setColor(Color.DARK_GRAY);
                g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
                Circle temp = new Circle(screenClick, screenClick.distance(currentPosition));
                temp.drawOn(g2d, W, H);
                currentPosition = null;
            }

            if (c1 != null && c2 != null) {

                g2d.setColor(Color.BLUE);
                g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
                Point[] ips = c1.intersections(c2);
                for (Point ip : ips) {
                    ip.drawOn(g, W, H);
                }
                g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
                if (ips.length == 2) {
                    g2d.setStroke(new BasicStroke(10.0f));
                    c1.highlightArc(g2d, ips[0], ips[1], W, H);
                }
            }

            g2d.dispose();
        }

        public void showDraggedCircle(Point p) {
            currentPosition = p;
            repaint();
        }
    }

    private static class Circle {

        public final Point center;
        public final double radius;

        public Circle(Point center, double radius) {
            this.center = center;
            this.radius = radius;
        }

        public void drawOn(Graphics g, int width, int height) {
            // translate Cartesian(x,y) to Screen(x,y)
            Point screenP = center.toScreenPoint(width, height);
            int r = (int) Math.rint(radius);
            g.drawOval((int) screenP.x - r, (int) screenP.y - r, r + r, r + r);

            // draw the center
            Point screenCenter = center.toScreenPoint(width, height);
            r = 4;
            g.drawOval((int) screenCenter.x - r, (int) screenCenter.y - r, r + r, r + r);
        }

        public void highlightArc(Graphics2D g2d, Point p1, Point p2, int width, int height) {

            double a = center.degrees(p1);
            double b = center.degrees(p2);

            // translate Cartesian(x,y) to Screen(x,y)
            Point screenP = center.toScreenPoint(width, height);
            int r = (int) Math.rint(radius);

            // find the point to start drawing our arc
            double start = Math.abs(a - b) < 180 ? Math.min(a, b) : Math.max(a, b);

            // find the minimum angle to go from `start`-angle to the other angle
            double extent = Math.abs(a - b) < 180 ? Math.abs(a - b) : 360 - Math.abs(a - b);

            // draw the arc
            g2d.draw(new Arc2D.Double((int) screenP.x - r, (int) screenP.y - r, r + r, r + r, start, extent, Arc2D.OPEN));
        }

        public Point[] intersections(Circle that) {

            // see: http://mathworld.wolfram.com/Circle-CircleIntersection.html
            double d = this.center.distance(that.center);
            double d1 = ((this.radius * this.radius) - (that.radius * that.radius) + (d * d)) / (2 * d);
            double h = Math.sqrt((this.radius * this.radius) - (d1 * d1));
            double x3 = this.center.x + (d1 * (that.center.x - this.center.x)) / d;
            double y3 = this.center.y + (d1 * (that.center.y - this.center.y)) / d;
            double x4_i = x3 + (h * (that.center.y - this.center.y)) / d;
            double y4_i = y3 - (h * (that.center.x - this.center.x)) / d;
            double x4_ii = x3 - (h * (that.center.y - this.center.y)) / d;
            double y4_ii = y3 + (h * (that.center.x - this.center.x)) / d;

            if (Double.isNaN(x4_i)) {
                // no intersections
                return new Point[0];
            }

            // create the intersection points
            Point i1 = new Point(x4_i, y4_i);
            Point i2 = new Point(x4_ii, y4_ii);

            if (i1.distance(i2) < 0.0000000001) {
                // i1 and i2 are (more or less) the same: a single intersection
                return new Point[]{i1};
            }

            // two unique intersections
            return new Point[]{i1, i2};
        }

        @Override
        public String toString() {
            return String.format("{center=%s, radius=%.2f}", center, radius);
        }
    }

    private static class Point {

        public final double x;
        public final double y;

        public Point(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public double degrees(Point that) {
            double deg = Math.toDegrees(Math.atan2(that.y - this.y, that.x - this.x));
            return deg < 0.0 ? deg + 360 : deg;
        }

        public double distance(Point that) {
            double dX = this.x - that.x;
            double dY = this.y - that.y;
            return Math.sqrt(dX * dX + dY * dY);
        }

        public void drawOn(Graphics g, int width, int height) {
            // translate Cartesian(x,y) to Screen(x,y)
            Point screenP = toScreenPoint(width, height);
            int r = 7;
            g.fillOval((int) screenP.x - r, (int) screenP.y - r, r + r, r + r);
        }

        public Point toCartesianPoint(int width, int height) {
            double xCart = x - (width / 2);
            double yCart = -(y - (height / 2));
            return new Point(xCart, yCart);
        }

        public Point toScreenPoint(int width, int height) {
            double screenX = x + (width / 2);
            double screenY = -(y - (height / 2));
            return new Point(screenX, screenY);
        }

        @Override
        public String toString() {
            return String.format("(%.2f,%.2f)", x, y);
        }
    }
}

如果您启动上面的 GUI,然后在文本框中输入 100 0 130 -80 55 180 并回车,您将看到以下内容:...

更改了代码,以便可以通过按住并拖动鼠标来绘制圆圈。截图:

【讨论】:

  • 巴特真的很有帮助。感谢您回到这里并付出如此多的努力。
  • @Greenhouse Gases,没问题。
【解决方案2】:

假设你知道两个圆的中心点和半径:

  1. 计算圆相交的点。这可以通过三角函数轻松完成。可能没有交点(中心点之间的距离长于半径之和,在您的情况下可忽略),一个点(中心点之间的距离等于半径之和,可忽略),或两个。特殊情况:圆圈相同,或者移动的圆圈更小,完全在主圆圈内。

  2. 如果有两个交点:取运动圆的中心点,在这些点之间画一条圆弧。

(我没有代码给你,但既然你喜欢数学……;-)

【讨论】:

    猜你喜欢
    • 2013-07-02
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 2014-05-01
    • 1970-01-01
    • 2015-08-30
    • 2021-10-09
    • 1970-01-01
    相关资源
    最近更新 更多