File indexing completed on 2024-04-28 03:43:55

0001 /* Licensed under a 3-clause BSD style license.
0002 
0003     Functions for calculating exact overlap between shapes.
0004 
0005     Original cython version by Thomas Robitaille. Converted to C by Kyle
0006     Barbary.*/
0007 
0008 #pragma once
0009 
0010 #include <math.h>
0011 
0012 /* Return area of a circle arc between (x1, y1) and (x2, y2) with radius r */
0013 /* reference: http://mathworld.wolfram.com/CircularSegment.html */
0014 static double area_arc(double x1, double y1, double x2, double y2,
0015                   double r)
0016 {
0017   double a, theta;
0018 
0019   a = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
0020   theta = 2. * asin(0.5 * a / r);
0021   return 0.5 * r * r * (theta - sin(theta));
0022 }
0023 
0024 /* Area of a triangle defined by three vertices */
0025 static double area_triangle(double x1, double y1, double x2, double y2,
0026                    double x3, double y3)
0027 {
0028   return 0.5 * fabs(x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2));
0029 }
0030 
0031 /* Core of circular overlap routine.
0032  * Assumes that xmax >= xmin >= 0.0, ymax >= ymin >= 0.0.
0033  * (can always modify input to conform to this).
0034  */
0035 static double circoverlap_core(double xmin, double ymin,
0036                       double xmax, double ymax, double r)
0037 {
0038   double a, b, x1, x2, y1, y2, r2, xmin2, ymin2, xmax2, ymax2;
0039 
0040   xmin2 = xmin*xmin;
0041   ymin2 = ymin*ymin;
0042   r2 = r*r;
0043   if (xmin2 + ymin2 > r2)
0044     return 0.;
0045 
0046   xmax2 = xmax*xmax;
0047   ymax2 = ymax*ymax;
0048   if (xmax2 + ymax2 < r2)
0049     return (xmax-xmin)*(ymax-ymin);
0050 
0051   a = xmax2 + ymin2;  /* (corner 1 distance)^2 */
0052   b = xmin2 + ymax2;  /* (corner 2 distance)^2 */
0053 
0054   if (a < r2 && b < r2)
0055     {
0056       x1 = sqrt(r2 - ymax2);
0057       y1 = ymax;
0058       x2 = xmax;
0059       y2 = sqrt(r2 - xmax2);
0060       return ((xmax-xmin)*(ymax-ymin) -
0061           area_triangle(x1, y1, x2, y2, xmax, ymax) +
0062           area_arc(x1, y1, x2, y2, r));
0063     }
0064 
0065   if (a < r2)
0066     {
0067       x1 = xmin;
0068       y1 = sqrt(r2 - xmin2);
0069       x2 = xmax;
0070       y2 = sqrt(r2 - xmax2);
0071       return (area_arc(x1, y1, x2, y2, r) +
0072           area_triangle(x1, y1, x1, ymin, xmax, ymin) +
0073           area_triangle(x1, y1, x2, ymin, x2, y2));
0074     }
0075 
0076   if (b < r2)
0077     {
0078       x1 = sqrt(r2 - ymin2);
0079       y1 = ymin;
0080       x2 = sqrt(r2 - ymax2);
0081       y2 = ymax;
0082       return (area_arc(x1, y1, x2, y2, r) +
0083           area_triangle(x1, y1, xmin, y1, xmin, ymax) +
0084           area_triangle(x1, y1, xmin, y2, x2, y2));
0085     }
0086 
0087   /* else */
0088   x1 = sqrt(r2 - ymin2);
0089   y1 = ymin;
0090   x2 = xmin;
0091   y2 = sqrt(r2 - xmin2);
0092   return (area_arc(x1, y1, x2, y2, r) +
0093       area_triangle(x1, y1, x2, y2, xmin, ymin));
0094 }
0095 
0096 
0097 
0098 /* Area of overlap of a rectangle and a circle */
0099 static double circoverlap(double xmin, double ymin, double xmax, double ymax,
0100               double r)
0101 {
0102   /* some subroutines demand that r > 0 */
0103   if (r <= 0.)
0104     return 0.;
0105 
0106   if (0. <= xmin)
0107     {
0108       if (0. <= ymin)
0109     return circoverlap_core(xmin, ymin, xmax, ymax, r);
0110       else if (0. >= ymax)
0111     return circoverlap_core(-ymax, xmin, -ymin, xmax, r);
0112       else
0113     return (circoverlap(xmin, ymin, xmax, 0., r) +
0114                 circoverlap(xmin, 0., xmax, ymax, r));
0115     }
0116   else if (0. >= xmax)
0117     {
0118       if (0. <= ymin)
0119     return circoverlap_core(-xmax, ymin, -xmin, ymax, r);
0120       else if (0. >= ymax)
0121     return circoverlap_core(-xmax, -ymax, -xmin, -ymin, r);
0122       else
0123     return (circoverlap(xmin, ymin, xmax, 0., r) +
0124                 circoverlap(xmin, 0., xmax, ymax, r));
0125     }
0126   else
0127     {
0128       if (0. <= ymin)
0129     return (circoverlap(xmin, ymin, 0., ymax, r) +
0130                 circoverlap(0., ymin, xmax, ymax, r));
0131       if (0. >= ymax)
0132     return (circoverlap(xmin, ymin, 0., ymax, r) +
0133                 circoverlap(0., ymin, xmax, ymax, r));
0134       else
0135     return (circoverlap(xmin, ymin, 0., 0., r) +
0136                 circoverlap(0., ymin, xmax, 0., r) +
0137                 circoverlap(xmin, 0., 0., ymax, r) +
0138                 circoverlap(0., 0., xmax, ymax, r));
0139     }
0140 }
0141 
0142 /*
0143 Start of new circular overlap routine that might be faster.
0144 
0145 double circoverlap_new(double dx, double dy, double r)
0146 {
0147   double xmin, xmax, ymin, ymax, xmin2, xmax2, ymin2, ymax2, r2;
0148 
0149   if (dx < 0.)
0150     dx = -dx;
0151   if (dy < 0.)
0152     dy = -dy;
0153   if (dy > dx)
0154     {
0155       r2 = dy;
0156       dy = dx;
0157       dx = r2;
0158     }
0159 
0160   xmax = dx + 0.5;
0161   ymax = dy + 0.5;
0162   xmax2 = xmax*xmax;
0163   ymax2 = ymax*ymax;
0164   r2 = r*r;
0165 
0166   if (xmax2 + ymax2 < r2)
0167     return 1.;
0168 
0169   xmin2 = xmin*xmin;
0170   if (xmin2 +
0171 }
0172 */
0173 
0174 /*****************************************************************************/
0175 /* ellipse overlap functions */
0176 
0177 typedef struct
0178 {
0179   double x, y;
0180 } point;
0181 
0182 
0183 typedef struct
0184 {
0185   point p1, p2;
0186 } intersections;
0187 
0188 static void swap(double *a, double *b)
0189 {
0190   double temp;
0191   temp = *a;
0192   *a = *b;
0193   *b = temp;
0194 }
0195 
0196 static void swap_point(point *a, point *b)
0197 {
0198   point temp;
0199   temp = *a;
0200   *a = *b;
0201   *b = temp;
0202 }
0203 
0204 /* rotate values to the left: a=b, b=c, c=a */
0205 static void rotate(double *a, double *b, double *c)
0206 {
0207   double temp;
0208   temp = *a;
0209   *a = *b;
0210   *b = *c;
0211   *c = temp;
0212 }
0213 
0214 /* Check if a point (x,y) is inside a triangle */
0215 static int in_triangle(double x, double y, double x1, double y1,
0216                double x2, double y2, double x3, double y3)
0217 {
0218   int c;
0219 
0220   c = 0;
0221   c += ((y1 > y) != (y2 > y)) && (x < (x2-x1) * (y-y1) / (y2-y1) + x1);
0222   c += ((y2 > y) != (y3 > y)) && (x < (x3-x2) * (y-y2) / (y3-y2) + x2);
0223   c += ((y3 > y) != (y1 > y)) && (x < (x1-x3) * (y-y3) / (y1-y3) + x3);
0224 
0225   return c % 2 == 1;
0226 }
0227 
0228 
0229 /* Intersection of a line defined by two points with a unit circle */
0230 static intersections circle_line(double x1, double y1, double x2, double y2)
0231 {
0232   double a, b, delta, dx, dy;
0233   double tol = 1.e-10;
0234   intersections inter;
0235 
0236   dx = x2 - x1;
0237   dy = y2 - y1;
0238 
0239   if (fabs(dx) < tol && fabs(dy) < tol)
0240     {
0241       inter.p1.x = 2.;
0242       inter.p1.y = 2.;
0243       inter.p2.x = 2.;
0244       inter.p2.y = 2.;
0245     }
0246   else if (fabs(dx) > fabs(dy))
0247     {
0248       /* Find the slope and intercept of the line */
0249       a = dy / dx;
0250       b = y1 - a * x1;
0251 
0252       /* Find the determinant of the quadratic equation */
0253       delta = 1. + a*a - b*b;
0254       if (delta > 0.)  /* solutions exist */
0255     {
0256       delta = sqrt(delta);
0257 
0258       inter.p1.x = (-a*b - delta) / (1. + a*a);
0259       inter.p1.y = a * inter.p1.x + b;
0260       inter.p2.x = (-a*b + delta) / (1. + a*a);
0261       inter.p2.y = a * inter.p2.x + b;
0262     }
0263       else  /* no solution, return values > 1 */
0264     {
0265       inter.p1.x = 2.;
0266       inter.p1.y = 2.;
0267       inter.p2.x = 2.;
0268       inter.p2.y = 2.;
0269     }
0270     }
0271   else
0272     {
0273       /* Find the slope and intercept of the line */
0274       a = dx / dy;
0275       b = x1 - a * y1;
0276 
0277       /* Find the determinant of the quadratic equation */
0278       delta = 1. + a*a - b*b;
0279       if (delta > 0.)  /* solutions exist */
0280     {
0281       delta = sqrt(delta);
0282       inter.p1.y = (-a*b - delta) / (1. + a*a);
0283       inter.p1.x = a * inter.p1.y + b;
0284       inter.p2.y = (-a*b + delta) / (1. + a*a);
0285       inter.p2.x = a * inter.p2.y + b;
0286     }
0287       else  /* no solution, return values > 1 */
0288     {
0289       inter.p1.x = 2.;
0290       inter.p1.y = 2.;
0291       inter.p2.x = 2.;
0292       inter.p2.y = 2.;
0293     }
0294     }
0295 
0296   return inter;
0297 }
0298 
0299 /* The intersection of a line with the unit circle. The intersection the
0300  * closest to (x2, y2) is chosen. */
0301 static point circle_segment_single2(double x1, double y1, double x2, double y2)
0302 {
0303   double dx1, dy1, dx2, dy2;
0304   intersections inter;
0305   point pt1, pt2, pt;
0306 
0307   inter = circle_line(x1, y1, x2, y2);
0308   pt1 = inter.p1;
0309   pt2 = inter.p2;
0310 
0311   /*Can be optimized, but just checking for correctness right now */
0312   dx1 = fabs(pt1.x - x2);
0313   dy1 = fabs(pt1.y - y2);
0314   dx2 = fabs(pt2.x - x2);
0315   dy2 = fabs(pt2.y - y2);
0316 
0317   if (dx1 > dy1)  /* compare based on x-axis */
0318     pt = (dx1 > dx2) ? pt2 : pt1;
0319   else
0320     pt = (dy1 > dy2) ? pt2 : pt1;
0321      
0322   return pt;
0323 }
0324 
0325 /* Intersection(s) of a segment with the unit circle. Discard any
0326    solution not on the segment. */
0327 intersections circle_segment(double x1, double y1, double x2, double y2)
0328 {
0329   intersections inter, inter_new;
0330   point pt1, pt2;
0331 
0332   inter = circle_line(x1, y1, x2, y2);
0333   pt1 = inter.p1;
0334   pt2 = inter.p2;
0335 
0336   if ((pt1.x > x1 && pt1.x > x2) || (pt1.x < x1 && pt1.x < x2) ||
0337       (pt1.y > y1 && pt1.y > y2) || (pt1.y < y1 && pt1.y < y2))
0338     {
0339       pt1.x = 2.;
0340       pt1.y = 2.;
0341     }
0342   if ((pt2.x > x1 && pt2.x > x2) || (pt2.x < x1 && pt2.x < x2) ||
0343       (pt2.y > y1 && pt2.y > y2) || (pt2.y < y1 && pt2.y < y2))
0344     {
0345       pt2.x = 2.;
0346       pt2.y = 2.;
0347     }
0348 
0349   if (pt1.x > 1. && pt2.x < 2.)
0350     {
0351       inter_new.p1 = pt1;
0352       inter_new.p2 = pt2;
0353     }
0354   else
0355     {
0356       inter_new.p1 = pt2;
0357       inter_new.p2 = pt1;
0358     }
0359   return inter_new;
0360 }
0361 
0362 
0363 /* Given a triangle defined by three points (x1, y1), (x2, y2), and
0364    (x3, y3), find the area of overlap with the unit circle. */
0365 static double triangle_unitcircle_overlap(double x1, double y1,
0366                       double x2, double y2,
0367                       double x3, double y3)
0368 {
0369   double d1, d2, d3, area, xp, yp;
0370   int in1, in2, in3, on1, on2, on3;
0371   int intersect13, intersect23;
0372   intersections inter;
0373   point pt1, pt2, pt3, pt4, pt5, pt6;
0374 
0375   /* Find distance of all vertices to circle center */
0376   d1 = x1*x1 + y1*y1;
0377   d2 = x2*x2 + y2*y2;
0378   d3 = x3*x3 + y3*y3;
0379 
0380   /* Order vertices by distance from origin */
0381   if (d1 < d2)
0382     {
0383       if (d2 < d3)
0384     {}
0385       else if (d1 < d3)
0386     {
0387       swap(&x2, &x3);
0388       swap(&y2, &y3);
0389       swap(&d2, &d3);
0390     }
0391       else
0392     {
0393       rotate(&x1, &x3, &x2);
0394       rotate(&y1, &y3, &y2);
0395       rotate(&d1, &d3, &d2);
0396     }
0397     }
0398   else
0399     {
0400       if (d1 < d3)
0401     {
0402       swap(&x1, &x2);
0403       swap(&y1, &y2);
0404       swap(&d1, &d2);
0405     }
0406       else if (d2 < d3)
0407     {
0408       rotate(&x1, &x2, &x3);
0409       rotate(&y1, &y2, &y3);
0410       rotate(&d1, &d2, &d3);
0411     }
0412       else
0413     {
0414       swap(&x1, &x3);
0415       swap(&y1, &y3);
0416       swap(&d1, &d3);
0417     }
0418     }
0419      
0420   /* Determine number of vertices inside circle */
0421   in1 = d1 < 1.;
0422   in2 = d2 < 1.;
0423   in3 = d3 < 1.;
0424 
0425   /* Determine which vertices are on the circle */
0426   on1 = fabs(d1 - 1.) < 1.e-10;
0427   on2 = fabs(d2 - 1.) < 1.e-10;
0428   on3 = fabs(d3 - 1.) < 1.e-10;
0429 
0430   if (on3 || in3) /* triangle completely within circle */
0431     area = area_triangle(x1, y1, x2, y2, x3, y3);
0432 
0433   else if (in2 || on2)
0434     {
0435       /* If vertex 1 or 2 are on the edge of the circle, then we use
0436        * the dot product to vertex 3 to determine whether an
0437        * intersection takes place. */
0438       intersect13 = !on1 || (x1*(x3-x1) + y1*(y3-y1) < 0.);
0439       intersect23 = !on2 || (x2*(x3-x2) + y2*(y3-y2) < 0.);
0440       if (intersect13 && intersect23)
0441     {
0442       pt1 = circle_segment_single2(x1, y1, x3, y3);
0443       pt2 = circle_segment_single2(x2, y2, x3, y3);
0444       area = (area_triangle(x1, y1, x2, y2, pt1.x, pt1.y) +
0445           area_triangle(x2, y2, pt1.x, pt1.y, pt2.x, pt2.y) +
0446           area_arc(pt1.x, pt1.y, pt2.x, pt2.y, 1.));
0447     }
0448       else if (intersect13)
0449     {
0450       pt1 = circle_segment_single2(x1, y1, x3, y3);
0451       area = (area_triangle(x1, y1, x2, y2, pt1.x, pt1.y) +
0452           area_arc(x2, y2, pt1.x, pt1.y, 1.));
0453     }
0454       else if (intersect23)
0455     {
0456       pt2 = circle_segment_single2(x2, y2, x3, y3);
0457       area = (area_triangle(x1, y1, x2, y2, pt2.x, pt2.y) +
0458           area_arc(x1, y1, pt2.x, pt2.y, 1.));
0459     }
0460       else
0461     area = area_arc(x1, y1, x2, y2, 1.);
0462     }
0463   else if (in1)
0464     {
0465       /* Check for intersections of far side with circle */
0466       inter = circle_segment(x2, y2, x3, y3);
0467       pt1 = inter.p1;
0468       pt2 = inter.p2;
0469       pt3 = circle_segment_single2(x1, y1, x2, y2);
0470       pt4 = circle_segment_single2(x1, y1, x3, y3);
0471 
0472       if (pt1.x > 1.)  /* indicates no intersection */
0473     {
0474       /* check if the pixel vertex (x1, y2) and the origin are on
0475        * different sides of the circle segment. If they are, the
0476        * circle segment spans more than pi radians.
0477        * We use the formula (y-y1) * (x2-x1) > (y2-y1) * (x-x1)
0478        * to determine if (x, y) is on the left of the directed
0479        * line segment from (x1, y1) to (x2, y2) */
0480       if (((0.-pt3.y) * (pt4.x-pt3.x) > (pt4.y-pt3.y) * (0.-pt3.x)) !=
0481           ((y1-pt3.y) * (pt4.x-pt3.x) > (pt4.y-pt3.y) * (x1-pt3.x)))
0482         {
0483           area = (area_triangle(x1, y1, pt3.x, pt3.y, pt4.x, pt4.y) +
0484               PI - area_arc(pt3.x, pt3.y, pt4.x, pt4.y, 1.));
0485         }
0486       else
0487         {
0488           area = (area_triangle(x1, y1, pt3.x, pt3.y, pt4.x, pt4.y) +
0489               area_arc(pt3.x, pt3.y, pt4.x, pt4.y, 1.));
0490         }
0491     }
0492       else
0493     {
0494       /* ensure that pt1 is the point closest to (x2, y2) */
0495       if (((pt2.x-x2)*(pt2.x-x2) + (pt2.y-y2)*(pt2.y-y2)) <
0496           ((pt1.x-x2)*(pt1.x-x2) + (pt1.y-y2)*(pt1.y-y2)))
0497         swap_point(&pt1, &pt2);
0498 
0499       area = (area_triangle(x1, y1, pt3.x, pt3.y, pt1.x, pt1.y) +
0500           area_triangle(x1, y1, pt1.x, pt1.y, pt2.x, pt2.y) +
0501           area_triangle(x1, y1, pt2.x, pt2.y, pt4.x, pt4.y) +
0502           area_arc(pt1.x, pt1.y, pt3.x, pt3.y, 1.) +
0503           area_arc(pt2.x, pt2.y, pt4.x, pt4.y, 1.));
0504     }
0505     }
0506   else
0507     {
0508       inter = circle_segment(x1, y1, x2, y2);
0509       pt1 = inter.p1;
0510       pt2 = inter.p2;
0511       inter = circle_segment(x2, y2, x3, y3);
0512       pt3 = inter.p1;
0513       pt4 = inter.p2;
0514       inter = circle_segment(x3, y3, x1, y1);
0515       pt5 = inter.p1;
0516       pt6 = inter.p2;
0517       if (pt1.x <= 1.)
0518     {
0519       xp = 0.5 * (pt1.x + pt2.x);
0520       yp = 0.5 * (pt1.y + pt2.y);
0521       area = (triangle_unitcircle_overlap(x1, y1, x3, y3, xp, yp) +
0522           triangle_unitcircle_overlap(x2, y2, x3, y3, xp, yp));
0523     }
0524       else if (pt3.x <= 1.)
0525     {
0526       xp = 0.5 * (pt3.x + pt4.x);
0527       yp = 0.5 * (pt3.y + pt4.y);
0528       area = (triangle_unitcircle_overlap(x3, y3, x1, y1, xp, yp) +
0529           triangle_unitcircle_overlap(x2, y2, x1, y1, xp, yp));
0530     }
0531       else if (pt5.x <= 1.)
0532     {
0533       xp = 0.5 * (pt5.x + pt6.x);
0534       yp = 0.5 * (pt5.y + pt6.y);
0535       area = (triangle_unitcircle_overlap(x1, y1, x2, y2, xp, yp) +
0536           triangle_unitcircle_overlap(x3, y3, x2, y2, xp, yp));
0537     }
0538       else  /* no intersections */
0539     {
0540       if (in_triangle(0., 0., x1, y1, x2, y2, x3, y3))
0541         return PI;
0542       else
0543         return 0.;
0544     }
0545     }
0546   return area;
0547 }
0548 
0549 /* exact overlap between a rectangle defined by (xmin, ymin, xmax,
0550    ymax) and an ellipse with major and minor axes rx and ry
0551    respectively and position angle theta. */
0552 static double ellipoverlap(double xmin, double ymin, double xmax, double ymax,
0553                double a, double b, double theta)
0554 {
0555   double cos_m_theta, sin_m_theta, scale;
0556   double x1, y1, x2, y2, x3, y3, x4, y4;
0557 
0558   cos_m_theta = cos(-theta);
0559   sin_m_theta = sin(-theta);
0560 
0561   /* scale by which the areas will be shrunk */
0562   scale = a * b;
0563 
0564   /* Reproject rectangle to a frame in which ellipse is a unit circle */
0565   x1 = (xmin * cos_m_theta - ymin * sin_m_theta) / a;
0566   y1 = (xmin * sin_m_theta + ymin * cos_m_theta) / b;
0567   x2 = (xmax * cos_m_theta - ymin * sin_m_theta) / a;
0568   y2 = (xmax * sin_m_theta + ymin * cos_m_theta) / b;
0569   x3 = (xmax * cos_m_theta - ymax * sin_m_theta) / a;
0570   y3 = (xmax * sin_m_theta + ymax * cos_m_theta) / b;
0571   x4 = (xmin * cos_m_theta - ymax * sin_m_theta) / a;
0572   y4 = (xmin * sin_m_theta + ymax * cos_m_theta) / b;
0573 
0574   /* Divide resulting quadrilateral into two triangles and find
0575      intersection with unit circle */
0576   return scale * (triangle_unitcircle_overlap(x1, y1, x2, y2, x3, y3) +
0577           triangle_unitcircle_overlap(x1, y1, x4, y4, x3, y3));
0578 }