Java 圆与矩形的相交面积

Java 圆与矩形的相交面积,java,math,intersection,area,Java,Math,Intersection,Area,我正在寻找一种快速的方法来确定矩形和圆形之间的相交面积(我需要做数百万次这样的计算) 一个特殊的属性是,在所有情况下,圆和矩形始终有两个交点。也许您可以使用答案,其中询问圆和三角形之间的交点面积。将矩形拆分为两个三角形,并使用此处描述的算法 另一种方法是在两个交点之间画一条线,这会将您的区域分割为多边形(3条或4条边)和a,因为这两条边都可以更容易地找到库或自己进行计算。给定两个交点: 0个顶点位于圆内:圆的区域 1个顶点位于圆内:圆段和三角形的面积之和 XXXXX

我正在寻找一种快速的方法来确定矩形和圆形之间的相交面积(我需要做数百万次这样的计算)


一个特殊的属性是,在所有情况下,圆和矩形始终有两个交点。

也许您可以使用答案,其中询问圆和三角形之间的交点面积。将矩形拆分为两个三角形,并使用此处描述的算法


另一种方法是在两个交点之间画一条线,这会将您的区域分割为多边形(3条或4条边)和a,因为这两条边都可以更容易地找到库或自己进行计算。

给定两个交点:

0个顶点位于圆内:圆的区域

1个顶点位于圆内:圆段和三角形的面积之和

    XXXXX                   XXXXXXXXX
   X     X       Triangle ->X     _-X
  X       X                 X   _-  X 
  X    +--X--+              X _-   X <- Circular segment 
   X   | X   |              X-  XXX 
    XXXXX    |              XXXX
       |     | 
XXXXX XXXXXXXXX
X X三角形->X X-X
X X_uux-X
X+--X--+X u-X |--/X

X | X | |--/XX以下是如何计算圆心位于矩形外的圆和矩形之间的重叠面积。其他情况也可以归结为这个问题

面积可以通过积分圆方程y=sqrt[a^2-(x-h)^2]+k来计算,其中a是半径,(h,k)是圆心,以找到曲线下的面积。您可以使用计算机集成,将区域划分为许多小矩形并计算它们的总和,或者在这里使用闭合形式

这是一个实现上述概念的C#source。请注意,有一种特殊情况,指定的x位于圆的边界之外。我只是在这里使用了一个简单的解决方法(并不是在所有情况下都能得到正确的答案)

