drizzle 2.1.1__cp314-cp314-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of drizzle might be problematic. Click here for more details.
- drizzle/__init__.py +12 -0
- drizzle/cdrizzle.cpython-314-darwin.so +0 -0
- drizzle/resample.py +707 -0
- drizzle/tests/__init__.py +0 -0
- drizzle/tests/helpers.py +221 -0
- drizzle/tests/test_cdrizzle.py +29 -0
- drizzle/tests/test_overlap_calc.py +286 -0
- drizzle/tests/test_resample.py +1350 -0
- drizzle/tests/test_utils.py +237 -0
- drizzle/util.py +34 -0
- drizzle/utils.py +285 -0
- drizzle-2.1.1.dist-info/METADATA +172 -0
- drizzle-2.1.1.dist-info/RECORD +16 -0
- drizzle-2.1.1.dist-info/WHEEL +6 -0
- drizzle-2.1.1.dist-info/licenses/LICENSE.rst +31 -0
- drizzle-2.1.1.dist-info/top_level.txt +1 -0
|
File without changes
|
drizzle/tests/helpers.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import gwcs
|
|
4
|
+
import numpy as np
|
|
5
|
+
from gwcs.coordinate_frames import CelestialFrame, Frame2D
|
|
6
|
+
|
|
7
|
+
from astropy import coordinates as coord
|
|
8
|
+
from astropy import units
|
|
9
|
+
from astropy import wcs as fits_wcs
|
|
10
|
+
from astropy.io import fits
|
|
11
|
+
from astropy.modeling.models import (
|
|
12
|
+
Mapping,
|
|
13
|
+
Pix2Sky_TAN,
|
|
14
|
+
Polynomial2D,
|
|
15
|
+
RotateNative2Celestial,
|
|
16
|
+
Shift,
|
|
17
|
+
)
|
|
18
|
+
from astropy.modeling.projections import AffineTransformation2D
|
|
19
|
+
|
|
20
|
+
__all__ = ["wcs_from_file"]
|
|
21
|
+
|
|
22
|
+
TEST_DIR = os.path.abspath(os.path.dirname(__file__))
|
|
23
|
+
DATA_DIR = os.path.join(TEST_DIR, 'data')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def wcs_from_file(filename, ext=None, return_data=False, crpix_shift=None,
|
|
27
|
+
wcs_type="fits"):
|
|
28
|
+
"""
|
|
29
|
+
Read the WCS from a ".fits" file.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
filename : str
|
|
34
|
+
Name of the file to load WCS from.
|
|
35
|
+
|
|
36
|
+
ext : int, None, optional
|
|
37
|
+
Extension number to load the WCS from. When `None`, the WCS will be
|
|
38
|
+
loaded from the first extension containing a WCS.
|
|
39
|
+
|
|
40
|
+
return_data : bool, optional
|
|
41
|
+
When `True`, this function will return a tuple with first item
|
|
42
|
+
being the WCS and the second item being the image data array.
|
|
43
|
+
|
|
44
|
+
crpix_shift : tuple, None, optional
|
|
45
|
+
A tuple of two values to be added to header CRPIX values before
|
|
46
|
+
creating the WCS. This effectively introduces a constant shift
|
|
47
|
+
in the image coordinate system.
|
|
48
|
+
|
|
49
|
+
wcs_type : {"fits", "gwcs"}, optional
|
|
50
|
+
Return either a FITS WCS or a gwcs.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
WCS or tuple of WCS and image data
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
full_file_name = os.path.join(DATA_DIR, filename)
|
|
58
|
+
path = os.path.join(DATA_DIR, full_file_name)
|
|
59
|
+
|
|
60
|
+
def get_shape(hdr):
|
|
61
|
+
naxis1 = hdr.get("WCSNAX1", hdr.get("NAXIS1"))
|
|
62
|
+
naxis2 = hdr.get("WCSNAX2", hdr.get("NAXIS2"))
|
|
63
|
+
if naxis1 is None or naxis2 is None:
|
|
64
|
+
return None
|
|
65
|
+
return (naxis2, naxis1)
|
|
66
|
+
|
|
67
|
+
def data_from_hdr(hdr, data=None, shape=None):
|
|
68
|
+
if data is not None:
|
|
69
|
+
return data
|
|
70
|
+
bitpix = hdr.get("BITPIX", -32)
|
|
71
|
+
dtype = fits.hdu.BITPIX2DTYPE[bitpix]
|
|
72
|
+
shape = get_shape(hdr) or shape
|
|
73
|
+
if shape is None:
|
|
74
|
+
return None
|
|
75
|
+
return np.zeros(shape, dtype=dtype)
|
|
76
|
+
|
|
77
|
+
if os.path.splitext(filename)[1] in [".hdr", ".txt"]:
|
|
78
|
+
hdul = None
|
|
79
|
+
hdr = fits.Header.fromfile(
|
|
80
|
+
path,
|
|
81
|
+
sep='\n',
|
|
82
|
+
endcard=False,
|
|
83
|
+
padding=False
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
with fits.open(path) as fits_hdul:
|
|
88
|
+
hdul = fits.HDUList([hdu.copy() for hdu in fits_hdul])
|
|
89
|
+
|
|
90
|
+
if ext is None and hdul is not None:
|
|
91
|
+
for k, u in enumerate(hdul):
|
|
92
|
+
if "CTYPE1" in u.header:
|
|
93
|
+
ext = k
|
|
94
|
+
break
|
|
95
|
+
|
|
96
|
+
hdr = hdul[ext].header
|
|
97
|
+
|
|
98
|
+
if crpix_shift is not None and "CRPIX1" in hdr:
|
|
99
|
+
hdr["CRPIX1"] += crpix_shift[0]
|
|
100
|
+
hdr["CRPIX2"] += crpix_shift[1]
|
|
101
|
+
|
|
102
|
+
result = fits_wcs.WCS(hdr, hdul)
|
|
103
|
+
shape = get_shape(hdr)
|
|
104
|
+
result.array_shape = shape
|
|
105
|
+
|
|
106
|
+
if wcs_type == "gwcs":
|
|
107
|
+
result = _gwcs_from_hst_fits_wcs(result)
|
|
108
|
+
|
|
109
|
+
if return_data:
|
|
110
|
+
if hdul is None:
|
|
111
|
+
data = data_from_hdr(hdr, data=None, shape=shape)
|
|
112
|
+
return (result, data)
|
|
113
|
+
|
|
114
|
+
result = (result, )
|
|
115
|
+
if not isinstance(return_data, (list, tuple)):
|
|
116
|
+
return_data = [ext]
|
|
117
|
+
for ext in return_data:
|
|
118
|
+
data = data_from_hdr(
|
|
119
|
+
hdul[ext].header,
|
|
120
|
+
data=hdul[ext].data,
|
|
121
|
+
shape=shape
|
|
122
|
+
)
|
|
123
|
+
result = result + (data, )
|
|
124
|
+
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _gwcs_from_hst_fits_wcs(w):
|
|
129
|
+
# NOTE: this function ignores table distortions
|
|
130
|
+
def coeffs_to_poly(mat, degree):
|
|
131
|
+
pol = Polynomial2D(degree=degree)
|
|
132
|
+
for i in range(mat.shape[0]):
|
|
133
|
+
for j in range(mat.shape[1]):
|
|
134
|
+
if 0 < i + j <= degree:
|
|
135
|
+
setattr(pol, f'c{i}_{j}', mat[i, j])
|
|
136
|
+
return pol
|
|
137
|
+
|
|
138
|
+
nx, ny = w.pixel_shape
|
|
139
|
+
x0, y0 = w.wcs.crpix - 1
|
|
140
|
+
|
|
141
|
+
cd = w.wcs.piximg_matrix
|
|
142
|
+
|
|
143
|
+
if w.sip is None:
|
|
144
|
+
# construct GWCS:
|
|
145
|
+
det2sky = (
|
|
146
|
+
(Shift(-x0) & Shift(-y0)) |
|
|
147
|
+
Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
|
|
148
|
+
)
|
|
149
|
+
else:
|
|
150
|
+
cfx, cfy = np.dot(cd, [w.sip.a.ravel(), w.sip.b.ravel()])
|
|
151
|
+
a = np.reshape(cfx, w.sip.a.shape)
|
|
152
|
+
b = np.reshape(cfy, w.sip.b.shape)
|
|
153
|
+
a[1, 0] = cd[0, 0]
|
|
154
|
+
a[0, 1] = cd[0, 1]
|
|
155
|
+
b[1, 0] = cd[1, 0]
|
|
156
|
+
b[0, 1] = cd[1, 1]
|
|
157
|
+
|
|
158
|
+
polx = coeffs_to_poly(a, w.sip.a_order)
|
|
159
|
+
poly = coeffs_to_poly(b, w.sip.b_order)
|
|
160
|
+
|
|
161
|
+
sip = Mapping((0, 1, 0, 1)) | (polx & poly)
|
|
162
|
+
|
|
163
|
+
# construct GWCS:
|
|
164
|
+
det2sky = (
|
|
165
|
+
(Shift(-x0) & Shift(-y0)) | sip |
|
|
166
|
+
Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
detector_frame = Frame2D(
|
|
170
|
+
name="detector",
|
|
171
|
+
axes_names=("x", "y"),
|
|
172
|
+
unit=(units.pix, units.pix)
|
|
173
|
+
)
|
|
174
|
+
sky_frame = CelestialFrame(
|
|
175
|
+
reference_frame=getattr(coord, w.wcs.radesys).__call__(),
|
|
176
|
+
name=w.wcs.radesys,
|
|
177
|
+
unit=(units.deg, units.deg)
|
|
178
|
+
)
|
|
179
|
+
pipeline = [(detector_frame, det2sky), (sky_frame, None)]
|
|
180
|
+
gw = gwcs.wcs.WCS(pipeline)
|
|
181
|
+
gw.array_shape = w.array_shape
|
|
182
|
+
gw.bounding_box = ((-0.5, nx - 0.5), (-0.5, ny - 0.5))
|
|
183
|
+
|
|
184
|
+
if w.sip is not None:
|
|
185
|
+
# compute inverse SIP and re-create output GWCS
|
|
186
|
+
|
|
187
|
+
# compute inverse SIP:
|
|
188
|
+
hdr = gw.to_fits_sip(
|
|
189
|
+
max_inv_pix_error=1e-5,
|
|
190
|
+
inv_degree=None,
|
|
191
|
+
npoints=64,
|
|
192
|
+
crpix=w.wcs.crpix,
|
|
193
|
+
projection='TAN',
|
|
194
|
+
verbose=False
|
|
195
|
+
)
|
|
196
|
+
winv = fits_wcs.WCS(hdr)
|
|
197
|
+
ap = winv.sip.ap.copy()
|
|
198
|
+
bp = winv.sip.bp.copy()
|
|
199
|
+
ap[1, 0] += 1
|
|
200
|
+
bp[0, 1] += 1
|
|
201
|
+
polx_inv = coeffs_to_poly(ap, winv.sip.ap_order)
|
|
202
|
+
poly_inv = coeffs_to_poly(bp, winv.sip.bp_order)
|
|
203
|
+
af = AffineTransformation2D(
|
|
204
|
+
matrix=np.linalg.inv(winv.wcs.piximg_matrix)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# set analytical inverses:
|
|
208
|
+
sip.inverse = af | Mapping((0, 1, 0, 1)) | (polx_inv & poly_inv)
|
|
209
|
+
|
|
210
|
+
# construct GWCS:
|
|
211
|
+
det2sky = (
|
|
212
|
+
(Shift(-x0) & Shift(-y0)) | sip |
|
|
213
|
+
Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
pipeline = [(detector_frame, det2sky), (sky_frame, None)]
|
|
217
|
+
gw = gwcs.wcs.WCS(pipeline)
|
|
218
|
+
gw.array_shape = w.array_shape
|
|
219
|
+
gw.bounding_box = ((-0.5, nx - 0.5), (-0.5, ny - 0.5))
|
|
220
|
+
|
|
221
|
+
return gw
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from drizzle import cdrizzle
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_cdrizzle():
|
|
7
|
+
"""
|
|
8
|
+
Call C unit tests for cdrizzle, which are in the src/tests directory
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
size = 100
|
|
12
|
+
data = np.zeros((size, size), dtype='float32')
|
|
13
|
+
weights = np.ones((size, size), dtype='float32')
|
|
14
|
+
|
|
15
|
+
pixmap = np.indices((size, size), dtype='float64')
|
|
16
|
+
pixmap = pixmap.transpose()
|
|
17
|
+
|
|
18
|
+
output_data = np.zeros((size, size), dtype='float32')
|
|
19
|
+
output_counts = np.zeros((size, size), dtype='float32')
|
|
20
|
+
output_context = np.zeros((size, size), dtype='int32')
|
|
21
|
+
|
|
22
|
+
cdrizzle.test_cdrizzle(
|
|
23
|
+
data,
|
|
24
|
+
weights,
|
|
25
|
+
pixmap,
|
|
26
|
+
output_data,
|
|
27
|
+
output_counts,
|
|
28
|
+
output_context,
|
|
29
|
+
)
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
from itertools import product
|
|
2
|
+
from math import sqrt
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from drizzle.cdrizzle import clip_polygon, invert_pixmap
|
|
8
|
+
|
|
9
|
+
SQ2 = 1.0 / sqrt(2.0)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _is_poly_eq(p1, p2, rtol=0, atol=4e-12):
|
|
13
|
+
if len(p1) != len(p2):
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
p1 = p1[:]
|
|
17
|
+
for _ in p1:
|
|
18
|
+
p1.append(p1.pop(0))
|
|
19
|
+
if np.allclose(p1, p2, rtol=rtol, atol=atol):
|
|
20
|
+
return True
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _coord_mapping(xin, yin):
|
|
25
|
+
crpix = (289, 348) # center of distortions
|
|
26
|
+
shift = (1000, 1000)
|
|
27
|
+
rmat = 2.0 * np.array([[0.78103169, 0.66712321], [-0.63246699, 0.74091539]])
|
|
28
|
+
x = xin - crpix[0]
|
|
29
|
+
y = yin - crpix[1]
|
|
30
|
+
|
|
31
|
+
# add non-linear distortions
|
|
32
|
+
x += 2.4e-6 * x**2 - 1.0e-7 * x * y + 3.1e-6 * y**2
|
|
33
|
+
y += 1.2e-6 * x**2 - 2.0e-7 * x * y + 1.1e-6 * y**2
|
|
34
|
+
|
|
35
|
+
x, y = np.dot(rmat, [x, y])
|
|
36
|
+
x += shift[0]
|
|
37
|
+
y += shift[1]
|
|
38
|
+
|
|
39
|
+
return x, y
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _roll_vertices(polygon, n=1):
|
|
43
|
+
n = n % len(polygon)
|
|
44
|
+
return polygon[n:] + polygon[:n]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_invert_pixmap():
|
|
48
|
+
yin, xin = np.indices((1000, 1200), dtype=float)
|
|
49
|
+
xin = xin.flatten()
|
|
50
|
+
yin = yin.flatten()
|
|
51
|
+
|
|
52
|
+
xout, yout = _coord_mapping(xin, yin)
|
|
53
|
+
xout = xout.reshape((1000, 1200))
|
|
54
|
+
yout = yout.reshape((1000, 1200))
|
|
55
|
+
pixmap = np.dstack([xout, yout])
|
|
56
|
+
|
|
57
|
+
test_coords = [
|
|
58
|
+
(300, 600),
|
|
59
|
+
(0, 0),
|
|
60
|
+
(1199, 999),
|
|
61
|
+
(0, 999),
|
|
62
|
+
(1199, 0),
|
|
63
|
+
(200, 0),
|
|
64
|
+
(0, 438),
|
|
65
|
+
(1199, 432),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
for xr, yr in test_coords:
|
|
69
|
+
xout_t, yout_t = _coord_mapping(xr, yr)
|
|
70
|
+
xyin = invert_pixmap(pixmap, [xout_t, yout_t], [[-0.5, 1199.5], [-0.5, 999.5]])
|
|
71
|
+
assert np.allclose(xyin, [xr, yr], atol=0.05)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_invert_small_pixmap():
|
|
75
|
+
yin, xin = np.indices((2, 2), dtype=float)
|
|
76
|
+
pixmap = np.dstack([xin, yin])
|
|
77
|
+
|
|
78
|
+
test_coords = list(product(*(2 * [[-0.5, 1.5]])))
|
|
79
|
+
|
|
80
|
+
for xr, yr in test_coords:
|
|
81
|
+
xyin = invert_pixmap(pixmap, [xr, yr], [[-0.5, 1.5], [-0.5, 1.5]])
|
|
82
|
+
assert np.allclose(xyin, [xr, yr], atol=0.05)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_poly_intersection_with_self():
|
|
86
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
87
|
+
|
|
88
|
+
for k in range(4):
|
|
89
|
+
q = _roll_vertices(p, k)
|
|
90
|
+
|
|
91
|
+
pq = clip_polygon(q, p)
|
|
92
|
+
assert _is_poly_eq(pq, q)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.mark.parametrize(
|
|
96
|
+
'shift', [(0.25, 0.1), (-0.25, -0.1), (-0.25, 0.1), (0.25, -0.1)],
|
|
97
|
+
)
|
|
98
|
+
def test_poly_intersection_shifted(shift):
|
|
99
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
100
|
+
sx, sy = shift
|
|
101
|
+
pq_ref = sorted(
|
|
102
|
+
[
|
|
103
|
+
(max(0, sx), max(0, sy)),
|
|
104
|
+
(min(1, sx + 1), max(0, sy)),
|
|
105
|
+
(min(1, sx + 1), min(1, sy + 1)),
|
|
106
|
+
(max(0, sx), min(1, sy + 1)),
|
|
107
|
+
],
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
for k in range(4):
|
|
111
|
+
q = [(x + sx, y + sy) for x, y in p]
|
|
112
|
+
q = _roll_vertices(q, k)
|
|
113
|
+
pq = clip_polygon(q, p)
|
|
114
|
+
assert np.allclose(sorted(pq), pq_ref)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.mark.parametrize(
|
|
118
|
+
'shift', [(0, 70), (70, 0), (0, -70), (-70, 0)],
|
|
119
|
+
)
|
|
120
|
+
def test_poly_intersection_shifted_large(shift):
|
|
121
|
+
p = [(-0.5, -0.5), (99.5, -0.5), (99.5, 99.5), (-0.5, 99.5)]
|
|
122
|
+
sx, sy = shift
|
|
123
|
+
pq_ref = sorted(
|
|
124
|
+
[
|
|
125
|
+
(max(-0.5, -0.5 + sx), max(-0.5, -0.5 + sy)),
|
|
126
|
+
(min(99.5, 99.5 + sx), max(-0.5, -0.5 + sy)),
|
|
127
|
+
(min(99.5, 99.5 + sx), min(99.5, 99.5 + sy)),
|
|
128
|
+
(max(-0.5, -0.5 + sx), min(99.5, 99.5 + sy)),
|
|
129
|
+
],
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
for k in range(4):
|
|
133
|
+
q = [(x + sx, y + sy) for x, y in p]
|
|
134
|
+
q = _roll_vertices(q, k)
|
|
135
|
+
pq = clip_polygon(p, q)
|
|
136
|
+
assert len(pq) == 4
|
|
137
|
+
assert np.allclose(sorted(pq), pq_ref)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_poly_intersection_rotated45():
|
|
141
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
142
|
+
q = [(0, 0), (SQ2, -SQ2), (2.0 * SQ2, 0), (SQ2, SQ2)]
|
|
143
|
+
pq_ref = [(0, 0), (SQ2, SQ2), (1, 0), (1, SQ2 / (1.0 + SQ2))]
|
|
144
|
+
|
|
145
|
+
for k in range(4):
|
|
146
|
+
q = _roll_vertices(q, k)
|
|
147
|
+
pq = clip_polygon(p, q)
|
|
148
|
+
assert np.allclose(sorted(pq), pq_ref)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@pytest.mark.parametrize(
|
|
152
|
+
'axis', [0, 1],
|
|
153
|
+
)
|
|
154
|
+
def test_poly_intersection_flipped_axis(axis):
|
|
155
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
156
|
+
# (flipped wrt X-axis or Y-axis). Also change direction:
|
|
157
|
+
if axis == 0:
|
|
158
|
+
q = [(i, -j) for i, j in p][::-1]
|
|
159
|
+
else:
|
|
160
|
+
q = [(-i, j) for i, j in p][::-1]
|
|
161
|
+
|
|
162
|
+
for k in range(4):
|
|
163
|
+
q = _roll_vertices(q, k)
|
|
164
|
+
pq = clip_polygon(p, q)
|
|
165
|
+
assert len(pq) == 0
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_poly_intersection_reflect_origin():
|
|
169
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
170
|
+
# reflect wrt origin:
|
|
171
|
+
q = [(-i, -j) for i, j in p]
|
|
172
|
+
|
|
173
|
+
for k in range(4):
|
|
174
|
+
q = _roll_vertices(q, k)
|
|
175
|
+
pq = clip_polygon(p, q)
|
|
176
|
+
assert not pq
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@pytest.mark.parametrize(
|
|
180
|
+
'q,small',
|
|
181
|
+
[
|
|
182
|
+
([(0.1, 0.1), (0.9, 0.1), (0.9, 0.9), (0.1, 0.9)], True),
|
|
183
|
+
([(0.0, 0.0), (1.0, 0.0), (1.0, 0.4), (0.0, 0.4)], True),
|
|
184
|
+
([(-0.1, -0.1), (1.1, -0.1), (1.1, 1.1), (-0.1, 1.1)], False),
|
|
185
|
+
],
|
|
186
|
+
)
|
|
187
|
+
def test_poly_includes_the_other(q, small):
|
|
188
|
+
wnd = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
189
|
+
|
|
190
|
+
for k in range(4):
|
|
191
|
+
q = _roll_vertices(q, k)
|
|
192
|
+
qp = clip_polygon(q, wnd)
|
|
193
|
+
|
|
194
|
+
assert _is_poly_eq(qp, q if small else wnd)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@pytest.mark.parametrize(
|
|
198
|
+
'q',
|
|
199
|
+
[
|
|
200
|
+
[(0, 0), (1, 0), (0.5, 0.6)],
|
|
201
|
+
[(0.1, 0), (0.9, 0), (0.5, 0.6)],
|
|
202
|
+
],
|
|
203
|
+
)
|
|
204
|
+
def test_poly_triangle_common_side(q):
|
|
205
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
206
|
+
sq = sorted(q)
|
|
207
|
+
|
|
208
|
+
for k in range(3):
|
|
209
|
+
q = _roll_vertices(q, k)
|
|
210
|
+
pq = clip_polygon(p, q)
|
|
211
|
+
assert np.allclose(sq, sorted(pq))
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def test_poly_triangle_common_side_lg():
|
|
215
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
216
|
+
q = [(-0.1, 0), (1.1, 0), (0.5, 0.6)]
|
|
217
|
+
ref_pq = [(0, 0), (0, 0.1), (0.5, 0.6), (1, 0), (1, 0.1)]
|
|
218
|
+
|
|
219
|
+
for k in range(3):
|
|
220
|
+
q = _roll_vertices(q, k)
|
|
221
|
+
pq = clip_polygon(p, q)
|
|
222
|
+
assert np.allclose(ref_pq, sorted(pq))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_poly_intersection_with_self_extra_vertices():
|
|
226
|
+
p = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
|
227
|
+
p_ref = [(0, 0), (0, 1), (1, 0), (1, 1)]
|
|
228
|
+
# Q is same as P with extra vertices places along P's edges
|
|
229
|
+
q = [(0, 0), (0.5, 0), (1, 0), (1, 0.4), (1, 1), (0.7, 1), (0, 1), (0, 0.2)]
|
|
230
|
+
|
|
231
|
+
for k in range(4):
|
|
232
|
+
q = _roll_vertices(q, k)
|
|
233
|
+
|
|
234
|
+
pq = clip_polygon(p, q)
|
|
235
|
+
assert sorted(pq) == p_ref
|
|
236
|
+
|
|
237
|
+
pq = clip_polygon(q, p)
|
|
238
|
+
assert sorted(pq) == p_ref
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def test_intersection_case01():
|
|
242
|
+
# a real case of failure of the code from PR #104
|
|
243
|
+
p = [
|
|
244
|
+
(4517.377385, 8863.424319),
|
|
245
|
+
(5986.279535, 12966.888023),
|
|
246
|
+
(1917.908619, 14391.538506),
|
|
247
|
+
(453.893145, 10397.019260),
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
wnd = [(-0.5, -0.5), (5224.5, -0.5), (5224.5, 15999.5), (-0.5, 15999.5)]
|
|
251
|
+
|
|
252
|
+
cp_ref = [
|
|
253
|
+
(4517.377385, 8863.424319),
|
|
254
|
+
(5224.5, 10838.812526396974),
|
|
255
|
+
(5224.5, 13233.64580022457),
|
|
256
|
+
(1917.908619, 14391.538506),
|
|
257
|
+
(453.893145, 10397.01926),
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
cp = clip_polygon(p, wnd)
|
|
261
|
+
|
|
262
|
+
assert _is_poly_eq(cp, cp_ref)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_intersection_case02():
|
|
266
|
+
# a real case of failure reported in #189
|
|
267
|
+
p = [
|
|
268
|
+
(-0.04000000000000009104, 1.5),
|
|
269
|
+
(2.73499999999999943157, 1.5),
|
|
270
|
+
(1.83500000000000018652, -0.5),
|
|
271
|
+
(-0.03999999999999998002, -0.5),
|
|
272
|
+
]
|
|
273
|
+
wnd = [
|
|
274
|
+
(-0.5, -0.5), (3.5, -0.5), (3.5, 3.5), (-0.5, 3.5)
|
|
275
|
+
]
|
|
276
|
+
|
|
277
|
+
cp_ref = [
|
|
278
|
+
(-0.04, 1.5),
|
|
279
|
+
(-0.04, -0.5),
|
|
280
|
+
(1.835, -0.5),
|
|
281
|
+
(2.735, 1.5),
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
cp = clip_polygon(p, wnd)
|
|
285
|
+
|
|
286
|
+
assert _is_poly_eq(cp, cp_ref)
|