drizzle 2.1.0__tar.gz → 2.1.1__tar.gz

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.

Files changed (63) hide show
  1. {drizzle-2.1.0 → drizzle-2.1.1}/.github/workflows/build.yml +1 -1
  2. {drizzle-2.1.0 → drizzle-2.1.1}/.github/workflows/ci.yml +1 -1
  3. {drizzle-2.1.0 → drizzle-2.1.1}/CHANGES.rst +25 -1
  4. {drizzle-2.1.0/drizzle.egg-info → drizzle-2.1.1}/PKG-INFO +1 -1
  5. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/tests/test_overlap_calc.py +24 -0
  6. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/tests/test_resample.py +130 -5
  7. {drizzle-2.1.0 → drizzle-2.1.1/drizzle.egg-info}/PKG-INFO +1 -1
  8. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzleapi.c +17 -7
  9. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzlebox.c +151 -144
  10. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzlebox.h +1 -3
  11. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzlemap.c +80 -29
  12. {drizzle-2.1.0 → drizzle-2.1.1}/.clang-format +0 -0
  13. {drizzle-2.1.0 → drizzle-2.1.1}/.coveragerc +0 -0
  14. {drizzle-2.1.0 → drizzle-2.1.1}/.flake8 +0 -0
  15. {drizzle-2.1.0 → drizzle-2.1.1}/.github/CODEOWNERS +0 -0
  16. {drizzle-2.1.0 → drizzle-2.1.1}/.github/dependabot.yml +0 -0
  17. {drizzle-2.1.0 → drizzle-2.1.1}/.gitignore +0 -0
  18. {drizzle-2.1.0 → drizzle-2.1.1}/.readthedocs.yaml +0 -0
  19. {drizzle-2.1.0 → drizzle-2.1.1}/.ruff.toml +0 -0
  20. {drizzle-2.1.0 → drizzle-2.1.1}/CODE_OF_CONDUCT.md +0 -0
  21. {drizzle-2.1.0 → drizzle-2.1.1}/LICENSE.rst +0 -0
  22. {drizzle-2.1.0 → drizzle-2.1.1}/MANIFEST.in +0 -0
  23. {drizzle-2.1.0 → drizzle-2.1.1}/README.rst +0 -0
  24. {drizzle-2.1.0 → drizzle-2.1.1}/docs/Makefile +0 -0
  25. {drizzle-2.1.0 → drizzle-2.1.1}/docs/_templates/autosummary/base.rst +0 -0
  26. {drizzle-2.1.0 → drizzle-2.1.1}/docs/_templates/autosummary/class.rst +0 -0
  27. {drizzle-2.1.0 → drizzle-2.1.1}/docs/_templates/autosummary/module.rst +0 -0
  28. {drizzle-2.1.0 → drizzle-2.1.1}/docs/conf.py +0 -0
  29. {drizzle-2.1.0 → drizzle-2.1.1}/docs/drizzle/api.rst +0 -0
  30. {drizzle-2.1.0 → drizzle-2.1.1}/docs/drizzle/index.rst +0 -0
  31. {drizzle-2.1.0 → drizzle-2.1.1}/docs/drizzle/user.rst +0 -0
  32. {drizzle-2.1.0 → drizzle-2.1.1}/docs/exts/numfig.py +0 -0
  33. {drizzle-2.1.0 → drizzle-2.1.1}/docs/index.rst +0 -0
  34. {drizzle-2.1.0 → drizzle-2.1.1}/docs/make.bat +0 -0
  35. {drizzle-2.1.0 → drizzle-2.1.1}/docs/rtd_environment.yaml +0 -0
  36. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/__init__.py +0 -0
  37. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/resample.py +0 -0
  38. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/tests/__init__.py +0 -0
  39. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/tests/helpers.py +0 -0
  40. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/tests/test_cdrizzle.py +0 -0
  41. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/tests/test_utils.py +0 -0
  42. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/util.py +0 -0
  43. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle/utils.py +0 -0
  44. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle.egg-info/SOURCES.txt +0 -0
  45. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle.egg-info/dependency_links.txt +0 -0
  46. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle.egg-info/not-zip-safe +0 -0
  47. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle.egg-info/requires.txt +0 -0
  48. {drizzle-2.1.0 → drizzle-2.1.1}/drizzle.egg-info/top_level.txt +0 -0
  49. {drizzle-2.1.0 → drizzle-2.1.1}/pyproject.toml +0 -0
  50. {drizzle-2.1.0 → drizzle-2.1.1}/setup.cfg +0 -0
  51. {drizzle-2.1.0 → drizzle-2.1.1}/setup.py +0 -0
  52. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzleblot.c +0 -0
  53. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzleblot.h +0 -0
  54. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzlemap.h +0 -0
  55. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzleutil.c +0 -0
  56. {drizzle-2.1.0 → drizzle-2.1.1}/src/cdrizzleutil.h +0 -0
  57. {drizzle-2.1.0 → drizzle-2.1.1}/src/driz_portability.h +0 -0
  58. {drizzle-2.1.0 → drizzle-2.1.1}/src/tests/.clang-format +0 -0
  59. {drizzle-2.1.0 → drizzle-2.1.1}/src/tests/drizzletest.h +0 -0
  60. {drizzle-2.1.0 → drizzle-2.1.1}/src/tests/fct.h +0 -0
  61. {drizzle-2.1.0 → drizzle-2.1.1}/src/tests/pandokia_fct.h +0 -0
  62. {drizzle-2.1.0 → drizzle-2.1.1}/src/tests/utest_cdrizzle.c +0 -0
  63. {drizzle-2.1.0 → drizzle-2.1.1}/tox.ini +0 -0