publicstaticvoid RunSnippet()
{
//测试代码
双a,h,k,x1,x2;
a=10;
h=4;
k=0;
x1=-100;
x2=100;
双r1=积分(x1,a,h,k);
双r2=积分(x2,a,h,k);
控制台写入线(r2-r1);
}
专用静态双积分(双x、双a、双h、双k)
{
双a0=a*a-(h-x)*(h-x);

如果(a0谢谢你的回答

我忘了提到面积估算已经足够了。 这就是为什么最后,在看了所有选项后,我选择了蒙特卡罗估计法,在圆圈中生成随机点,并测试它们是否在方框中

在我的例子中,这可能更有效。(我在圆上放置了一个网格,我必须测量属于每个网格单元的圆的比率。)


谢谢

以下是解决此问题的另一种方法:

public static bool IsIntersected(PointF circle, float radius, RectangleF rectangle)
{

        var rectangleCenter = new PointF((rectangle.X +  rectangle.Width / 2),
                                         (rectangle.Y + rectangle.Height / 2));

        var w = rectangle.Width  / 2;
        var h = rectangle.Height / 2;

        var dx = Math.Abs(circle.X - rectangleCenter.X);
        var dy = Math.Abs(circle.Y - rectangleCenter.Y);

        if (dx > (radius + w) || dy > (radius + h)) return false;


        var circleDistance = new PointF
                                 {
                                     X = Math.Abs(circle.X - rectangle.X - w),
                                     Y = Math.Abs(circle.Y - rectangle.Y - h)
                                 };


        if (circleDistance.X <= (w))
        {
            return true;
        }

        if (circleDistance.Y <= (h))
        {
            return true;
        }

        var cornerDistanceSq = Math.Pow(circleDistance.X - w, 2) + 
                    Math.Pow(circleDistance.Y - h, 2);

        return (cornerDistanceSq <= (Math.Pow(radius, 2)));
}
public static bool是相交的(PointF circle、float radius、RectangleF rectangle)
{
var rectangleCenter=新点F((矩形.X+矩形.Width/2),
(矩形Y+矩形高度/2));
var w=矩形。宽度/2;
var h=矩形。高度/2;
var dx=Math.Abs(circle.X-rectangleCenter.X);
var dy=Math.Abs(circle.Y-rectangleCenter.Y);
如果(dx>(半径+w)| dy>(半径+h))返回false;
var circleDistance=新点F
{
X=Math.Abs(circle.X-rectangle.X-w),
Y=Math.Abs(circle.Y-rectangle.Y-h)
};

if(circleDistance.X我希望对这样一个老问题给出一个答案不是坏事。我仔细研究了上述解决方案,并制定了一个类似于Daniels first答案的算法,但有点紧

简言之,假设整个区域在矩形中,减去外部半平面中的四个线段,然后在四个外部象限中添加任何区域,沿途丢弃一些无关紧要的情况

伪CDE(我的实际代码只有~12行..)

找到从圆心算起的有符号(负输出)标准化距离
对于每个无限延伸的矩形边缘线,
即。
d_1=(xcenter xleft)/r
d_2=(Y中心Y底部)/r
等
为了方便起见,在边缘周围排列1,2,3,4。如果矩形不是
与笛卡尔坐标对齐,此步骤更复杂,但
算法的其余部分是相同的
如果有d_i=1,则返回Pi r^2
这只剩下一个完全在外的情况:圆心在内
外部象限,与拐角的距离大于圆半径:
对于每个相邻的i,j(即i,j=1,2;2,3;3,4;4,1)
如果d_i-1
a_i=arcin(d_i)#保存a_i供下一步使用
面积-=r^2/2(π-2 a_i-sin(2 a_i))
注意,在这一点上,我们在四个外部区域中重复计算了面积
象限,因此请重新添加:
对于每个相邻的i,j
如果d_i<1,d_j<1,d_i^2+d_j^2<1
面积+=r^2/4(π-2αi-2αj-正弦(2αi)-正弦(2αj)+4αi-正弦(αj))
返回区
顺便说一句,平面象限中圆面积的最后一个公式很容易导出为圆段、两个直角三角形和一个矩形的和


享受。

我意识到这一问题不久前已经得到了回答,但我正在解决同样的问题,我找不到一个现成的可行解决方案。请注意,我的框是,OP没有完全指定。下面的解决方案是完全通用的,适用于任意数量的交叉口(不仅仅是两个交叉口)。请注意,如果长方体未对齐轴(但长方体仍具有直角,而非常规),则可以利用圆的优势,旋转所有对象的坐标,使长方体最终对齐轴,然后使用此代码

我想使用积分-这似乎是个好主意。让我们先写一个明显的公式来绘制一个圆:

x = center.x + cos(theta) * radius
y = center.y + sin(theta) * radius

^
|
|**###        **
| #*  #      *          * x
|#  *  #    *           # y
|#  *  #    *     
+-----------------------> theta
     *  #  *  # 
     *  #  *  #
      *  #*  #
       ***###
这很好,但我不知道
    XXXXX
   X  +--X+             XXX
  X   |   X         -------XXX-----+ <- Triangle outside
 X    |   |X        Rect ''.  XXX  |
 X    +---+X                ''.  XX|  
 X         X                   ''. X <- Circular segment inside 
  X       X                       ^|X 
   X     X                         | X 
    XXXXX
public static void RunSnippet()
{
    // test code
    double a,h,k,x1,x2;
    a = 10;
    h = 4;
    k = 0;
    x1 = -100;
    x2 = 100;

    double r1 = Integrate(x1, a, h, k);
    double r2 = Integrate(x2, a, h, k);

    Console.WriteLine(r2 - r1);

}

private static double Integrate(double x, double a,double h, double k)
{
    double a0 = a*a - (h-x)*(h-x);

    if(a0 <= 0.0){
        if(k == 0.0)
            return Math.PI * a * a / 4.0 * Math.Sign(x);
        else
            throw new Exception("outside boundaries");
    }

    double a1 = Math.Sqrt(a*a - (h-x)*(h-x)) * (h-x);
    double area = 0.5 * Math.Atan(a1 / ((h-x)*(h-x) - a*a))*a*a - 0.5 * a1 + k * x;
    return area;
}
public static bool IsIntersected(PointF circle, float radius, RectangleF rectangle)
{

        var rectangleCenter = new PointF((rectangle.X +  rectangle.Width / 2),
                                         (rectangle.Y + rectangle.Height / 2));

        var w = rectangle.Width  / 2;
        var h = rectangle.Height / 2;

        var dx = Math.Abs(circle.X - rectangleCenter.X);
        var dy = Math.Abs(circle.Y - rectangleCenter.Y);

        if (dx > (radius + w) || dy > (radius + h)) return false;


        var circleDistance = new PointF
                                 {
                                     X = Math.Abs(circle.X - rectangle.X - w),
                                     Y = Math.Abs(circle.Y - rectangle.Y - h)
                                 };


        if (circleDistance.X <= (w))
        {
            return true;
        }

        if (circleDistance.Y <= (h))
        {
            return true;
        }

        var cornerDistanceSq = Math.Pow(circleDistance.X - w, 2) + 
                    Math.Pow(circleDistance.Y - h, 2);

        return (cornerDistanceSq <= (Math.Pow(radius, 2)));
}
find the signed (negative out) normalized distance from the circle center
to each of the infinitely extended rectangle edge lines,
ie.
d_1=(xcenter-xleft)/r
d_2=(ycenter-ybottom)/r
etc

for convenience order 1,2,3,4 around the edge. If the rectangle is not
aligned with the cartesian coordinates this step is more complicated but
the remainder of the algorithm is the same

If ANY d_i <=- 1 return 0

if ALL d_i >=  1 return Pi r^2

this leave only one remaining fully outside case: circle center in
an external quadrant, and distance to corner greater than circle radius:

for each adjacent i,j (ie. i,j=1,2;2,3;3,4;4,1)
     if d_i<=0 and d_j <= 0 and d_i^2+d_j^2 > 1 return 0

now begin with full circle area  and subtract any areas in the
four external half planes

Area= Pi r^2
for each  d_i>-1
     a_i=arcsin( d_i )  #save a_i for next step
     Area -= r^2/2 (Pi - 2 a_i - sin(2 a_i)) 

At this point note we have double counted areas in the four external
quadrants, so add back in:

for each adjacent i,j
   if  d_i < 1 and   d_j < 1  and d_i^2+d_j^2 < 1
       Area += r^2/4 (Pi- 2 a_i - 2 a_j -sin(2 a_i) -sin(2 a_j) + 4 sin(a_i) sin(a_j))

return Area
x = center.x + cos(theta) * radius
y = center.y + sin(theta) * radius

^
|
|**###        **
| #*  #      *          * x
|#  *  #    *           # y
|#  *  #    *     
+-----------------------> theta
     *  #  *  # 
     *  #  *  #
      *  #*  #
       ***###
(x - center.x) / radius = cos(theta) // the 1st equation
theta = acos((x - center.x) / radius) // value of theta from the 1st equation
y = center.y + sin(acos((x - center.x) / radius)) * radius // substitute to the 2nd equation
y = sin(acos(x)) // x in [-1, 1]
y = sqrt(1 - x * x) // the same thing, arguably faster to compute http://www.wolframalpha.com/input/?i=sin%28acos%28x%29%29+

               ^ y
               |
            ***|***     <- 1
        ****   |   ****
      **       |       **
     *         |         *
    *          |          *
----|----------+----------|-----> x
   -1                     1
f(x): integral(sqrt(1 - x * x) * dx) = (sqrt(1 - x * x) * x + asin(x)) / 2 + C // http://www.wolframalpha.com/input/?i=sqrt%281+-+x*x%29
area(x0, x1) = f(max(-1, min(1, x1))) - f(max(-1, min(1, x0))) // the integral is not defined outside [-1, 1] but we want it to be zero out there

        ~         ~
        |      ^  |
        |      |  |
        |   ***|***     <- 1
        ****###|##|****
      **|######|##|    **
     *  |######|##|      *
    *   |######|##|       *
----|---|------+--|-------|-----> x
   -1   x0        x1      1
g(x, h): integral((sqrt(1 - x * x) - h) * dx) = (sqrt(1 - x * x) * x + asin(x) - 2 * h * x) / 2 + C // http://www.wolframalpha.com/input/?i=sqrt%281+-+x*x%29+-+h
area(x0, x1, h) = g(min(section(h), max(-section(h), x1))) - g(min(section(h), max(-section(h), x0)))

        ~         ~
        |      ^  |
        |      |  |
        |   ***|***     <- 1
        ****###|##|****
      **|######|##|    **
     *  +------+--+      *   <- h
    *          |          *
----|---|------+--|-------|-----> x
   -1   x0        x1      1
sqrt(1 - x * x) = h // http://www.wolframalpha.com/input/?i=sqrt%281+-+x+*+x%29+%3D+h
section(h): (h < 1)? sqrt(1 - h * h) : 0 // if h is 1 or above, then the section is an empty interval and we want the area integral to be zero

               ^ y
               |  
            ***|***     <- 1
        ****   |   ****  
      **       |       **
-----*---------+---------*------- y = h
    *          |          *
----||---------+---------||-----> x
   -1|                   |1
-section(h)          section(h)
area(x0, x1, y0, y1) = area(x0, x1, y0) - area(x0, x1, y1) // where x0 <= x1 and y0 <= y1

        ~         ~                              ~         ~
        |      ^  |                              |      ^  |
        |      |  |                              |      |  |
        |   ***|***                              |   ***|*** 
        ****###|##|****                          ****---+--+****      <- y1
      **|######|##|    **                      **       |       **
     *  +------+--+      *   <- y0            *         |         *
    *          |          *                  *          |          *
----|---|------+--|-------|-----> x      ----|---|------+--|-------|-----> x
        x0        x1                             x0        x1


               ^
               |
            ***|***
        ****---+--+****      <- y1
      **|######|##|    **
     *  +------+--+      *   <- y0
    *          |          *
----|---|------+--|-------|-----> x
        x0        x1
float section(float h, float r = 1) // returns the positive root of intersection of line y = h with circle centered at the origin and radius r
{
    assert(r >= 0); // assume r is positive, leads to some simplifications in the formula below (can factor out r from the square root)
    return (h < r)? sqrt(r * r - h * h) : 0; // http://www.wolframalpha.com/input/?i=r+*+sin%28acos%28x+%2F+r%29%29+%3D+h
}

float g(float x, float h, float r = 1) // indefinite integral of circle segment
{
    return .5f * (sqrt(1 - x * x / (r * r)) * x * r + r * r * asin(x / r) - 2 * h * x); // http://www.wolframalpha.com/input/?i=r+*+sin%28acos%28x+%2F+r%29%29+-+h
}

float area(float x0, float x1, float h, float r) // area of intersection of an infinitely tall box with left edge at x0, right edge at x1, bottom edge at h and top edge at infinity, with circle centered at the origin with radius r
{
    if(x0 > x1)
        std::swap(x0, x1); // this must be sorted otherwise we get negative area
    float s = section(h, r);
    return g(max(-s, min(s, x1)), h, r) - g(max(-s, min(s, x0)), h, r); // integrate the area
}

float area(float x0, float x1, float y0, float y1, float r) // area of the intersection of a finite box with a circle centered at the origin with radius r
{
    if(y0 > y1)
        std::swap(y0, y1); // this will simplify the reasoning
    if(y0 < 0) {
        if(y1 < 0)
            return area(x0, x1, -y0, -y1, r); // the box is completely under, just flip it above and try again
        else
            return area(x0, x1, 0, -y0, r) + area(x0, x1, 0, y1, r); // the box is both above and below, divide it to two boxes and go again
    } else {
        assert(y1 >= 0); // y0 >= 0, which means that y1 >= 0 also (y1 >= y0) because of the swap at the beginning
        return area(x0, x1, y0, r) - area(x0, x1, y1, r); // area of the lower box minus area of the higher box
    }
}

float area(float x0, float x1, float y0, float y1, float cx, float cy, float r) // area of the intersection of a general box with a general circle
{
    x0 -= cx; x1 -= cx;
    y0 -= cy; y1 -= cy;
    // get rid of the circle center

    return area(x0, x1, y0, y1, r);
}
printf("%f\n", area(-10, 10, -10, 10, 0, 0, 1)); // unit circle completely inside a huge box, area of intersection is pi
printf("%f\n", area(-10, 0, -10, 10, 0, 0, 1)); // half of unit circle inside a large box, area of intersection is pi/2
printf("%f\n", area(0, 10, -10, 10, 0, 0, 1)); // half of unit circle inside a large box, area of intersection is pi/2
printf("%f\n", area(-10, 10, -10, 0, 0, 0, 1)); // half of unit circle inside a large box, area of intersection is pi/2
printf("%f\n", area(-10, 10, 0, 10, 0, 0, 1)); // half of unit circle inside a large box, area of intersection is pi/2
printf("%f\n", area(0, 1, 0, 1, 0, 0, 1)); // unit box covering one quadrant of the circle, area of intersection is pi/4
printf("%f\n", area(0, -1, 0, 1, 0, 0, 1)); // unit box covering one quadrant of the circle, area of intersection is pi/4
printf("%f\n", area(0, -1, 0, -1, 0, 0, 1)); // unit box covering one quadrant of the circle, area of intersection is pi/4
printf("%f\n", area(0, 1, 0, -1, 0, 0, 1)); // unit box covering one quadrant of the circle, area of intersection is pi/4
printf("%f\n", area(-.5f, .5f, -.5f, .5f, 0, 0, 10)); // unit box completely inside a huge circle, area of intersection is 1
printf("%f\n", area(-20, -10, -10, 10, 0, 0, 1)); // huge box completely outside a circle (left), area of intersection is 0
printf("%f\n", area(10, 20, -10, 10, 0, 0, 1)); // huge box completely outside a circle (right), area of intersection is 0
printf("%f\n", area(-10, 10, -20, -10, 0, 0, 1)); // huge box completely outside a circle (below), area of intersection is 0
printf("%f\n", area(-10, 10, 10, 20, 0, 0, 1)); // huge box completely outside a circle (above), area of intersection is 0
3.141593
1.570796
1.570796
1.570796
1.570796
0.785398
0.785398
0.785398
0.785398
1.000000
-0.000000
0.000000
0.000000
0.000000