代码之家  ›  专栏  ›  技术社区  ›  Mark Maxham

计算圆和三角形的相交面积?

  •  18
  • Mark Maxham  · 技术社区  · 17 年前

    如何计算三角形(指定为三(X,Y)对)和圆(X,Y,R)之间的相交面积?我做了一些搜索,但没有结果。这是为了工作,不是为了上学。:)

    在C#中看起来像这样:

    struct { PointF vert[3]; } Triangle;
    struct { PointF center; float radius; } Circle;
    
    // returns the area of intersection, e.g.:
    // if the circle contains the triangle, return area of triangle
    // if the triangle contains the circle, return area of circle
    // if partial intersection, figure that out
    // if no intersection, return 0
    double AreaOfIntersection(Triangle t, Circle c)
    {
     ...
    }
    
    11 回复  |  直到 12 年前
        1
  •  37
  •   Community Mohan Dere    9 年前

    首先,我将提醒我们如何找到多边形的面积。一旦我们这样做了,找到多边形和圆之间的交点的算法应该很容易理解。

    如何求多边形的面积

    让我们看看三角形的情况,因为所有基本逻辑都出现在那里。假设我们有一个顶点为(x1,y1),(x2,y2)和(x3,y3)的三角形,逆时针旋转三角形,如下图所示: triangleFigure

    然后你可以用公式计算面积

    A=(x1 y2+x2 y3+x3 y1-x2y1-x3 y2-x1y3)/2。

    要了解这个公式为什么有效,让我们重新排列它,使其处于形式中

    A=(x1y2-x2y1)/2+(x2y3-x3y2)/2+(x3y1-x1y3)/2。

    enter image description here

    如果不清楚绿色区域的面积是否确实为(x1 y2-x2 y1)/2,则读取 this .

    enter image description here

    第三个区域如下图所示。这次该区域为负数

    enter image description here

    把这三个加起来,我们得到了下面的图片

    enter image description here

    我们看到三角形外的绿色区域被红色区域抵消,因此净面积就是三角形的面积,这说明了为什么我们的公式在这种情况下是正确的。

    我上面所说的是关于为什么面积公式是正确的直观解释。更严格的解释是,当我们从一条边计算面积时,我们得到的面积与积分r^2d/2得到的面积相同,因此我们有效地积分了多边形边界周围的r^2d/2,根据斯托克斯定理,这给出了与在多边形边界区域上积分rdrd相同的结果。由于在多边形所包围的区域上积分rdrd会得到面积,因此我们得出结论,我们的程序必须正确地给出面积。

    圆与多边形相交的面积

    现在,让我们讨论如何找到半径为R的圆与多边形的交点区域,如下图所示:

    enter image description here

    我们对寻找绿色区域的面积感兴趣。就像单个多边形的情况一样,我们可以将计算分解为为为多边形的每一侧找到一个面积,然后将这些面积相加。

    我们的第一个区域如下所示: enter image description here

    第二个区域看起来像 enter image description here

    第三个领域是 enter image description here

    同样,在我们的案例中,前两个方面是积极的,而第三个方面是消极的。希望取消的结果会好起来,这样的净面积确实是我们感兴趣的领域。让我看看。

    enter image description here

    事实上,这些领域的总和将是我们感兴趣的领域。

    再一次,我们可以更严格地解释为什么会这样。设I为交点定义的区域,P为多边形。从前面的讨论中,我们知道我们想要计算I边界附近r^2d/2的积分。然而,这很难做到,因为它需要找到交点。

    我们在多边形上做了一个积分。我们在多边形的边界上积分max(r,r)^2d/2。为了了解为什么这会给出正确的答案,让我们定义一个函数,它将极坐标(r,)中的一个点取为点(max(r,r),)。引用(r)=max(r,r)和()=的坐标函数应该不会混淆。然后我们做的是在多边形的边界上积分(r)^2d/2。

    另一方面,由于()=,这与在多边形边界上积分(r)^2 d()/2是相同的。

    现在做一个变量的改变,我们发现如果我们在(P)的边界上积分r^2d/2,我们会得到相同的答案,其中(P)是P下的图像。

    再次使用斯托克斯定理,我们知道在(P)的边界上对r^2d/2积分得到(P)的面积。换句话说,它给出了与积分dxdy到(P)相同的答案。

    再次使用变量的变化,我们知道在(P)上积分dxdy与在P上积分Jdxdy是一样的,其中J是的雅可比矩阵。

    因此,我们计算的实际上是交叉点的面积。

    现在我们相对确定我们在概念上知道如何找到面积,让我们更具体地讨论如何计算单个线段的贡献。让我们先看一下我称之为“标准几何”的一段。如下所示。

    enter image description here

    在标准几何图形中,边从左到右水平移动。它用三个数字描述:席,X坐标,边缘开始,XF,边缘的X坐标,Y,边缘的Y坐标。

    现在我们看到如果| y |<R、 如图所示,边将在点(-xint,y)和(xint,y)处与圆相交,其中xint=(R^2-y^2)^(1/2)。然后,我们需要计算的面积被分成图中标记的三部分。为了得到区域1和3的面积,我们可以使用arctan获得各个点的角度,然后将面积等于R^2/2。例如,我们设置i=atan2(y,xi)和l=atan2(y,-xint)。那么区域1的面积是R^2(l-i)/2。我们同样可以得到区域3的面积。

    区域2的面积就是三角形的面积。然而,我们必须小心这个标志。我们希望显示的面积是正的,所以我们会说面积是-(xint-(-xint))y/2。

    另一件要牢记的是,一般来说,XI不一定要小于-XNT,XF不一定要大于席特。

    另一个要考虑的情况是Y & gt;R.这种情况更简单,因为只有一件类似于图中的区域1。

    现在我们知道了如何从标准几何体中的边计算面积,剩下的唯一要做的就是描述如何将任何边转换为标准几何体。

    但这只是一个简单的坐标变化。给定一些初始顶点vi和最终顶点vf,新的x单位向量将是从vi指向vf的单位向量。然后,席席只是从中心的圆心点向X的位移,XF是XI加上VI和VF之间的距离。同时,y由x的楔形积和vi从圆心的位移给出。

    密码

    首先,既然我们是和圆圈一起工作的,我们应该有一个圆圈课

    public class Circle {
    
        final Point2D center;
        final double radius;
    
        public Circle(double x, double y, double radius) {
            center = new Point2D.Double(x, y);
            this.radius = radius;
        }
    
        public Circle(Point2D.Double center, double radius) {
            this(center.getX(), center.getY(), radius);
        }
    
        public Point2D getCenter() {
            return new Point2D.Double(getCenterX(), getCenterY());
        }
    
        public double getCenterX() {
            return center.getX();
        }
    
        public double getCenterY() {
            return center.getY();
        }
    
        public double getRadius() {
            return radius;
        }
    
    }
    

    对于多边形,我将使用java的 Shape 形状 PathIterator 我可以用它来迭代多边形的边。

    现在开始实际工作。我将把遍历边、将边放入标准几何体等的逻辑与完成后计算面积的逻辑分开。这样做的原因是,您将来可能希望计算除区域之外的其他内容,并且希望能够重用代码来处理遍历边缘的问题。

    所以我有一个泛型类,它计算类的一些属性 T 关于我们的多边形圆交点。

    public abstract class CircleShapeIntersectionFinder<T> {

    它有三种静态方法可以帮助计算几何体:

    private static double[] displacment2D(final double[] initialPoint, final double[] finalPoint) {
        return new double[]{finalPoint[0] - initialPoint[0], finalPoint[1] - initialPoint[1]};
    }
    
    private static double wedgeProduct2D(final double[] firstFactor, final double[] secondFactor) {
        return firstFactor[0] * secondFactor[1] - firstFactor[1] * secondFactor[0];
    }
    
    static private double dotProduct2D(final double[] firstFactor, final double[] secondFactor) {
        return firstFactor[0] * secondFactor[0] + firstFactor[1] * secondFactor[1];
    }
    

    有两个实例字段,一个 Circle 它只保留了一个圆圈的副本 currentSquareRadius ,它保留了一个方形半径的副本。这可能看起来很奇怪,但我使用的类实际上是用来查找整个圆形多边形交点集合的区域的。这就是为什么我将其中一个圆圈称为“当前”。

    private Circle currentCircle;
    private double currentSquareRadius;
    

    接下来是计算我们要计算的内容的方法:

    public final T computeValue(Circle circle, Shape shape) {
        initialize();
        processCircleShape(circle, shape);
        return getValue();
    }
    

    initialize() getValue() 这些都是抽象的。 初始化() 将保持总面积为零的变量设置为 我会直接返回该区域。定义 processCircleShape

    private void processCircleShape(Circle circle, final Shape cellBoundaryPolygon) {
        initializeForNewCirclePrivate(circle);
        if (cellBoundaryPolygon == null) {
            return;
        }
        PathIterator boundaryPathIterator = cellBoundaryPolygon.getPathIterator(null);
        double[] firstVertex = new double[2];
        double[] oldVertex = new double[2];
        double[] newVertex = new double[2];
        int segmentType = boundaryPathIterator.currentSegment(firstVertex);
        if (segmentType != PathIterator.SEG_MOVETO) {
            throw new AssertionError();
        }
        System.arraycopy(firstVertex, 0, newVertex, 0, 2);
        boundaryPathIterator.next();
        System.arraycopy(newVertex, 0, oldVertex, 0, 2);
        segmentType = boundaryPathIterator.currentSegment(newVertex);
        while (segmentType != PathIterator.SEG_CLOSE) {
            processSegment(oldVertex, newVertex);
            boundaryPathIterator.next();
            System.arraycopy(newVertex, 0, oldVertex, 0, 2);
            segmentType = boundaryPathIterator.currentSegment(newVertex);
        }
        processSegment(newVertex, firstVertex);
    }
    

    让我们看一看 initializeForNewCirclePrivate 迅速地此方法仅设置实例字段,并允许派生类存储圆的任何属性。它的定义是

    private void initializeForNewCirclePrivate(Circle circle) {
        currentCircle = circle;
        currentSquareRadius = currentCircle.getRadius() * currentCircle.getRadius();
        initializeForNewCircle(circle);
    }
    

    initializeForNewCircle 它是抽象的,一个实现是存储圆的半径,以避免进行平方根运算。无论如何,回到 圆形 . 打完电话 初始化FornewCirclePrivate ,我们检查多边形是否为 null (我将其解释为一个空多边形),如果它是,我们将返回 . 在这种情况下,我们的计算面积为零。如果多边形不是 无效的 然后我们得到 路径迭代器 多边形的形状。这一论据符合事实 getPathIterator 我调用的方法是可以应用于路径的仿射变换。不过我不想申请,所以我只是通过了 无效的 .

    接下来我宣布 double[] 路径迭代器 每个顶点只给我一次,所以在它给我最后一个顶点后,我必须返回,并用最后一个顶点和第一个顶点形成一条边。

    这个 currentSegment

    此方法的大多数其余代码都是与遍历顶点相关的无趣逻辑。重要的是,我调用while循环的每次迭代一次 processSegment 然后我打电话 进程段 在方法的末尾,再次处理将最后一个顶点连接到第一个顶点的边。

    让我们看一下 进程段 :

    private void processSegment(double[] initialVertex, double[] finalVertex) {
        double[] segmentDisplacement = displacment2D(initialVertex, finalVertex);
        if (segmentDisplacement[0] == 0 && segmentDisplacement[1] == 0) {
            return;
        }
        double segmentLength = Math.sqrt(dotProduct2D(segmentDisplacement, segmentDisplacement));
        double[] centerToInitialDisplacement = new double[]{initialVertex[0] - getCurrentCircle().getCenterX(), initialVertex[1] - getCurrentCircle().getCenterY()};
        final double leftX = dotProduct2D(centerToInitialDisplacement, segmentDisplacement) / segmentLength;
        final double rightX = leftX + segmentLength;
        final double y = wedgeProduct2D(segmentDisplacement, centerToInitialDisplacement) / segmentLength;
        processSegmentStandardGeometry(leftX, rightX, y);
    }
    

    在该方法中,我实现了将边转换为标准几何体的步骤,如上所述。首先我计算 segmentDisplacement ,从初始顶点到最终顶点的位移。这将定义标准几何图形的x轴。如果位移为零,我会提前返回。

    接下来我计算位移的长度,因为这是得到x单位向量所必需的。一旦我有了这些信息,我就计算从圆心到初始顶点的位移。这个的点积与 给我 leftX 我一直在叫席。然后 rightX ,我一直叫它xf,它只是 leftX + segmentLength . 最后,我做了楔形产品,以获得 y

    现在,我已经将问题转化为标准几何,它将很容易处理。这就是问题所在 processSegmentStandardGeometry 方法确实如此。让我们看看代码

    private void processSegmentStandardGeometry(double leftX, double rightX, double y) {
        if (y * y > getCurrentSquareRadius()) {
            processNonIntersectingRegion(leftX, rightX, y);
        } else {
            final double intersectionX = Math.sqrt(getCurrentSquareRadius() - y * y);
            if (leftX < -intersectionX) {
                final double leftRegionRightEndpoint = Math.min(-intersectionX, rightX);
                processNonIntersectingRegion(leftX, leftRegionRightEndpoint, y);
            }
            if (intersectionX < rightX) {
                final double rightRegionLeftEndpoint = Math.max(intersectionX, leftX);
                processNonIntersectingRegion(rightRegionLeftEndpoint, rightX, y);
            }
            final double middleRegionLeftEndpoint = Math.max(-intersectionX, leftX);
            final double middleRegionRightEndpoint = Math.min(intersectionX, rightX);
            final double middleRegionLength = Math.max(middleRegionRightEndpoint - middleRegionLeftEndpoint, 0);
            processIntersectingRegion(middleRegionLength, y);
        }
    }
    

    第一 if Y 足够小,边缘可以与圆相交。如果 Y 是大的,没有交叉的可能性,然后我调用方法来处理这种情况。否则,我将处理可能发生交叉的情况。

    如果可以相交,我计算相交的x坐标, intersectionX ,我将边缘分为三部分,分别对应于上面标准几何图形的区域1、2和3。首先我处理区域1。

    要处理区域1,我检查 左撇子 确实比 -intersectionX 否则就不会有区域1。如果有一个区域1,那么我需要知道它何时结束。它以最小值结束 -交叉 . 找到这些x坐标后,我处理这个非相交区域。

    我做了类似的事情来处理区域3。

    左撇子 确实要在两者之间加上一些区域 交叉 . 找到区域后,我只需要区域的长度和 Y

    现在让我们看一下 processNonIntersectingRegion

    private void processNonIntersectingRegion(double leftX, double rightX, double y) {
        final double initialTheta = Math.atan2(y, leftX);
        final double finalTheta = Math.atan2(y, rightX);
        double deltaTheta = finalTheta - initialTheta;
        if (deltaTheta < -Math.PI) {
            deltaTheta += 2 * Math.PI;
        } else if (deltaTheta > Math.PI) {
            deltaTheta -= 2 * Math.PI;
        }
        processNonIntersectingRegion(deltaTheta);
    }
    

    我只是用 atan2 左撇子 atan2 ,但这可能是不必要的,因为不连续性发生在180度或0度。然后我将角度的差异传递给一个抽象方法。最后,我们只有抽象方法和getter:

        protected abstract void initialize();
    
        protected abstract void initializeForNewCircle(Circle circle);
    
        protected abstract void processNonIntersectingRegion(double deltaTheta);
    
        protected abstract void processIntersectingRegion(double length, double y);
    
        protected abstract T getValue();
    
        protected final Circle getCurrentCircle() {
            return currentCircle;
        }
    
        protected final double getCurrentSquareRadius() {
            return currentSquareRadius;
        }
    
    }
    

    现在让我们看看扩展类, CircleAreaFinder

    public class CircleAreaFinder extends CircleShapeIntersectionFinder<Double> {
    
    public static double findAreaOfCircle(Circle circle, Shape shape) {
        CircleAreaFinder circleAreaFinder = new CircleAreaFinder();
        return circleAreaFinder.computeValue(circle, shape);
    }
    
    double area;
    
    @Override
    protected void initialize() {
        area = 0;
    }
    
    @Override
    protected void processNonIntersectingRegion(double deltaTheta) {
        area += getCurrentSquareRadius() * deltaTheta / 2;
    }
    
    @Override
    protected void processIntersectingRegion(double length, double y) {
        area -= length * y / 2;
    }
    
    @Override
    protected Double getValue() {
        return area;
    }
    
    @Override
    protected void initializeForNewCircle(Circle circle) {
    
    }
    

    }

    它有一个领域 area 跟踪该区域。 initialize 按预期将区域设置为零。当我们处理一条不相交的边时,我们将面积增加R^2/2,正如我们在上面得出的结论。对于相交边,我们将面积减少 y*length/2 . 这使得 Y 按照我们的决定,与积极的方面相对应。

    现在最妙的事情是,如果我们想跟踪周界,我们不需要做更多的工作。我定义了一个 AreaPerimeter 类别:

    public class AreaPerimeter {
    
        final double area;
        final double perimeter;
    
        public AreaPerimeter(double area, double perimeter) {
            this.area = area;
            this.perimeter = perimeter;
        }
    
        public double getArea() {
            return area;
        }
    
        public double getPerimeter() {
            return perimeter;
        }
    
    }
    

    现在我们只需要使用 面积周长 作为类型。

    public class CircleAreaPerimeterFinder extends CircleShapeIntersectionFinder<AreaPerimeter> {
    
        public static AreaPerimeter findAreaPerimeterOfCircle(Circle circle, Shape shape) {
            CircleAreaPerimeterFinder circleAreaPerimeterFinder = new CircleAreaPerimeterFinder();
            return circleAreaPerimeterFinder.computeValue(circle, shape);
        }
    
        double perimeter;
        double radius;
        CircleAreaFinder circleAreaFinder;
    
        @Override
        protected void initialize() {
            perimeter = 0;
            circleAreaFinder = new CircleAreaFinder();
        }
    
        @Override
        protected void initializeForNewCircle(Circle circle) {
            radius = Math.sqrt(getCurrentSquareRadius());
        }
    
        @Override
        protected void processNonIntersectingRegion(double deltaTheta) {
            perimeter += deltaTheta * radius;
            circleAreaFinder.processNonIntersectingRegion(deltaTheta);
        }
    
        @Override
        protected void processIntersectingRegion(double length, double y) {
            perimeter += Math.abs(length);
            circleAreaFinder.processIntersectingRegion(length, y);
        }
    
        @Override
        protected AreaPerimeter getValue() {
            return new AreaPerimeter(circleAreaFinder.getValue(), perimeter);
        }
    
    }
    

    我们有一个变量 perimeter radius 为了避免这种情况,我们必须打电话 Math.sqrt 很多,我们将面积的计算委托给 电路查找器 . 我们可以看出周长的公式很简单。

    CircleShapeIntersectionFinder

    private static double[] displacment2D(final double[] initialPoint, final double[] finalPoint) {
            return new double[]{finalPoint[0] - initialPoint[0], finalPoint[1] - initialPoint[1]};
        }
    
        private static double wedgeProduct2D(final double[] firstFactor, final double[] secondFactor) {
            return firstFactor[0] * secondFactor[1] - firstFactor[1] * secondFactor[0];
        }
    
        static private double dotProduct2D(final double[] firstFactor, final double[] secondFactor) {
            return firstFactor[0] * secondFactor[0] + firstFactor[1] * secondFactor[1];
        }
    
        private Circle currentCircle;
        private double currentSquareRadius;
    
        public final T computeValue(Circle circle, Shape shape) {
            initialize();
            processCircleShape(circle, shape);
            return getValue();
        }
    
        private void processCircleShape(Circle circle, final Shape cellBoundaryPolygon) {
            initializeForNewCirclePrivate(circle);
            if (cellBoundaryPolygon == null) {
                return;
            }
            PathIterator boundaryPathIterator = cellBoundaryPolygon.getPathIterator(null);
            double[] firstVertex = new double[2];
            double[] oldVertex = new double[2];
            double[] newVertex = new double[2];
            int segmentType = boundaryPathIterator.currentSegment(firstVertex);
            if (segmentType != PathIterator.SEG_MOVETO) {
                throw new AssertionError();
            }
            System.arraycopy(firstVertex, 0, newVertex, 0, 2);
            boundaryPathIterator.next();
            System.arraycopy(newVertex, 0, oldVertex, 0, 2);
            segmentType = boundaryPathIterator.currentSegment(newVertex);
            while (segmentType != PathIterator.SEG_CLOSE) {
                processSegment(oldVertex, newVertex);
                boundaryPathIterator.next();
                System.arraycopy(newVertex, 0, oldVertex, 0, 2);
                segmentType = boundaryPathIterator.currentSegment(newVertex);
            }
            processSegment(newVertex, firstVertex);
        }
    
        private void initializeForNewCirclePrivate(Circle circle) {
            currentCircle = circle;
            currentSquareRadius = currentCircle.getRadius() * currentCircle.getRadius();
            initializeForNewCircle(circle);
        }
    
        private void processSegment(double[] initialVertex, double[] finalVertex) {
            double[] segmentDisplacement = displacment2D(initialVertex, finalVertex);
            if (segmentDisplacement[0] == 0 && segmentDisplacement[1] == 0) {
                return;
            }
            double segmentLength = Math.sqrt(dotProduct2D(segmentDisplacement, segmentDisplacement));
            double[] centerToInitialDisplacement = new double[]{initialVertex[0] - getCurrentCircle().getCenterX(), initialVertex[1] - getCurrentCircle().getCenterY()};
            final double leftX = dotProduct2D(centerToInitialDisplacement, segmentDisplacement) / segmentLength;
            final double rightX = leftX + segmentLength;
            final double y = wedgeProduct2D(segmentDisplacement, centerToInitialDisplacement) / segmentLength;
            processSegmentStandardGeometry(leftX, rightX, y);
        }
    
        private void processSegmentStandardGeometry(double leftX, double rightX, double y) {
            if (y * y > getCurrentSquareRadius()) {
                processNonIntersectingRegion(leftX, rightX, y);
            } else {
                final double intersectionX = Math.sqrt(getCurrentSquareRadius() - y * y);
                if (leftX < -intersectionX) {
                    final double leftRegionRightEndpoint = Math.min(-intersectionX, rightX);
                    processNonIntersectingRegion(leftX, leftRegionRightEndpoint, y);
                }
                if (intersectionX < rightX) {
                    final double rightRegionLeftEndpoint = Math.max(intersectionX, leftX);
                    processNonIntersectingRegion(rightRegionLeftEndpoint, rightX, y);
                }
                final double middleRegionLeftEndpoint = Math.max(-intersectionX, leftX);
                final double middleRegionRightEndpoint = Math.min(intersectionX, rightX);
                final double middleRegionLength = Math.max(middleRegionRightEndpoint - middleRegionLeftEndpoint, 0);
                processIntersectingRegion(middleRegionLength, y);
            }
        }
    
        private void processNonIntersectingRegion(double leftX, double rightX, double y) {
            final double initialTheta = Math.atan2(y, leftX);
            final double finalTheta = Math.atan2(y, rightX);
            double deltaTheta = finalTheta - initialTheta;
            if (deltaTheta < -Math.PI) {
                deltaTheta += 2 * Math.PI;
            } else if (deltaTheta > Math.PI) {
                deltaTheta -= 2 * Math.PI;
            }
            processNonIntersectingRegion(deltaTheta);
        }
    
        protected abstract void initialize();
    
        protected abstract void initializeForNewCircle(Circle circle);
    
        protected abstract void processNonIntersectingRegion(double deltaTheta);
    
        protected abstract void processIntersectingRegion(double length, double y);
    
        protected abstract T getValue();
    
        protected final Circle getCurrentCircle() {
            return currentCircle;
        }
    
        protected final double getCurrentSquareRadius() {
            return currentSquareRadius;
        }
    

    无论如何,这就是我对算法的描述。我认为这很好,因为它是准确的,而且没有那么多的病例需要检查。

        2
  •  27
  •   Gareth Rees    11 年前

    如果你想要一个精确的解决方案(或者至少如你可以使用浮点算术那样精确),那么这将涉及大量的法律问题,因为要考虑的案例太多了。

    我统计了九种不同的情况(在下图中根据圆圈内三角形顶点的数量以及圆圈内相交或包含的三角形边的数量进行分类):

    Nine cases for intersection: 1, 2. no vertices, no edges; 3. no vertices, one edge; 4. no vertices, two edges; 5. no vertices, three edges; 6. one vertex, two edges; 7. one vertex, three edges; 8. two vertices, three edges; 9. three vertices, three edges.

    (然而,这种几何情况的枚举是众所周知的棘手问题,如果我遗漏了一两个,我一点也不会感到惊讶!)

    因此,方法是:

    1. 确定三角形的每个顶点是否在圆内。我假设你知道怎么做。

    2. 确定三角形的每条边是否与圆相交。(我写了一个方法 here ,或参阅任何计算几何书籍。)您需要计算交点(如果有),以便在步骤4中使用。

    3. 计算交叉点的面积。案例1、2和9很简单。在剩下的六种情况下,我画了虚线来展示如何将相交区域划分为三角形和三角形 circular segments 基于三角形的原始顶点和在步骤2中计算的交点。

    该算法将非常微妙,并且容易出现只影响其中一种情况的错误,因此请确保您有涵盖所有九种情况的测试情况(我建议也排列测试三角形的顶点)。请特别注意三角形的一个顶点位于圆边缘的情况。

    如果您不需要精确的解决方案,那么光栅化图形和计算交叉点的像素(正如其他几位受访者所建议的那样)似乎是一种更容易编码的方法,并且相应地不太容易出错。

        3
  •  2
  •   Victor Liu    16 年前

    code here 我写的,我认为这是正确的。查看底部附近的函数A。一般的方法是先提取出由圆外接的凸多边形,然后再对小圆帽进行处理。

        4
  •  1
  •   lc.    17 年前

    假设你说的是整数像素,而不是真像素,那么最简单的实现就是在三角形的每个像素上循环,然后根据圆心的半径检查距离。

    这不是一个可爱的公式,也不是特别快,但它确实完成了任务。

        5
  •  1
  •   Steven A. Lowe    17 年前

    computational geometry

    注意:这不是一个小问题,我希望这不是家庭作业;-)

        6
  •  1
  •   Community Mohan Dere    9 年前

    如果你有一个GPU,你可以使用 this 获取交点像素计数的技术。。

        7
  •  1
  •   okutane    17 年前

    我认为你们不应该把圆近似为一组三角形,相反,你们可以用多边形来近似它的形状。 朴素算法可以如下所示:

    1. 计算两个多边形(转换圆和三角形)的交点。
    2. 计算那个交点的平方。

    请阅读以下链接:
    Area of convex polygon
    Intersection of convex polygons

        8
  •  1
  •   Imran    17 年前

    由于您的形状是凸面的,因此可以使用蒙特卡罗面积估计。

    选择框中的随机点,并记录有多少落在圆中,以及有多少落在圆和三角形中。

    相交面积圆的面积*#圆中的点和三角形/#圆中的点

    注意:以下是确定点是否位于三角形中的方法: Barycentric coordinates

        9
  •  0
  •   Mark Ransom    17 年前

    你需要精确到什么程度?如果可以用更简单的形状近似圆,就可以简化问题。例如,将一个圆建模为一组在中心相交的非常窄的三角形并不困难。

        10
  •  0
  •   Nikhil    17 年前

    如果三角形的一条线段与圆相交,那么纯数学解并不难。一旦知道两点相交的时间,就可以使用距离公式来计算弦长。

    根据 these equations :

    ϑ = 2 sin⁻¹(0.5 c / r)
    A = 0.5 r² (ϑ - sin(ϑ))
    

    如果您只需要一个近似值,那么可能不值得这样做,因为它会对实际交叉口的外观做出一些假设。

        11
  •  0
  •   Crashworks    17 年前

    我的第一反应是变换一切,使圆以原点为中心,将三角形转换为极坐标,并求解三角形与圆的交点(或包围)。我还没有在纸上把它写下来,所以这只是一种预感。