1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
# Licensed under a 3-clause BSD style license - see LICENSE.rst
# cython: language_level=3
"""
The functions defined here allow one to determine the exact area of
overlap of a rectangle and a circle (written by Thomas Robitaille).
"""
import numpy as np
cimport numpy as np
__all__ = ['circular_overlap_grid']
cdef extern from "math.h":
double asin(double x)
double sin(double x)
double sqrt(double x)
DTYPE = np.float64
ctypedef np.float64_t DTYPE_t
# NOTE: Here we need to make sure we use cimport to import the C functions from
# core (since these were defined with cdef). This also requires the core.pxd
# file to exist with the function signatures.
from .core cimport area_arc, area_triangle, floor_sqrt
def circular_overlap_grid(double xmin, double xmax, double ymin, double ymax,
int nx, int ny, double r, int use_exact,
int subpixels):
"""
circular_overlap_grid(xmin, xmax, ymin, ymax, nx, ny, r,
use_exact, subpixels)
Area of overlap between a circle and a pixel grid. The circle is centered
on the origin.
Parameters
----------
xmin, xmax, ymin, ymax : float
Extent of the grid in the x and y direction.
nx, ny : int
Grid dimensions.
r : float
The radius of the circle.
use_exact : 0 or 1
If ``1`` calculates exact overlap, if ``0`` uses ``subpixel`` number
of subpixels to calculate the overlap.
subpixels : int
Each pixel resampled by this factor in each dimension, thus each
pixel is divided into ``subpixels ** 2`` subpixels.
Returns
-------
frac : `~numpy.ndarray` (float)
2-d array of shape (ny, nx) giving the fraction of the overlap.
"""
cdef unsigned int i, j
cdef double x, y, dx, dy, d, pixel_radius
cdef double bxmin, bxmax, bymin, bymax
cdef double pxmin, pxcen, pxmax, pymin, pycen, pymax
# Define output array
cdef np.ndarray[DTYPE_t, ndim=2] frac = np.zeros([ny, nx], dtype=DTYPE)
# Find the width of each element in x and y
dx = (xmax - xmin) / nx
dy = (ymax - ymin) / ny
# Find the radius of a single pixel
pixel_radius = 0.5 * sqrt(dx * dx + dy * dy)
# Define bounding box
bxmin = -r - 0.5 * dx
bxmax = +r + 0.5 * dx
bymin = -r - 0.5 * dy
bymax = +r + 0.5 * dy
for i in range(nx):
pxmin = xmin + i * dx # lower end of pixel
pxcen = pxmin + dx * 0.5
pxmax = pxmin + dx # upper end of pixel
if pxmax > bxmin and pxmin < bxmax:
for j in range(ny):
pymin = ymin + j * dy
pycen = pymin + dy * 0.5
pymax = pymin + dy
if pymax > bymin and pymin < bymax:
# Distance from circle center to pixel center.
d = sqrt(pxcen * pxcen + pycen * pycen)
# If pixel center is "well within" circle, count full
# pixel.
if d < r - pixel_radius:
frac[j, i] = 1.
# If pixel center is "close" to circle border, find
# overlap.
elif d < r + pixel_radius:
# Either do exact calculation or use subpixel
# sampling:
if use_exact:
frac[j, i] = circular_overlap_single_exact(
pxmin, pymin, pxmax, pymax, r) / (dx * dy)
else:
frac[j, i] = circular_overlap_single_subpixel(
pxmin, pymin, pxmax, pymax, r, subpixels)
# Otherwise, it is fully outside circle.
# No action needed.
return frac
# NOTE: The following two functions use cdef because they are not
# intended to be called from the Python code. Using def makes them
# callable from outside, but also slower. In any case, these aren't useful
# to call from outside because they only operate on a single pixel.
cdef double circular_overlap_single_subpixel(double x0, double y0,
double x1, double y1,
double r, int subpixels):
"""Return the fraction of overlap between a circle and a single pixel
with given extent, using a sub-pixel sampling method."""
cdef unsigned int i, j
cdef double x, y, dx, dy, r_squared
cdef double frac = 0. # Accumulator.
dx = (x1 - x0) / subpixels
dy = (y1 - y0) / subpixels
r_squared = r ** 2
x = x0 - 0.5 * dx
for i in range(subpixels):
x += dx
y = y0 - 0.5 * dy
for j in range(subpixels):
y += dy
if x * x + y * y < r_squared:
frac += 1.
return frac / (subpixels * subpixels)
cdef double circular_overlap_single_exact(double xmin, double ymin,
double xmax, double ymax,
double r):
"""
Area of overlap of a rectangle and a circle
"""
if 0. <= xmin:
if 0. <= ymin:
return circular_overlap_core(xmin, ymin, xmax, ymax, r)
elif 0. >= ymax:
return circular_overlap_core(-ymax, xmin, -ymin, xmax, r)
else:
return circular_overlap_single_exact(xmin, ymin, xmax, 0., r) \
+ circular_overlap_single_exact(xmin, 0., xmax, ymax, r)
elif 0. >= xmax:
if 0. <= ymin:
return circular_overlap_core(-xmax, ymin, -xmin, ymax, r)
elif 0. >= ymax:
return circular_overlap_core(-xmax, -ymax, -xmin, -ymin, r)
else:
return circular_overlap_single_exact(xmin, ymin, xmax, 0., r) \
+ circular_overlap_single_exact(xmin, 0., xmax, ymax, r)
else:
if 0. <= ymin:
return circular_overlap_single_exact(xmin, ymin, 0., ymax, r) \
+ circular_overlap_single_exact(0., ymin, xmax, ymax, r)
if 0. >= ymax:
return circular_overlap_single_exact(xmin, ymin, 0., ymax, r) \
+ circular_overlap_single_exact(0., ymin, xmax, ymax, r)
else:
return circular_overlap_single_exact(xmin, ymin, 0., 0., r) \
+ circular_overlap_single_exact(0., ymin, xmax, 0., r) \
+ circular_overlap_single_exact(xmin, 0., 0., ymax, r) \
+ circular_overlap_single_exact(0., 0., xmax, ymax, r)
cdef double circular_overlap_core(double xmin, double ymin, double xmax, double ymax,
double r):
"""
Assumes that the center of the circle is <= xmin,
ymin (can always modify input to conform to this).
"""
cdef double area, d1, d2, x1, x2, y1, y2
if xmin * xmin + ymin * ymin > r * r:
area = 0.
elif xmax * xmax + ymax * ymax < r * r:
area = (xmax - xmin) * (ymax - ymin)
else:
area = 0.
d1 = floor_sqrt(xmax * xmax + ymin * ymin)
d2 = floor_sqrt(xmin * xmin + ymax * ymax)
if d1 < r and d2 < r:
x1, y1 = floor_sqrt(r * r - ymax * ymax), ymax
x2, y2 = xmax, floor_sqrt(r * r - xmax * xmax)
area = ((xmax - xmin) * (ymax - ymin) -
area_triangle(x1, y1, x2, y2, xmax, ymax) +
area_arc(x1, y1, x2, y2, r))
elif d1 < r:
x1, y1 = xmin, floor_sqrt(r * r - xmin * xmin)
x2, y2 = xmax, floor_sqrt(r * r - xmax * xmax)
area = (area_arc(x1, y1, x2, y2, r) +
area_triangle(x1, y1, x1, ymin, xmax, ymin) +
area_triangle(x1, y1, x2, ymin, x2, y2))
elif d2 < r:
x1, y1 = floor_sqrt(r * r - ymin * ymin), ymin
x2, y2 = floor_sqrt(r * r - ymax * ymax), ymax
area = (area_arc(x1, y1, x2, y2, r) +
area_triangle(x1, y1, xmin, y1, xmin, ymax) +
area_triangle(x1, y1, xmin, y2, x2, y2))
else:
x1, y1 = floor_sqrt(r * r - ymin * ymin), ymin
x2, y2 = xmin, floor_sqrt(r * r - xmin * xmin)
area = (area_arc(x1, y1, x2, y2, r) +
area_triangle(x1, y1, x2, y2, xmin, ymin))
return area
|