Generated by Cython 0.29.23
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
Raw output: traverse2d.c
+001: # -*- coding: utf-8 -*-
__pyx_t_1 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_t_1, __pyx_kp_u_traverse2d_line_99, __pyx_kp_u_Fast_pixel_traversal_for_2D_lin) < 0) __PYX_ERR(0, 1, __pyx_L1_error) if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
002:
003:
004: # pept is a Python library that unifies Positron Emission Particle
005: # Tracking (PEPT) research, including tracking, simulation, data analysis
006: # and visualisation tools.
007: #
008: # If you used this codebase or any software making use of it in a scientific
009: # publication, you must cite the following paper:
010: # Nicuşan AL, Windows-Yule CR. Positron emission particle tracking
011: # using machine learning. Review of Scientific Instruments.
012: # 2020 Jan 1;91(1):013329.
013: # https://doi.org/10.1063/1.5129251
014: #
015: # Copyright (C) 2019-2021 the pept developers
016: #
017: # This program is free software: you can redistribute it and/or modify
018: # it under the terms of the GNU General Public License as published by
019: # the Free Software Foundation, either version 3 of the License, or
020: # (at your option) any later version.
021: #
022: # This program is distributed in the hope that it will be useful,
023: # but WITHOUT ANY WARRANTY; without even the implied warranty of
024: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025: # GNU General Public License for more details.
026: #
027: # You should have received a copy of the GNU General Public License
028: # along with this program. If not, see <https://www.gnu.org/licenses/>.
029: # pept is a Python library that unifies Positron Emission Particle
030: # Tracking (PEPT) research, including tracking, simulation, data analysis
031: # and visualisation tools
032:
033:
034: # File : traverse2d.pyx
035: # License : GNU v3.0
036: # Author : Andrei Leonard Nicusan <a.l.nicusan@bham.ac.uk>
037: # Date : 03.02.2019
038:
039:
040: # cython: language_level=3
041: # cython: boundscheck=False
042: # cython: wraparound=False
043: # cython: initializedcheck=False
044: # cython: nonecheck=False
045: # cython: embedsignature=True
046: # cython: cdivision=True
047:
048:
049: from libc.float cimport DBL_MAX
050:
051:
+052: cdef inline double fabs(double x) nogil:
static CYTHON_INLINE double __pyx_f_4pept_9utilities_8traverse_10traverse2d_fabs(double __pyx_v_x) { double __pyx_r; /* … */ /* function exit code */ __pyx_L0:; return __pyx_r; }
+053: return (x if x >= 0 else -x)
if (((__pyx_v_x >= 0.0) != 0)) { __pyx_t_1 = __pyx_v_x; } else { __pyx_t_1 = (-__pyx_v_x); } __pyx_r = __pyx_t_1; goto __pyx_L0;
054:
055:
+056: cdef inline void swap(double *x, double *y) nogil:
static CYTHON_INLINE void __pyx_f_4pept_9utilities_8traverse_10traverse2d_swap(double *__pyx_v_x, double *__pyx_v_y) { double __pyx_v_aux; /* … */ /* function exit code */ }
057: cdef double aux
058:
+059: aux = x[0]
__pyx_v_aux = (__pyx_v_x[0]);
+060: x[0] = y[0]
(__pyx_v_x[0]) = (__pyx_v_y[0]);
+061: y[0] = aux
(__pyx_v_y[0]) = __pyx_v_aux;
062:
063:
+064: cdef double intersect(
static double __pyx_f_4pept_9utilities_8traverse_10traverse2d_intersect(double *__pyx_v_u, double *__pyx_v_v, double __pyx_v_xmin, double __pyx_v_xmax, double __pyx_v_ymin, double __pyx_v_ymax) { double __pyx_v_tmin; double __pyx_v_tmax; double __pyx_v_tymin; double __pyx_v_tymax; double __pyx_r; /* … */ /* function exit code */ __pyx_L0:; return __pyx_r; }
065: double[2] u,
066: double[2] v,
067: double xmin,
068: double xmax,
069: double ymin,
070: double ymax,
071: ) nogil:
072: '''Given a 2D line defined as L(t) = U + t V and an axis-aligned bounding
073: box (AABB), find the `t` for which the line intersects the box, if it
074: exists.
075:
076: It is assumed that the line starts *outside* the AABB, so that t == 0.0 is
077: only reserved for when the line does not intersect the box.
078: '''
079:
080: cdef double tmin, tmax, tymin, tymax
081:
+082: tmin = (xmin - u[0]) / v[0] # Relies on IEEE FP behaviour for div by 0
__pyx_v_tmin = ((__pyx_v_xmin - (__pyx_v_u[0])) / (__pyx_v_v[0]));
+083: tmax = (xmax - u[0]) / v[0]
__pyx_v_tmax = ((__pyx_v_xmax - (__pyx_v_u[0])) / (__pyx_v_v[0]));
084:
+085: tymin = (ymin - u[1]) / v[1]
__pyx_v_tymin = ((__pyx_v_ymin - (__pyx_v_u[1])) / (__pyx_v_v[1]));
+086: tymax = (ymax - u[1]) / v[1]
__pyx_v_tymax = ((__pyx_v_ymax - (__pyx_v_u[1])) / (__pyx_v_v[1]));
087:
+088: if tmin > tmax: swap(&tmin, &tmax)
__pyx_t_1 = ((__pyx_v_tmin > __pyx_v_tmax) != 0); if (__pyx_t_1) { __pyx_f_4pept_9utilities_8traverse_10traverse2d_swap((&__pyx_v_tmin), (&__pyx_v_tmax)); }
+089: if tymin > tymax: swap(&tymin, &tymax)
__pyx_t_1 = ((__pyx_v_tymin > __pyx_v_tymax) != 0); if (__pyx_t_1) { __pyx_f_4pept_9utilities_8traverse_10traverse2d_swap((&__pyx_v_tymin), (&__pyx_v_tymax)); }
090:
+091: if tmin > tymax or tymin > tmax: return 0.0
__pyx_t_2 = ((__pyx_v_tmin > __pyx_v_tymax) != 0); if (!__pyx_t_2) { } else { __pyx_t_1 = __pyx_t_2; goto __pyx_L6_bool_binop_done; } __pyx_t_2 = ((__pyx_v_tymin > __pyx_v_tmax) != 0); __pyx_t_1 = __pyx_t_2; __pyx_L6_bool_binop_done:; if (__pyx_t_1) { __pyx_r = 0.0; goto __pyx_L0; }
092:
+093: if tymin > tmin: tmin = tymin
__pyx_t_1 = ((__pyx_v_tymin > __pyx_v_tmin) != 0); if (__pyx_t_1) { __pyx_v_tmin = __pyx_v_tymin; }
+094: if tymax < tmax: tmax = tymax
__pyx_t_1 = ((__pyx_v_tymax < __pyx_v_tmax) != 0); if (__pyx_t_1) { __pyx_v_tmax = __pyx_v_tymax; }
095:
+096: return tmin
__pyx_r = __pyx_v_tmin; goto __pyx_L0;
097:
098:
+099: cpdef void traverse2d(
static PyObject *__pyx_pw_4pept_9utilities_8traverse_10traverse2d_1traverse2d(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static void __pyx_f_4pept_9utilities_8traverse_10traverse2d_traverse2d(__Pyx_memviewslice __pyx_v_pixels, __Pyx_memviewslice __pyx_v_lines, __Pyx_memviewslice __pyx_v_grid_x, __Pyx_memviewslice __pyx_v_grid_y, CYTHON_UNUSED int __pyx_skip_dispatch) { Py_ssize_t __pyx_v_n_lines; Py_ssize_t __pyx_v_nx; Py_ssize_t __pyx_v_ny; double __pyx_v_gsize_x; double __pyx_v_gsize_y; double __pyx_v_xmin; double __pyx_v_xmax; double __pyx_v_ymin; double __pyx_v_ymax; Py_ssize_t __pyx_v_ix; Py_ssize_t __pyx_v_iy; double __pyx_v_p1[2]; double __pyx_v_p2[2]; double __pyx_v_u[2]; double __pyx_v_v[2]; Py_ssize_t __pyx_v_step_x; Py_ssize_t __pyx_v_step_y; double __pyx_v_tnext_x; double __pyx_v_tnext_y; double __pyx_v_deltat_x; double __pyx_v_deltat_y; Py_ssize_t __pyx_v_i; Py_ssize_t __pyx_v_j; double __pyx_v_t; /* … */ /* function exit code */ } /* Python wrapper */ static PyObject *__pyx_pw_4pept_9utilities_8traverse_10traverse2d_1traverse2d(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static char __pyx_doc_4pept_9utilities_8traverse_10traverse2d_traverse2d[] = "traverse2d(double[:, :] pixels, double[:, :] lines, double[:] grid_x, double[:] grid_y) -> void\n Fast pixel traversal for 2D lines (or LoRs).\n\n ::\n\n Function Signature:\n traverse2d(\n double[:, :] pixels, # Initialised to zero!\n double[:, :] lines, # Has exactly 7 columns!\n double[:] grid_x, # Has pixels.shape[0] + 1 elements!\n double[:] grid_y, # Has pixels.shape[1] + 1 elements!\n )\n\n This function computes the number of lines that passes through each pixel,\n saving the result in `pixels`. It does so in an efficient manner, in which\n for every line, only the pixels that it passes through are traversed.\n\n As it is highly optimised, this function does not perform any checks on the\n validity of the input data. Please check the parameters before calling\n `traverse2d`, as it WILL segfault on wrong input data. Details are given\n below, along with an example call.\n\n Parameters\n ----------\n pixels : numpy.ndarray(dtype = numpy.float64, ndim = 2)\n The `pixels` parameter is a numpy.ndarray of shape (X, Y) that has been\n initialised to zeros before the function call. The values will be\n modified in-place in the function to reflect the number of lines that\n pass through each pixel.\n\n lines : numpy.ndarray(dtype = numpy.float64, ndim = 2)\n The `lines` parameter is a numpy.ndarray of shape(N, 5), where each row\n is formatted as [time, x1, y1, x2, y2]. Only indices 1:5 will be used\n as the two points P1 = [x1, y1] and P2 = [x2, y2] defining the line (or\n LoR).\n\n grid_x : numpy.ndarray(dtype = numpy.float64, ndim = 1)\n The grid_x parameter is a one-dimensional grid that delimits the pixels\n in the x-dimension. It must be *sorted* in ascending order with\n *equally-spaced* numbers and length X + 1 (pixels.shap""e[0] + 1).\n\n grid_y : numpy.ndarray(dtype = numpy.float64, ndim = 1)\n The grid_y parameter is a one-dimensional grid that delimits the pixels\n in the y-dimension. It must be *sorted* in ascending order with\n *equally-spaced* numbers and length Y + 1 (pixels.shape[1] + 1).\n\n Examples\n --------\n The input parameters can be easily generated using numpy before calling the\n function. For example, if a plane of 300 x 400 is split into\n 30 x 40 pixels, a possible code would be:\n\n >>> import numpy as np\n >>> from pept.utilities.traverse import traverse2d\n >>>\n >>> plane = [300, 400]\n >>> number_of_pixels = [30, 40]\n >>> pixels = np.zeros(number_of_pixels)\n\n The grid has one extra element than the number of pixels. For example, 5\n pixels between 0 and 5 would be delimited by the grid [0, 1, 2, 3, 4, 5]\n which has 6 elements (see off-by-one errors - story of my life).\n\n >>> grid_x = np.linspace(0, plane[0], number_of_pixels[0] + 1)\n >>> grid_y = np.linspace(0, plane[1], number_of_pixels[1] + 1)\n >>>\n >>> random_lines = np.random.random((100, 5)) * 100\n\n Calling `traverse2d` will modify `pixels` in-place.\n\n >>> traverse2d(pixels, random_lines, grid_x, grid_y)\n\n Notes\n -----\n This function is an adaptation of a widely-used algorithm [1]_, optimised\n for PEPT LoRs traversal.\n\n .. [1] Amanatides J, Woo A. A fast voxel traversal algorithm for ray tracing.\n InEurographics 1987 Aug 24 (Vol. 87, No. 3, pp. 3-10).\n\n "; static PyObject *__pyx_pw_4pept_9utilities_8traverse_10traverse2d_1traverse2d(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { __Pyx_memviewslice __pyx_v_pixels = { 0, 0, { 0 }, { 0 }, { 0 } }; __Pyx_memviewslice __pyx_v_lines = { 0, 0, { 0 }, { 0 }, { 0 } }; __Pyx_memviewslice __pyx_v_grid_x = { 0, 0, { 0 }, { 0 }, { 0 } }; __Pyx_memviewslice __pyx_v_grid_y = { 0, 0, { 0 }, { 0 }, { 0 } }; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("traverse2d (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_pixels,&__pyx_n_s_lines,&__pyx_n_s_grid_x,&__pyx_n_s_grid_y,0}; PyObject* values[4] = {0,0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3); CYTHON_FALLTHROUGH; case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); CYTHON_FALLTHROUGH; case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_pixels)) != 0)) kw_args--; else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_lines)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("traverse2d", 1, 4, 4, 1); __PYX_ERR(0, 99, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 2: if (likely((values[2] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_grid_x)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("traverse2d", 1, 4, 4, 2); __PYX_ERR(0, 99, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 3: if (likely((values[3] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_grid_y)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("traverse2d", 1, 4, 4, 3); __PYX_ERR(0, 99, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "traverse2d") < 0)) __PYX_ERR(0, 99, __pyx_L3_error) } } else if (PyTuple_GET_SIZE(__pyx_args) != 4) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[2] = PyTuple_GET_ITEM(__pyx_args, 2); values[3] = PyTuple_GET_ITEM(__pyx_args, 3); } __pyx_v_pixels = __Pyx_PyObject_to_MemoryviewSlice_dsds_double(values[0], PyBUF_WRITABLE); if (unlikely(!__pyx_v_pixels.memview)) __PYX_ERR(0, 100, __pyx_L3_error) __pyx_v_lines = __Pyx_PyObject_to_MemoryviewSlice_dsds_double(values[1], PyBUF_WRITABLE); if (unlikely(!__pyx_v_lines.memview)) __PYX_ERR(0, 101, __pyx_L3_error) __pyx_v_grid_x = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[2], PyBUF_WRITABLE); if (unlikely(!__pyx_v_grid_x.memview)) __PYX_ERR(0, 102, __pyx_L3_error) __pyx_v_grid_y = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[3], PyBUF_WRITABLE); if (unlikely(!__pyx_v_grid_y.memview)) __PYX_ERR(0, 103, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("traverse2d", 1, 4, 4, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 99, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("pept.utilities.traverse.traverse2d.traverse2d", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4pept_9utilities_8traverse_10traverse2d_traverse2d(__pyx_self, __pyx_v_pixels, __pyx_v_lines, __pyx_v_grid_x, __pyx_v_grid_y); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_4pept_9utilities_8traverse_10traverse2d_traverse2d(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_pixels, __Pyx_memviewslice __pyx_v_lines, __Pyx_memviewslice __pyx_v_grid_x, __Pyx_memviewslice __pyx_v_grid_y) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("traverse2d", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = __Pyx_void_to_None(__pyx_f_4pept_9utilities_8traverse_10traverse2d_traverse2d(__pyx_v_pixels, __pyx_v_lines, __pyx_v_grid_x, __pyx_v_grid_y, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 99, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("pept.utilities.traverse.traverse2d.traverse2d", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __PYX_XDEC_MEMVIEW(&__pyx_v_pixels, 1); __PYX_XDEC_MEMVIEW(&__pyx_v_lines, 1); __PYX_XDEC_MEMVIEW(&__pyx_v_grid_x, 1); __PYX_XDEC_MEMVIEW(&__pyx_v_grid_y, 1); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
100: double[:, :] pixels, # Initialised to zero!
101: double[:, :] lines, # Has exactly 5 columns!
102: double[:] grid_x, # Has pixels.shape[0] + 1 elements!
103: double[:] grid_y, # Has pixels.shape[1] + 1 elements!
104: ) nogil:
105: ''' Fast pixel traversal for 2D lines (or LoRs).
106:
107: ::
108:
109: Function Signature:
110: traverse2d(
111: double[:, :] pixels, # Initialised to zero!
112: double[:, :] lines, # Has exactly 7 columns!
113: double[:] grid_x, # Has pixels.shape[0] + 1 elements!
114: double[:] grid_y, # Has pixels.shape[1] + 1 elements!
115: )
116:
117: This function computes the number of lines that passes through each pixel,
118: saving the result in `pixels`. It does so in an efficient manner, in which
119: for every line, only the pixels that it passes through are traversed.
120:
121: As it is highly optimised, this function does not perform any checks on the
122: validity of the input data. Please check the parameters before calling
123: `traverse2d`, as it WILL segfault on wrong input data. Details are given
124: below, along with an example call.
125:
126: Parameters
127: ----------
128: pixels : numpy.ndarray(dtype = numpy.float64, ndim = 2)
129: The `pixels` parameter is a numpy.ndarray of shape (X, Y) that has been
130: initialised to zeros before the function call. The values will be
131: modified in-place in the function to reflect the number of lines that
132: pass through each pixel.
133:
134: lines : numpy.ndarray(dtype = numpy.float64, ndim = 2)
135: The `lines` parameter is a numpy.ndarray of shape(N, 5), where each row
136: is formatted as [time, x1, y1, x2, y2]. Only indices 1:5 will be used
137: as the two points P1 = [x1, y1] and P2 = [x2, y2] defining the line (or
138: LoR).
139:
140: grid_x : numpy.ndarray(dtype = numpy.float64, ndim = 1)
141: The grid_x parameter is a one-dimensional grid that delimits the pixels
142: in the x-dimension. It must be *sorted* in ascending order with
143: *equally-spaced* numbers and length X + 1 (pixels.shape[0] + 1).
144:
145: grid_y : numpy.ndarray(dtype = numpy.float64, ndim = 1)
146: The grid_y parameter is a one-dimensional grid that delimits the pixels
147: in the y-dimension. It must be *sorted* in ascending order with
148: *equally-spaced* numbers and length Y + 1 (pixels.shape[1] + 1).
149:
150: Examples
151: --------
152: The input parameters can be easily generated using numpy before calling the
153: function. For example, if a plane of 300 x 400 is split into
154: 30 x 40 pixels, a possible code would be:
155:
156: >>> import numpy as np
157: >>> from pept.utilities.traverse import traverse2d
158: >>>
159: >>> plane = [300, 400]
160: >>> number_of_pixels = [30, 40]
161: >>> pixels = np.zeros(number_of_pixels)
162:
163: The grid has one extra element than the number of pixels. For example, 5
164: pixels between 0 and 5 would be delimited by the grid [0, 1, 2, 3, 4, 5]
165: which has 6 elements (see off-by-one errors - story of my life).
166:
167: >>> grid_x = np.linspace(0, plane[0], number_of_pixels[0] + 1)
168: >>> grid_y = np.linspace(0, plane[1], number_of_pixels[1] + 1)
169: >>>
170: >>> random_lines = np.random.random((100, 5)) * 100
171:
172: Calling `traverse2d` will modify `pixels` in-place.
173:
174: >>> traverse2d(pixels, random_lines, grid_x, grid_y)
175:
176: Notes
177: -----
178: This function is an adaptation of a widely-used algorithm [1]_, optimised
179: for PEPT LoRs traversal.
180:
181: .. [1] Amanatides J, Woo A. A fast voxel traversal algorithm for ray tracing.
182: InEurographics 1987 Aug 24 (Vol. 87, No. 3, pp. 3-10).
183:
184: '''
185:
+186: n_lines = lines.shape[0]
__pyx_v_n_lines = (__pyx_v_lines.shape[0]);
+187: cdef Py_ssize_t nx = pixels.shape[0]
__pyx_v_nx = (__pyx_v_pixels.shape[0]);
+188: cdef Py_ssize_t ny = pixels.shape[1]
__pyx_v_ny = (__pyx_v_pixels.shape[1]);
189:
190: # Grid size
+191: cdef double gsize_x = grid_x[1] - grid_x[0]
__pyx_t_1 = 1; __pyx_t_2 = 0; __pyx_v_gsize_x = ((*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_1 * __pyx_v_grid_x.strides[0]) ))) - (*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_2 * __pyx_v_grid_x.strides[0]) ))));
+192: cdef double gsize_y = grid_y[1] - grid_y[0]
__pyx_t_2 = 1; __pyx_t_1 = 0; __pyx_v_gsize_y = ((*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_2 * __pyx_v_grid_y.strides[0]) ))) - (*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_1 * __pyx_v_grid_y.strides[0]) ))));
193:
194: # Delimiting grid
+195: cdef double xmin = grid_x[0]
__pyx_t_1 = 0; __pyx_v_xmin = (*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_1 * __pyx_v_grid_x.strides[0]) )));
+196: cdef double xmax = grid_x[nx]
__pyx_t_1 = __pyx_v_nx; __pyx_v_xmax = (*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_1 * __pyx_v_grid_x.strides[0]) )));
197:
+198: cdef double ymin = grid_y[0]
__pyx_t_1 = 0; __pyx_v_ymin = (*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_1 * __pyx_v_grid_y.strides[0]) )));
+199: cdef double ymax = grid_y[ny]
__pyx_t_1 = __pyx_v_ny; __pyx_v_ymax = (*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_1 * __pyx_v_grid_y.strides[0]) )));
200:
201: # The current pixel indices [ix, iy] that the line passes
202: # through.
203: cdef Py_ssize_t ix, iy
204:
205: # Define a line as L(t) = U + t V
206: # If an LoR is defined as two points P1 and P2, then
207: # U = P1 and V = P2 - P1
208: cdef double[2] p1, p2, u, v
209:
210: # The step [step_x, step_y, step_z] defines the sense of the LoR.
211: # If V[0] is positive, then step_x = 1
212: # If V[0] is negative, then step_x = -1
213: cdef Py_ssize_t step_x, step_y
214:
215: # The value of t at which the line passes through to the next
216: # pixel, for each dimension.
217: cdef double tnext_x, tnext_y
218:
219: # deltat indicates how far along the ray we must move (in units of
220: # t) for each component to be equal to the size of the pixel in
221: # that dimension.
222: cdef double deltat_x, deltat_y
223:
224: cdef Py_ssize_t i, j
225:
+226: for i in range(n_lines):
__pyx_t_3 = __pyx_v_n_lines; __pyx_t_4 = __pyx_t_3; for (__pyx_t_5 = 0; __pyx_t_5 < __pyx_t_4; __pyx_t_5+=1) { __pyx_v_i = __pyx_t_5;
227:
+228: for j in range(2):
for (__pyx_t_6 = 0; __pyx_t_6 < 2; __pyx_t_6+=1) { __pyx_v_j = __pyx_t_6;
+229: p1[j] = lines[i, 1 + j]
__pyx_t_1 = __pyx_v_i; __pyx_t_2 = (1 + __pyx_v_j); (__pyx_v_p1[__pyx_v_j]) = (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_lines.data + __pyx_t_1 * __pyx_v_lines.strides[0]) ) + __pyx_t_2 * __pyx_v_lines.strides[1]) )));
+230: p2[j] = lines[i, 3 + j]
__pyx_t_2 = __pyx_v_i; __pyx_t_1 = (3 + __pyx_v_j); (__pyx_v_p2[__pyx_v_j]) = (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_lines.data + __pyx_t_2 * __pyx_v_lines.strides[0]) ) + __pyx_t_1 * __pyx_v_lines.strides[1]) )));
231:
+232: u[j] = p1[j]
(__pyx_v_u[__pyx_v_j]) = (__pyx_v_p1[__pyx_v_j]);
+233: v[j] = p2[j] - p1[j]
(__pyx_v_v[__pyx_v_j]) = ((__pyx_v_p2[__pyx_v_j]) - (__pyx_v_p1[__pyx_v_j])); }
234:
235: ##############################################################
236: # Initialisation stage
237:
+238: step_x = 1 if v[0] >= 0 else -1
if ((((__pyx_v_v[0]) >= 0.0) != 0)) { __pyx_t_6 = 1; } else { __pyx_t_6 = -1L; } __pyx_v_step_x = __pyx_t_6;
+239: step_y = 1 if v[1] >= 0 else -1
if ((((__pyx_v_v[1]) >= 0.0) != 0)) { __pyx_t_6 = 1; } else { __pyx_t_6 = -1L; } __pyx_v_step_y = __pyx_t_6;
240:
241: # If the first point is outside the box, find the first pixel it hits
+242: if (u[0] < xmin or u[0] > xmax or
__pyx_t_8 = (((__pyx_v_u[0]) < __pyx_v_xmin) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L8_bool_binop_done; } __pyx_t_8 = (((__pyx_v_u[0]) > __pyx_v_xmax) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L8_bool_binop_done; } /* … */ if (__pyx_t_7) { /* … */ }
+243: u[1] < ymin or u[1] > ymax):
__pyx_t_8 = (((__pyx_v_u[1]) < __pyx_v_ymin) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L8_bool_binop_done; } __pyx_t_8 = (((__pyx_v_u[1]) > __pyx_v_ymax) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L8_bool_binop_done:;
244:
+245: t = intersect(u, v, xmin, xmax, ymin, ymax)
__pyx_v_t = __pyx_f_4pept_9utilities_8traverse_10traverse2d_intersect(__pyx_v_u, __pyx_v_v, __pyx_v_xmin, __pyx_v_xmax, __pyx_v_ymin, __pyx_v_ymax);
246:
247: # No intersection
+248: if t == 0.0:
__pyx_t_7 = ((__pyx_v_t == 0.0) != 0); if (__pyx_t_7) { /* … */ }
+249: continue
goto __pyx_L3_continue;
250:
251: # Overwrite U to correspond to the first intersection point
+252: for j in range(2):
for (__pyx_t_6 = 0; __pyx_t_6 < 2; __pyx_t_6+=1) { __pyx_v_j = __pyx_t_6;
+253: u[j] = u[j] + t * v[j]
(__pyx_v_u[__pyx_v_j]) = ((__pyx_v_u[__pyx_v_j]) + (__pyx_v_t * (__pyx_v_v[__pyx_v_j]))); }
254:
255: # Corner case: every pixel is defined as lower boundary (inclusive) and
256: # upper boundary (exclusive). Therefore, at the upper end of the pixel
257: # grid an undefined case occurs. If a point lies right at the upper
258: # boundary of the pixel space, "move it" a bit lower on the line
+259: if u[0] == xmax or u[1] == ymax:
__pyx_t_8 = (((__pyx_v_u[0]) == __pyx_v_xmax) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L16_bool_binop_done; } __pyx_t_8 = (((__pyx_v_u[1]) == __pyx_v_ymax) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L16_bool_binop_done:; if (__pyx_t_7) { /* … */ }
+260: for j in range(2):
for (__pyx_t_6 = 0; __pyx_t_6 < 2; __pyx_t_6+=1) { __pyx_v_j = __pyx_t_6;
+261: u[j] = u[j] + 1e-5 * v[j]
(__pyx_v_u[__pyx_v_j]) = ((__pyx_v_u[__pyx_v_j]) + (1e-5 * (__pyx_v_v[__pyx_v_j]))); }
262:
263: # If, for dimension x, there are 5 pixels between coordinates 0
264: # and 5, then the delimiting grid is [0, 1, 2, 3, 4, 5].
265: # If the line starts at 1.5, then it is part of the pixel at
266: # index 1
267:
+268: ix = <int>(u[0] / gsize_x)
__pyx_v_ix = ((int)((__pyx_v_u[0]) / __pyx_v_gsize_x));
+269: iy = <int>(u[1] / gsize_y)
__pyx_v_iy = ((int)((__pyx_v_u[1]) / __pyx_v_gsize_y));
270:
271: # Check the indices are inside the pixel grid
+272: if (ix < 0 or ix >= nx or iy < 0 or iy >= ny):
__pyx_t_8 = ((__pyx_v_ix < 0) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L21_bool_binop_done; } __pyx_t_8 = ((__pyx_v_ix >= __pyx_v_nx) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L21_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iy < 0) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L21_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iy >= __pyx_v_ny) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L21_bool_binop_done:; if (__pyx_t_7) { /* … */ }
+273: continue
goto __pyx_L3_continue;
274:
275: # If the line is going "up", the next pixel is the next one
276: # If the line is going "down", the next pixel is the current one
+277: if v[0] > 0:
__pyx_t_7 = (((__pyx_v_v[0]) > 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L25; }
+278: tnext_x = (grid_x[ix + 1] - u[0]) / v[0]
__pyx_t_1 = (__pyx_v_ix + 1); __pyx_v_tnext_x = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_1 * __pyx_v_grid_x.strides[0]) ))) - (__pyx_v_u[0])) / (__pyx_v_v[0]));
+279: elif v[0] < 0:
__pyx_t_7 = (((__pyx_v_v[0]) < 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L25; }
+280: tnext_x = (grid_x[ix] - u[0]) / v[0]
__pyx_t_1 = __pyx_v_ix; __pyx_v_tnext_x = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_1 * __pyx_v_grid_x.strides[0]) ))) - (__pyx_v_u[0])) / (__pyx_v_v[0]));
281: else:
+282: tnext_x = DBL_MAX
/*else*/ { __pyx_v_tnext_x = DBL_MAX; } __pyx_L25:;
283:
+284: if v[1] > 0:
__pyx_t_7 = (((__pyx_v_v[1]) > 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L26; }
+285: tnext_y = (grid_y[iy + 1] - u[1]) / v[1]
__pyx_t_1 = (__pyx_v_iy + 1); __pyx_v_tnext_y = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_1 * __pyx_v_grid_y.strides[0]) ))) - (__pyx_v_u[1])) / (__pyx_v_v[1]));
+286: elif v[1] < 0:
__pyx_t_7 = (((__pyx_v_v[1]) < 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L26; }
+287: tnext_y = (grid_y[iy] - u[1]) / v[1]
__pyx_t_1 = __pyx_v_iy; __pyx_v_tnext_y = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_1 * __pyx_v_grid_y.strides[0]) ))) - (__pyx_v_u[1])) / (__pyx_v_v[1]));
288: else:
+289: tnext_y = DBL_MAX
/*else*/ { __pyx_v_tnext_y = DBL_MAX; } __pyx_L26:;
290:
+291: deltat_x = fabs((grid_x[1] - grid_x[0]) / v[0]) if v[0] else 0
if (((__pyx_v_v[0]) != 0)) { __pyx_t_1 = 1; __pyx_t_2 = 0; __pyx_t_9 = __pyx_f_4pept_9utilities_8traverse_10traverse2d_fabs((((*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_1 * __pyx_v_grid_x.strides[0]) ))) - (*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_2 * __pyx_v_grid_x.strides[0]) )))) / (__pyx_v_v[0]))); } else { __pyx_t_9 = 0.0; } __pyx_v_deltat_x = __pyx_t_9;
+292: deltat_y = fabs((grid_y[1] - grid_y[0]) / v[1]) if v[1] else 0
if (((__pyx_v_v[1]) != 0)) { __pyx_t_2 = 1; __pyx_t_1 = 0; __pyx_t_9 = __pyx_f_4pept_9utilities_8traverse_10traverse2d_fabs((((*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_2 * __pyx_v_grid_y.strides[0]) ))) - (*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_1 * __pyx_v_grid_y.strides[0]) )))) / (__pyx_v_v[1]))); } else { __pyx_t_9 = 0.0; } __pyx_v_deltat_y = __pyx_t_9;
293:
294: ###############################################################
295: # Incremental traversal stage
296:
297: # Loop until we reach the last pixel in space
+298: while (ix < nx and iy < ny) and (ix >= 0 and iy >= 0):
while (1) { __pyx_t_8 = ((__pyx_v_ix < __pyx_v_nx) != 0); if (__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L29_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iy < __pyx_v_ny) != 0); if (__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L29_bool_binop_done; } __pyx_t_8 = ((__pyx_v_ix >= 0) != 0); if (__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L29_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iy >= 0) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L29_bool_binop_done:; if (!__pyx_t_7) break;
299:
+300: pixels[ix, iy] += 1
__pyx_t_1 = __pyx_v_ix; __pyx_t_2 = __pyx_v_iy; *((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_pixels.data + __pyx_t_1 * __pyx_v_pixels.strides[0]) ) + __pyx_t_2 * __pyx_v_pixels.strides[1]) )) += 1.0;
301:
302: # Select the minimum t that makes the line pass
303: # through to the next pixel
+304: if tnext_x < tnext_y:
__pyx_t_7 = ((__pyx_v_tnext_x < __pyx_v_tnext_y) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L33; }
305: # If the next pixel falls beyond the end of the line (that is
306: # at t = 1), then stop the traversal stage
+307: if tnext_x > 1.:
__pyx_t_7 = ((__pyx_v_tnext_x > 1.) != 0); if (__pyx_t_7) { /* … */ }
+308: break
goto __pyx_L28_break;
309:
+310: ix = ix + step_x
__pyx_v_ix = (__pyx_v_ix + __pyx_v_step_x);
+311: tnext_x = tnext_x + deltat_x
__pyx_v_tnext_x = (__pyx_v_tnext_x + __pyx_v_deltat_x);
312: else:
+313: if tnext_y > 1.:
/*else*/ { __pyx_t_7 = ((__pyx_v_tnext_y > 1.) != 0); if (__pyx_t_7) { /* … */ }
+314: break
goto __pyx_L28_break;
315:
+316: iy = iy + step_y
__pyx_v_iy = (__pyx_v_iy + __pyx_v_step_y);
+317: tnext_y = tnext_y + deltat_y
__pyx_v_tnext_y = (__pyx_v_tnext_y + __pyx_v_deltat_y); } __pyx_L33:; } __pyx_L28_break:; __pyx_L3_continue:; }
318: