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 }