@@ -8,7 +8,7 @@ on:
8
8
 
9
9
  jobs:
10
10
  build:
11
- uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@v1
11
+ uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@v2
12
12
  with:
13
13
  upload_to_pypi: ${{ (github.event_name == 'release') && (github.event.action == 'released') }}
14
14
  targets: |
@@ -17,7 +17,7 @@ concurrency:
17
17
 
18
18
  jobs:
19
19
  test:
20
- uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1
20
+ uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2
21
21
  with:
22
22
  envs: |
23
23
  - linux: check-style
@@ -5,10 +5,34 @@ Release Notes
5
5
  =============
6
6
 
7
7
 
8
+ 2.1.1 (2025-08-14)
9
+ ==================
10
+
11
+ - Fix a bug in the Cython code for ``tdriz()`` due to which the code would
12
+ raise an error for sub-second exposure times. [#192]
13
+
14
+ - Fixed a numerical instability in the new and old "boxer" algorithm in #175
15
+ (withdrawn release 2.1.0). [#191]
16
+
17
+
18
+ 2.1.0 (2025-02-20; withdrawn)
19
+ =============================
20
+
21
+ - Restored faster "boxer" overlap for square kernel, improving resample speed;
22
+ optimized and simplified related code. [#175]
23
+
24
+
25
+ 2.1.1 (unreleased)
26
+ ==================
27
+
28
+ - Fixed a bug in polygon intersection algorithm that could result in
29
+ incorrect output when the input polygons have nearly collinear edges. [#194]
30
+
31
+
8
32
  2.0.2 (unreleased)
9
33
  ==================
10
34
 
11
- - Fix a bug in ``resample.Drizzle`` due to which initially, before adding
35
+ - Fixed a bug in ``resample.Drizzle`` due to which initially, before adding
12
36
  the first image, ``Drizzle.output_img`` is not filled with ``NaN`` when
13
37
  ``fillval`` is either ``INDEF``, ``NAN``, ``None`` *and* the ``Drizzle``
14
38
  object was initialized with ``out_img=None``. [#170]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drizzle
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: A package for combining dithered images into a single image
5
5
  Author-email: STScI <help@stsci.edu>
6
6
  Project-URL: Homepage, https://github.com/spacetelescope/drizzle
@@ -260,3 +260,27 @@ def test_intersection_case01():
260
260
  cp = clip_polygon(p, wnd)
261
261
 
262
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)
@@ -286,6 +286,7 @@ def test_drizzle_defaults():
286
286
  assert driz.out_img[1, 2] == 1
287
287
  assert (driz.out_img[2, 1] - 2.0) < 1.0e-14
288
288
 
289
+
289
290
  @pytest.mark.parametrize(
290
291
  'kernel,test_image_type,max_diff_atol',
291
292
  [
@@ -355,7 +356,10 @@ def test_resample_kernel(tmpdir, kernel, test_image_type, max_diff_atol):
355
356
  scale=pscale_ratio,
356
357
  )
357
358
  else:
358
- with pytest.warns(Warning):
359
+ with pytest.warns(
360
+ Warning,
361
+ match=f"Kernel '{kernel}' is not a flux-conserving kernel"
362
+ ):
359
363
  driz.add_image(
360
364
  insci,
361
365
  exptime=1.0,
@@ -486,7 +490,10 @@ def test_zero_input_weight(kernel, fc):
486
490
  fillstr='INDEF',
487
491
  )
488
492
  else:
489
- with pytest.warns(Warning):
493
+ with pytest.warns(
494
+ Warning,
495
+ match=f"Kernel '{kernel}' is not a flux-conserving kernel"
496
+ ):
490
497
  cdrizzle.tdriz(
491
498
  insci,
492
499
  inwht,
@@ -760,7 +767,10 @@ def test_flux_conservation_nondistorted(kernel, fc):
760
767
  wtscale=1.0,
761
768
  )
762
769
  else:
763
- with pytest.warns(Warning):
770
+ with pytest.warns(
771
+ Warning,
772
+ match=f"Kernel '{kernel}' is not a flux-conserving kernel"
773
+ ):
764
774
  cdrizzle.tdriz(
765
775
  in_sci,
766
776
  in_wht,
@@ -848,7 +858,10 @@ def test_flux_conservation_distorted(kernel, fc):
848
858
  wtscale=1.0,
849
859
  )
850
860
  else:
851
- with pytest.warns(Warning):
861
+ with pytest.warns(
862
+ Warning,
863
+ match=f"Kernel '{kernel}' is not a flux-conserving kernel"
864
+ ):
852
865
  cdrizzle.tdriz(
853
866
  in_sci,
854
867
  in_wht,
@@ -1138,7 +1151,7 @@ def test_resample_get_shape_from_pixmap():
1138
1151
  exptime=0.0,
1139
1152
  )
1140
1153
 
1141
- driz.add_image(in_sci, weight_map=in_wht, exptime=1.0, pixmap=pixmap)
1154
+ driz.add_image(in_sci, weight_map=in_wht, exptime=0.1, pixmap=pixmap)
1142
1155
  assert driz.out_img.shape == in_shape
1143
1156
 
1144
1157
 
@@ -1223,3 +1236,115 @@ def test_nan_fillval(fillval):
1223
1236
  )
1224
1237
 
1225
1238
  assert np.all(np.isnan(driz.out_img))
1239
+
1240
+
1241
+ def test_resample_edge_sgarea_bug():
1242
+ """
1243
+ Test from https://github.com/spacetelescope/drizzle/issues/187
1244
+
1245
+ """
1246
+ pixmap = (np.array([
1247
+ [
1248
+ [0.31887051, 1.],
1249
+ [1.01898591, 1.],
1250
+ [1.71909665, 1.],
1251
+ ],
1252
+ [
1253
+ [0.31591881, 0.],
1254
+ [1.0160342312345672, 0.],
1255
+ [1.716145, 0.],
1256
+ ]
1257
+ ], dtype="f8"))
1258
+
1259
+ in_shape = pixmap.shape[:2]
1260
+ img = np.full(in_shape, 42, dtype=np.float32)
1261
+ out_shape = (4, 4)
1262
+
1263
+ driz = resample.Drizzle(
1264
+ kernel='square',
1265
+ fillval='nan',
1266
+ out_shape=out_shape,
1267
+ disable_ctx=True,
1268
+ )
1269
+
1270
+ driz.add_image(
1271
+ img,
1272
+ exptime=11.776,
1273
+ in_units='cps',
1274
+ pixfrac=1.0,
1275
+ pixmap=pixmap,
1276
+ scale=1.0,
1277
+ wht_scale=1.0,
1278
+ )
1279
+ # expected pixels should be close to 42
1280
+ np.testing.assert_allclose(driz.out_img[:2, :3], img[0, 0], rtol=1e-6)
1281
+
1282
+ # other values should be nan
1283
+ np.testing.assert_equal(driz.out_img[:, 3:], np.nan)
1284
+ np.testing.assert_equal(driz.out_img[2:], np.nan)
1285
+
1286
+
1287
+ def test_resample_edge_collinear():
1288
+ """
1289
+ Test that resample does not crash when the input image is smaller than the
1290
+ output image, and the edges of the two images are nearly collinear.
1291
+
1292
+ Test based on the example from
1293
+ https://github.com/spacetelescope/drizzle/issues/189#issue-3196294879
1294
+
1295
+ """
1296
+ pixmap = (np.array([
1297
+ [
1298
+ [0.31, 1.0],
1299
+ [1.01, 1.0],
1300
+ [2.01, 1.0],
1301
+ ],
1302
+ [
1303
+ [0.31, 0.],
1304
+ [1.01, 0.],
1305
+ [1.71, 0.],
1306
+ ]
1307
+ ], dtype="f8"))
1308
+
1309
+ in_shape = pixmap.shape[:2]
1310
+ img = np.full(in_shape, np.pi, dtype=np.float32)
1311
+ in_flux = np.sum(img)
1312
+ out_shape = (4, 4)
1313
+
1314
+ driz = resample.Drizzle(
1315
+ kernel='square',
1316
+ fillval='nan',
1317
+ out_shape=out_shape,
1318
+ disable_ctx=True,
1319
+ )
1320
+
1321
+ driz.add_image(
1322
+ img,
1323
+ exptime=11.776,
1324
+ in_units='cps',
1325
+ pixfrac=1.0,
1326
+ pixmap=pixmap,
1327
+ scale=1.0,
1328
+ wht_scale=1.0,
1329
+ )
1330
+
1331
+ out_flux = np.nansum(driz.out_img * driz.out_wht)
1332
+
1333
+ # Given this pixmap, the entire input image should fit within the output
1334
+ # image. There should be at least 7 pixels with finite values in the output
1335
+ # image. We can get more than 7 pixels with finite values due to rounding
1336
+ # errors when computing polygon intersections (those "extra" pixels should)
1337
+ # have very small weights.
1338
+ assert np.sum(driz.out_wht > 1e-30) == 7
1339
+ assert np.sum(np.isfinite(driz.out_img)) >= 7
1340
+ # output image intensity must be equal to the input image intensity:
1341
+ assert np.allclose(
1342
+ driz.out_img[np.isfinite(driz.out_img)],
1343
+ img[0, 0],
1344
+ rtol=0,
1345
+ atol=1e-6
1346
+ )
1347
+ # flux in the output image should be equal to the flux in the input image:
1348
+ assert np.allclose(out_flux, in_flux, rtol=1e-6, atol=0.0)
1349
+ # area of the signal in the input image:
1350
+ assert np.allclose(np.sum(driz.out_wht), 6.0, rtol=0, atol=1.0e-6)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drizzle
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: A package for combining dithered images into a single image
5
5
  Author-email: STScI <help@stsci.edu>
6
6
  Project-URL: Homepage, https://github.com/spacetelescope/drizzle
@@ -230,19 +230,29 @@ tdriz(PyObject *obj UNUSED_PARAM, PyObject *args, PyObject *keywords) {
230
230
  p.fill_value = fill_value;
231
231
  p.error = &error;
232
232
 
233
- if (driz_error_check(&error, "xmin must be >= 0", p.xmin >= 0)) goto _exit;
234
- if (driz_error_check(&error, "ymin must be >= 0", p.ymin >= 0)) goto _exit;
235
- if (driz_error_check(&error, "xmax must be > xmin", p.xmax > p.xmin))
233
+ if (driz_error_check(&error, "xmin must be >= 0", p.xmin >= 0)) {
236
234
  goto _exit;
237
- if (driz_error_check(&error, "ymax must be > ymin", p.ymax > p.ymin))
235
+ }
236
+ if (driz_error_check(&error, "ymin must be >= 0", p.ymin >= 0)) {
238
237
  goto _exit;
239
- if (driz_error_check(&error, "scale must be > 0", p.scale > 0.0))
238
+ }
239
+ if (driz_error_check(&error, "xmax must be > xmin", p.xmax > p.xmin)) {
240
240
  goto _exit;
241
- if (driz_error_check(&error, "exposure time must be > 0", p.exposure_time))
241
+ }
242
+ if (driz_error_check(&error, "ymax must be > ymin", p.ymax > p.ymin)) {
243
+ goto _exit;
244
+ }
245
+ if (driz_error_check(&error, "scale must be > 0", p.scale > 0.0f)) {
242
246
  goto _exit;
247
+ }
248
+ if (driz_error_check(&error, "exposure time must be > 0",
249
+ p.exposure_time > 0.0f)) {
250
+ goto _exit;
251
+ }
243
252
  if (driz_error_check(&error, "weight scale must be > 0",
244
- p.weight_scale > 0.0))
253
+ p.weight_scale > 0.0f)) {
245
254
  goto _exit;
255
+ }
246
256
 
247
257
  get_dimensions(p.pixmap, psize);
248
258
  if (psize[0] != isize[0] || psize[1] != isize[1]) {
@@ -88,75 +88,98 @@ This is used by BOXER.
88
88
  */
89
89
 
90
90
  static inline_macro double
91
- sgarea(const double x1, const double y1, const double x2, const double y2,
92
- const int sgn_dx, const double slope, const double inv_slope) {
93
- double c, xlo, xhi, ylo, yhi, xtop;
91
+ sgarea(const double x1, const double y1, const double x2, const double y2) {
92
+ double xlo, xhi, ylo, yhi, xtop;
93
+ double dx, dy, det, sgn_dx;
94
94
 
95
- /* Trap vertical line */
96
- if (inv_slope == 0) {
97
- return 0.0;
98
- }
99
-
100
- if (sgn_dx < 0) {
101
- xlo = x2;
102
- xhi = x1;
103
- } else {
104
- xlo = x1;
105
- xhi = x2;
106
- }
107
-
108
- /* And determine the bounds ignoring y for now */
109
- if (xlo >= 1.0 || xhi <= 0.0) {
110
- return 0.0;
111
- }
95
+ dx = x2 - x1;
96
+ dy = y2 - y1;
112
97
 
113
- xlo = MAX(xlo, 0.0);
114
- xhi = MIN(xhi, 1.0);
98
+ /* Trap vertical line */
99
+ if (dx == 0) {
100
+ return 0.0;
101
+ }
115
102
 
116
- /* Now look at y */
117
- c = y1 - slope * x1;
118
- ylo = slope * xlo + c;
119
- yhi = slope * xhi + c;
103
+ if (dx < 0) {
104
+ sgn_dx = -1.0;
105
+ xlo = x2;
106
+ xhi = x1;
107
+ } else {
108
+ sgn_dx = 1.0;
109
+ xlo = x1;
110
+ xhi = x2;
111
+ }
120
112
 
121
- /* Trap segment entirely below axis */
122
- if (ylo <= 0.0 && yhi <= 0.0) {
123
- return 0.0;
124
- }
125
-
126
- /* There are four possibilities: both y below 1, both y above 1 and
127
- one of each. */
128
- if (ylo >= 1.0 && yhi >= 1.0) {
129
- /* Line segment is entirely above square */
130
- return sgn_dx * (xhi - xlo);
131
- }
132
-
133
- /* Adjust bounds if segment crosses axis (to exclude anything below
134
- axis) */
135
- if (ylo < 0.0) {
136
- ylo = 0.0;
137
- xlo = -c * inv_slope;
138
- }
139
-
140
- if (yhi < 0.0) {
141
- yhi = 0.0;
142
- xhi = -c * inv_slope;
143
- }
144
-
145
- if (ylo <= 1.0) {
146
- if (yhi <= 1.0) {
147
- /* Segment is entirely within the square.
148
- The case of zero slope will end up here without ever
149
- calling for inv_slope earlier. */
150
- return sgn_dx * 0.5 * (xhi - xlo) * (yhi + ylo);
113
+ /* And determine the bounds ignoring y for now */
114
+ if (xlo >= 1.0 || xhi <= 0.0) {
115
+ return 0.0;
116
+ }
117
+
118
+ xlo = MAX(xlo, 0.0);
119
+ xhi = MIN(xhi, 1.0);
120
+
121
+ /* Now look at y */
122
+ double slope = dy / dx;
123
+ ylo = y1 + slope * (xlo - x1);
124
+ yhi = y1 + slope * (xhi - x1);
125
+
126
+ /* Alternative code that may be more stable under certain circumstances */
127
+ /*
128
+ if (xlo < 0.0) {
129
+ xlo = 0.0;
130
+ ylo = y1 + (dy / dx) * (xlo - x1);
131
+ } else {
132
+ ylo = (sgn_dx > 0.0) ? y1 : y2;
133
+ }
134
+
135
+ if (xhi > 1.0) {
136
+ xhi = 1.0;
137
+ yhi = y1 + (dy / dx) * (xhi - x1);
138
+ } else {
139
+ yhi = (sgn_dx > 0.0) ? y2 : y1;
140
+ }
141
+ */
142
+
143
+ /* Trap segment entirely below axis */
144
+ if (ylo <= 0.0 && yhi <= 0.0) {
145
+ return 0.0;
146
+ }
147
+
148
+ /* There are four possibilities: both y below 1, both y above 1 and
149
+ one of each. */
150
+ if (ylo >= 1.0 && yhi >= 1.0) {
151
+ /* Line segment is entirely above square */
152
+ return sgn_dx * (xhi - xlo);
151
153
  }
152
154
 
153
- /* Otherwise, it must cross the top of the square */
154
- xtop = (1.0 - c) * inv_slope;
155
- return sgn_dx * (0.5 * (xtop - xlo) * (1.0 + ylo) + xhi - xtop);
156
- }
155
+ det = x1 * y2 - y1 * x2;
157
156
 
158
- xtop = (1.0 - c) * inv_slope;
159
- return sgn_dx * (0.5 * (xhi - xtop) * (1.0 + yhi) + xtop - xlo);
157
+ /* Adjust bounds if segment crosses axis (to exclude anything below
158
+ axis) */
159
+ if (ylo < 0.0) {
160
+ ylo = 0.0;
161
+ xlo = det / dy;
162
+ }
163
+
164
+ if (yhi < 0.0) {
165
+ yhi = 0.0;
166
+ xhi = det / dy;
167
+ }
168
+
169
+ if (ylo <= 1.0) {
170
+ if (yhi <= 1.0) {
171
+ /* Segment is entirely within the square.
172
+ The case of zero slope will end up here. */
173
+ return sgn_dx * 0.5 * (xhi - xlo) * (yhi + ylo);
174
+ }
175
+
176
+ /* Otherwise, it must cross the top of the square */
177
+ xtop = (dx + det) / dy;
178
+ return sgn_dx * (0.5 * (xtop - xlo) * (1.0 + ylo) + xhi - xtop);
179
+ }
180
+
181
+ xtop = (dx + det) / dy;
182
+ return sgn_dx * (0.5 * (xhi - xtop) * (1.0 + yhi) + xtop - xlo);
160
183
  }
161
184
 
162
185
  /**
@@ -170,37 +193,34 @@ sgarea(const double x1, const double y1, const double x2, const double y2,
170
193
  */
171
194
 
172
195
  double
173
- boxer(double is, double js,
174
- const double x[4], const double y[4],
175
- const int sgn_dx[4], const double slope[4], const double inv_slope[4]) {
176
- integer_t i;
177
- double sum;
178
- double px[4], py[4];
179
-
180
- assert(x);
181
- assert(y);
182
-
183
- is -= 0.5;
184
- js -= 0.5;
185
- /* Set up coords relative to unit square at origin Note that the
186
- +0.5s were added when this code was included in DRIZZLE */
187
-
188
- for (i = 0; i < 4; ++i) {
189
- px[i] = x[i] - is;
190
- py[i] = y[i] - js;
191
- }
192
-
193
- /* For each line in the polygon (or at this stage, input
194
- quadrilateral) calculate the area common to the unit square
195
- (allow negative area for subsequent `vector' addition of
196
- subareas). */
197
- sum = 0.0;
198
- for (i = 0; i < 4; ++i) {
199
- sum += sgarea(px[i], py[i], px[(i+1) & 0x3], py[(i+1) & 0x3],
200
- sgn_dx[i], slope[i], inv_slope[i]);
201
- }
202
-
203
- return sum;
196
+ boxer(double is, double js, const double x[4], const double y[4]) {
197
+ integer_t i;
198
+ double sum;
199
+ double px[4], py[4];
200
+
201
+ assert(x);
202
+ assert(y);
203
+
204
+ is -= 0.5;
205
+ js -= 0.5;
206
+ /* Set up coords relative to unit square at origin Note that the
207
+ +0.5s were added when this code was included in DRIZZLE */
208
+
209
+ for (i = 0; i < 4; ++i) {
210
+ px[i] = x[i] - is;
211
+ py[i] = y[i] - js;
212
+ }
213
+
214
+ /* For each line in the polygon (or at this stage, input
215
+ quadrilateral) calculate the area common to the unit square
216
+ (allow negative area for subsequent `vector' addition of
217
+ subareas). */
218
+ sum = 0.0;
219
+ for (i = 0; i < 4; ++i) {
220
+ sum += sgarea(px[i], py[i], px[(i + 1) & 0x3], py[(i + 1) & 0x3]);
221
+ }
222
+
223
+ return sum;
204
224
  }
205
225
 
206
226
  /** ---------------------------------------------------------------------------
@@ -284,8 +304,7 @@ compute_area(double is, double js, const double x[4], const double y[4]) {
284
304
  * segment inside the square
285
305
  */
286
306
  width = segment[1][0] - segment[0][0];
287
- area += 0.5 * width *
288
- ((1.0 + delta[0]) + (1.0 + delta[1]));
307
+ area += 0.5 * width * (2.0 + delta[0] + delta[1]);
289
308
  }
290
309
  }
291
310
 
@@ -313,14 +332,14 @@ compute_area(double is, double js, const double x[4], const double y[4]) {
313
332
  width = segment[1][0] - midpoint[0];
314
333
  /* Delta[0] is at the crossing point and thus zero
315
334
  */
316
- area += 0.5 * width * (1.0 + (1.0 + delta[1]));
335
+ area += 0.5 * width * (2.0 + delta[1]);
317
336
  } else {
318
337
  width = segment[1][0] - midpoint[0];
319
338
  area += width;
320
339
  width = midpoint[0] - segment[0][0];
321
340
  /* Delta[1] is at the crossing point and thus zero
322
341
  */
323
- area += 0.5 * width * ((1.0 + delta[0]) + 1.0);
342
+ area += 0.5 * width * (2.0 + delta[0]);
324
343
  }
325
344
 
326
345
  } else {
@@ -873,10 +892,8 @@ do_kernel_square(struct driz_param_t *p) {
873
892
  integer_t bv, i, j, ii, jj, min_ii, max_ii, min_jj, max_jj, nhit;
874
893
  integer_t osize[2], mapsize[2];
875
894
  float scale2, vc, d, dow;
876
- double dh, jaco, dover, w, dx, dy;
895
+ double dh, jaco, dover, w;
877
896
  double xin[4], yin[4], xout[4], yout[4];
878
- double slope[4], inv_slope[4];
879
- int sgn_dx[4];
880
897
 
881
898
  struct scanner s;
882
899
  int xmin, xmax, ymin, ymax, n;
@@ -918,32 +935,40 @@ do_kernel_square(struct driz_param_t *p) {
918
935
  }
919
936
 
920
937
  /* Set the input corner positions */
921
-
922
938
  yin[1] = yin[0] = (double)j + dh;
923
939
  yin[3] = yin[2] = (double)j - dh;
924
940
 
925
941
  for (i = xmin; i <= xmax; ++i) {
926
942
  nhit = 0;
927
943
 
928
- xin[3] = xin[0] = (double)i - dh;
929
- xin[2] = xin[1] = (double)i + dh;
930
-
931
- /* Assuming we don't need to extrapolate, call a more
932
- * efficient interpolator that takes advantage of the fact
933
- * that pixfrac<1 and that we are using a square grid.
934
- */
935
- if (i > 0 && i < mapsize[0] - 2 && j > 0 && j < mapsize[1] - 2) {
936
- if (interpolate_four_points(p, i, j, dh,
937
- xout, xout + 1, xout + 2, xout + 3,
938
- yout, yout + 1, yout + 2, yout + 3))
939
- goto _miss;
940
- } else {
941
- for (ii = 0; ii < 4; ++ii) {
944
+ // xin[3] = xin[0] = (double)i - dh;
945
+ // xin[2] = xin[1] = (double)i + dh;
946
+
947
+ // for (ii = 0; ii < 4; ++ii) {
948
+ // if (interpolate_point(p, xin[ii], yin[ii], xout + ii,
949
+ // yout + ii)) {
950
+ // goto _miss;
951
+ // }
952
+ // }
953
+
954
+ /* Assuming we don't need to extrapolate, call a more
955
+ * efficient interpolator that takes advantage of the fact
956
+ * that pixfrac<1 and that we are using a square grid.
957
+ */
958
+ if (i > 0 && i < mapsize[0] - 2 && j > 0 && j < mapsize[1] - 2) {
959
+ if (interpolate_four_points(p, i, j, dh, xout, xout + 1,
960
+ xout + 2, xout + 3, yout, yout + 1,
961
+ yout + 2, yout + 3))
962
+ goto _miss;
963
+ } else {
964
+ xin[3] = xin[0] = (double)i - dh;
965
+ xin[2] = xin[1] = (double)i + dh;
966
+ for (ii = 0; ii < 4; ++ii) {
942
967
  if (interpolate_point(p, xin[ii], yin[ii], xout + ii,
943
968
  yout + ii))
944
- goto _miss;
945
- }
946
- }
969
+ goto _miss;
970
+ }
971
+ }
947
972
 
948
973
  /* Work out the area of the quadrilateral on the output
949
974
  * grid. If the points are in clockwise order we get a
@@ -951,7 +976,7 @@ do_kernel_square(struct driz_param_t *p) {
951
976
  * will be negative, but so will the areas computed by
952
977
  * boxer, so it doesn't actually matter once we divide it
953
978
  * out.
954
- */
979
+ */
955
980
 
956
981
  jaco = 0.5f * ((xout[1] - xout[3]) * (yout[0] - yout[2]) -
957
982
  (xout[0] - xout[2]) * (yout[1] - yout[3]));
@@ -967,38 +992,20 @@ do_kernel_square(struct driz_param_t *p) {
967
992
  w = 1.0 / jaco;
968
993
  }
969
994
 
970
- /* Pre-compute slopes and sign of dx for each segment,
971
- since they will be used for all pixels in the loop.
972
- Also compute the inverse of the slope to avoid more
973
- division calls later.
974
- */
975
-
976
- for (ii = 0; ii < 4; ii++) {
977
- dx = xout[(ii+1) & 0x3] - xout[ii];
978
- dy = yout[(ii+1) & 0x3] - yout[ii];
979
- if (dx >= 0) {
980
- sgn_dx[ii] = 1;
981
- } else {
982
- sgn_dx[ii] = -1;
983
- }
984
- slope[ii] = dy / dx;
985
- inv_slope[ii] = dx / dy;
986
- }
987
-
988
995
  /* Loop over output pixels which could be affected */
989
996
  min_jj = MAX(fortran_round(min_doubles(yout, 4)), 0);
990
997
  max_jj = MIN(fortran_round(max_doubles(yout, 4)), osize[1] - 1);
991
998
  min_ii = MAX(fortran_round(min_doubles(xout, 4)), 0);
992
999
  max_ii = MIN(fortran_round(max_doubles(xout, 4)), osize[0] - 1);
993
1000
 
994
- for (jj = min_jj; jj <= max_jj; ++jj) {
995
- for (ii = min_ii; ii <= max_ii; ++ii) {
1001
+ for (jj = min_jj; jj <= max_jj; ++jj) {
1002
+ for (ii = min_ii; ii <= max_ii; ++ii) {
996
1003
  /* Call boxer to calculate overlap */
997
- //dover = compute_area((double)ii, (double)jj, xout, yout);
998
- dover = boxer((double)ii, (double)jj, xout, yout,
999
- sgn_dx, slope, inv_slope);
1004
+ // dover = compute_area((double)ii, (double)jj, xout, yout);
1005
+ dover = boxer((double)ii, (double)jj, xout, yout);
1000
1006
 
1001
- /* Could be positive or negative, depending on the sign of jaco */
1007
+ /* Could be positive or negative, depending on the sign of
1008
+ * jaco */
1002
1009
  if (dover != 0.0) {
1003
1010
  vc = get_pixel(p->output_counts, ii, jj);
1004
1011
 
@@ -1008,7 +1015,7 @@ do_kernel_square(struct driz_param_t *p) {
1008
1015
  ++nhit;
1009
1016
 
1010
1017
  /* If we are creating or modifying the context image we
1011
- do so here */
1018
+ do so here */
1012
1019
  if (p->output_context && dow > 0.0) {
1013
1020
  set_bit(p->output_context, ii, jj, bv);
1014
1021
  }
@@ -25,9 +25,7 @@ int dobox(struct driz_param_t *p);
25
25
 
26
26
  double compute_area(double is, double js, const double x[4], const double y[4]);
27
27
 
28
- double boxer(double is, double js, const double x[4], const double y[4],
29
- const int sgn_dx[4], const double slope[4],
30
- const double inv_slope[4]);
28
+ double boxer(double is, double js, const double x[4], const double y[4]);
31
29
 
32
30
  typedef int (*kernel_handler_t)(struct driz_param_t *);
33
31
 
@@ -19,6 +19,22 @@ static const double VERTEX_ATOL = 1.0e-12;
19
19
  static const double APPROX_ZERO = 1.0e3 * DBL_MIN;
20
20
  static const double MAX_INV_ERR = 0.03;
21
21
 
22
+ void
23
+ debug_print_polygon(const struct polygon *p, char *msg) {
24
+ int i;
25
+ if (msg) {
26
+ printf("%s\n", msg);
27
+ }
28
+ printf("Polygon with %d vertices:\n", p->npv);
29
+ for (i = 0; i < p->npv; ++i) {
30
+ printf(" Vertex %d: (%.20f, %.20f)\n", i, p->v[i].x, p->v[i].y);
31
+ }
32
+ printf("\n");
33
+ fflush(stdout);
34
+ }
35
+
36
+ void rotate_polygon(struct polygon *p, int pos);
37
+
22
38
  /** ---------------------------------------------------------------------------
23
39
  * Find the tighest bounding box around valid (finite) pixmap values.
24
40
  *
@@ -178,10 +194,10 @@ interpolate_point(struct driz_param_t *par, double xin, double yin,
178
194
  * four x points, then four y points.
179
195
  */
180
196
  int
181
- interpolate_four_points(struct driz_param_t *par,
182
- int ixcen, int iycen, double h,
183
- double *x1, double *x2, double *x3, double *x4,
184
- double *y1, double *y2, double *y3, double *y4) {
197
+ interpolate_four_points(struct driz_param_t *par, int ixcen, int iycen,
198
+ double h, double *x1, double *x2, double *x3,
199
+ double *x4, double *y1, double *y2, double *y3,
200
+ double *y4) {
185
201
  int i, j, nx2, ny2;
186
202
  double fac;
187
203
  npy_intp *ndim;
@@ -205,30 +221,30 @@ interpolate_four_points(struct driz_param_t *par,
205
221
  * center point, given by i=j=1 below. Loop ordered for speed
206
222
  * based on numpy array order.
207
223
  */
224
+ ixcen -= 1;
225
+ iycen -= 1;
208
226
  assert(h <= 1);
209
- assert(ixcen >= 1 && iycen >= 1 && ixcen <= nx2 && iycen <= ny2);
210
-
211
- for (j = 0; j <= 2; j++) {
212
- for (i = 0; i <= 2; i++) {
213
- p = get_pixmap(pixmap, i + ixcen - 1, j + iycen - 1);
214
- if (i == 1 && j == 1) {
215
- fac = (1 - h) * (1 - h);
216
- } else {
217
- if (i == 1 || j == 1) {
218
- fac = (1 - h) * h;
219
- } else {
220
- fac = h * h;
221
- }
222
- }
223
- f[i][j] = p[0] * fac;
224
- g[i][j] = p[1] * fac;
225
- }
227
+ assert(ixcen >= 0 && iycen >= 0 && ixcen < nx2 && iycen < ny2);
228
+
229
+ for (j = 0; j <= 2; ++j) {
230
+ for (i = 0; i <= 2; ++i) {
231
+ p = get_pixmap(pixmap, i + ixcen, j + iycen);
232
+ if (i == 1 && j == 1) {
233
+ fac = (1 - h) * (1 - h);
234
+ } else if (i == 1 || j == 1) {
235
+ fac = (1 - h) * h;
236
+ } else {
237
+ fac = h * h;
238
+ }
239
+ f[i][j] = p[0] * fac;
240
+ g[i][j] = p[1] * fac;
241
+ }
226
242
  }
227
243
 
228
244
  /* We return the vertices of a square going clockwise in the
229
245
  * original pixel grid.
230
246
  * Order in y: +h, +h, -h, -h; order in x: -h, +h, +h, -h
231
- */
247
+ */
232
248
 
233
249
  *x1 = f[0][1] + f[1][1] + f[0][2] + f[1][2];
234
250
  *y1 = g[0][1] + g[1][1] + g[0][2] + g[1][2];
@@ -242,17 +258,16 @@ interpolate_four_points(struct driz_param_t *par,
242
258
  *x4 = f[0][0] + f[1][0] + f[0][1] + f[1][1];
243
259
  *y4 = g[0][0] + g[1][0] + g[0][1] + g[1][1];
244
260
 
245
- if (npy_isnan(*x1) || npy_isnan(*y1) ||
246
- npy_isnan(*x2) || npy_isnan(*y2) ||
247
- npy_isnan(*x3) || npy_isnan(*y3) ||
248
- npy_isnan(*x4) || npy_isnan(*y4)) return 1;
261
+ if (npy_isnan(*x1) || npy_isnan(*y1) || npy_isnan(*x2) || npy_isnan(*y2) ||
262
+ npy_isnan(*x3) || npy_isnan(*y3) || npy_isnan(*x4) || npy_isnan(*y4)) {
263
+ return 1;
264
+ }
249
265
 
250
266
  return 0;
251
267
  }
252
268
 
253
269
  /** ---------------------------------------------------------------------------
254
270
  * Map an integer pixel position from the input to the output image.
255
- * Fall back on interpolation if the value at the point is undefined
256
271
  *
257
272
  * pixmap: The mapping of the pixel centers from input to output image
258
273
  * i - The index of the x coordinate
@@ -260,7 +275,6 @@ interpolate_four_points(struct driz_param_t *par,
260
275
  * x - X-coordinate of the point on the output image (output)
261
276
  * y - Y-coordinate of the point on the output image (output)
262
277
  */
263
-
264
278
  int
265
279
  map_pixel(PyArrayObject *pixmap, int i, int j, double *x, double *y) {
266
280
  double *pv = (double *)PyArray_GETPTR3(pixmap, j, i, 0);
@@ -442,6 +456,38 @@ is_point_strictly_in_hp(const struct vertex pt, const struct vertex v_,
442
456
  return ((area(v, pt) - area(v_, pt) - area(v, v_)) > 0.0);
443
457
  }
444
458
 
459
+ /**
460
+ * Rotates the vertices of a polygon by a specified number of positions.
461
+ *
462
+ * This function shifts the vertices of the given polygon `p` by `pos`
463
+ * positions. For each rotation, the first vertex is moved to the end of the
464
+ * vertex array, effectively rotating the polygon's vertex order. The operation
465
+ * is performed in-place and repeated `pos` times.
466
+ *
467
+ * @param p Pointer to the polygon structure to be rotated. The polygon is
468
+ * expected to have an array of vertices `v` and a count `npv`.
469
+ * @param pos Number of positions to rotate the polygon's vertices.
470
+ */
471
+ void
472
+ rotate_polygon(struct polygon *p, int pos) {
473
+ int i, k, m;
474
+ double x, y;
475
+
476
+ for (i = 0; i < pos; ++i) {
477
+ // rotate polygon p by pos vertices
478
+ // (move first pos vertices to the end of the polygon)
479
+ x = p->v[0].x;
480
+ y = p->v[0].y;
481
+ for (k = 0; k < p->npv - 1; ++k) {
482
+ m = mod(k + 1, p->npv);
483
+ p->v[k].x = p->v[m].x;
484
+ p->v[k].y = p->v[m].y;
485
+ }
486
+ p->v[p->npv - 1].x = x;
487
+ p->v[p->npv - 1].y = y;
488
+ }
489
+ }
490
+
445
491
  /**
446
492
  * Append a vertex to a polygon.
447
493
  *
@@ -619,12 +665,16 @@ clip_polygon_to_window(const struct polygon *p, const struct polygon *wnd,
619
665
  // compute intersection point:
620
666
  // https://en.wikipedia.org/wiki/Line–line_intersection
621
667
  d = area(dp, dw); // d != 0 because (v2_inside != v1_inside)
668
+
622
669
  app_ = area(*pv, *pv_);
623
670
  aww_ = area(*wv, *wv_);
671
+
624
672
  vi.x = (app_ * dw.x - aww_ * dp.x) / d;
625
673
  vi.y = (app_ * dw.y - aww_ * dp.y) / d;
626
674
 
627
- append_vertex(ppout, vi);
675
+ if (isfinite(vi.x) && isfinite(vi.y)) {
676
+ append_vertex(ppout, vi);
677
+ }
628
678
  if (v2_inside) {
629
679
  // outside to inside:
630
680
  append_vertex(ppout, *pv);
@@ -633,6 +683,7 @@ clip_polygon_to_window(const struct polygon *p, const struct polygon *wnd,
633
683
  // both edge vertices are inside
634
684
  append_vertex(ppout, *pv);
635
685
  }
686
+
636
687
  // nothing to do when both edge vertices are outside
637
688
 
638
689
  // advance polygon edge:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes