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: traverse3d.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_traverse3d_line_110, __pyx_kp_u_Fast_voxel_traversal_for_3D_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 : traverse3d.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_10traverse3d_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_10traverse3d_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_10traverse3d_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_zmin, double __pyx_v_zmax) { double __pyx_v_tmin; double __pyx_v_tmax; double __pyx_v_tymin; double __pyx_v_tymax; double __pyx_v_tzmin; double __pyx_v_tzmax; double __pyx_r; /* … */ /* function exit code */ __pyx_L0:; return __pyx_r; }
065: double[3] u,
066: double[3] v,
067: double xmin,
068: double xmax,
069: double ymin,
070: double ymax,
071: double zmin,
072: double zmax,
073: ) nogil:
074: '''Given a 3D line defined as L(t) = U + t V and an axis-aligned bounding
075: box (AABB), find the `t` for which the line intersects the box, if it
076: exists.
077:
078: It is assumed that the line starts *outside* the AABB, so that t == 0.0 is
079: only reserved for when the line does not intersect the box.
080: '''
081:
082: cdef double tmin, tmax, tymin, tymax, tzmin, tzmax
083:
+084: 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]));
+085: tmax = (xmax - u[0]) / v[0]
__pyx_v_tmax = ((__pyx_v_xmax - (__pyx_v_u[0])) / (__pyx_v_v[0]));
086:
+087: tymin = (ymin - u[1]) / v[1]
__pyx_v_tymin = ((__pyx_v_ymin - (__pyx_v_u[1])) / (__pyx_v_v[1]));
+088: tymax = (ymax - u[1]) / v[1]
__pyx_v_tymax = ((__pyx_v_ymax - (__pyx_v_u[1])) / (__pyx_v_v[1]));
089:
+090: tzmin = (zmin - u[2]) / v[2]
__pyx_v_tzmin = ((__pyx_v_zmin - (__pyx_v_u[2])) / (__pyx_v_v[2]));
+091: tzmax = (zmax - u[2]) / v[2]
__pyx_v_tzmax = ((__pyx_v_zmax - (__pyx_v_u[2])) / (__pyx_v_v[2]));
092:
+093: 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_10traverse3d_swap((&__pyx_v_tmin), (&__pyx_v_tmax)); }
+094: 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_10traverse3d_swap((&__pyx_v_tymin), (&__pyx_v_tymax)); }
+095: if tzmin > tzmax: swap(&tzmin, &tzmax)
__pyx_t_1 = ((__pyx_v_tzmin > __pyx_v_tzmax) != 0); if (__pyx_t_1) { __pyx_f_4pept_9utilities_8traverse_10traverse3d_swap((&__pyx_v_tzmin), (&__pyx_v_tzmax)); }
096:
+097: 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_L7_bool_binop_done; } __pyx_t_2 = ((__pyx_v_tymin > __pyx_v_tmax) != 0); __pyx_t_1 = __pyx_t_2; __pyx_L7_bool_binop_done:; if (__pyx_t_1) { __pyx_r = 0.0; goto __pyx_L0; }
098:
+099: 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; }
+100: 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; }
101:
+102: if tmin > tzmax or tzmin > tmax: return 0.0
__pyx_t_2 = ((__pyx_v_tmin > __pyx_v_tzmax) != 0); if (!__pyx_t_2) { } else { __pyx_t_1 = __pyx_t_2; goto __pyx_L12_bool_binop_done; } __pyx_t_2 = ((__pyx_v_tzmin > __pyx_v_tmax) != 0); __pyx_t_1 = __pyx_t_2; __pyx_L12_bool_binop_done:; if (__pyx_t_1) { __pyx_r = 0.0; goto __pyx_L0; }
103:
+104: if tzmin > tmin: tmin = tzmin
__pyx_t_1 = ((__pyx_v_tzmin > __pyx_v_tmin) != 0); if (__pyx_t_1) { __pyx_v_tmin = __pyx_v_tzmin; }
+105: if tzmax < tmax: tmax = tzmax
__pyx_t_1 = ((__pyx_v_tzmax < __pyx_v_tmax) != 0); if (__pyx_t_1) { __pyx_v_tmax = __pyx_v_tzmax; }
106:
+107: return tmin
__pyx_r = __pyx_v_tmin; goto __pyx_L0;
108:
109:
+110: cpdef void traverse3d(
static PyObject *__pyx_pw_4pept_9utilities_8traverse_10traverse3d_1traverse3d(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static void __pyx_f_4pept_9utilities_8traverse_10traverse3d_traverse3d(__Pyx_memviewslice __pyx_v_voxels, __Pyx_memviewslice __pyx_v_lines, __Pyx_memviewslice __pyx_v_grid_x, __Pyx_memviewslice __pyx_v_grid_y, __Pyx_memviewslice __pyx_v_grid_z, 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; Py_ssize_t __pyx_v_nz; double __pyx_v_gsize_x; double __pyx_v_gsize_y; double __pyx_v_gsize_z; double __pyx_v_xmin; double __pyx_v_xmax; double __pyx_v_ymin; double __pyx_v_ymax; double __pyx_v_zmin; double __pyx_v_zmax; Py_ssize_t __pyx_v_ix; Py_ssize_t __pyx_v_iy; Py_ssize_t __pyx_v_iz; double __pyx_v_p1[3]; double __pyx_v_p2[3]; double __pyx_v_u[3]; double __pyx_v_v[3]; double __pyx_v_t; Py_ssize_t __pyx_v_step_x; Py_ssize_t __pyx_v_step_y; Py_ssize_t __pyx_v_step_z; double __pyx_v_tnext_x; double __pyx_v_tnext_y; double __pyx_v_tnext_z; double __pyx_v_deltat_x; double __pyx_v_deltat_y; double __pyx_v_deltat_z; Py_ssize_t __pyx_v_i; Py_ssize_t __pyx_v_j; /* … */ /* function exit code */ } /* Python wrapper */ static PyObject *__pyx_pw_4pept_9utilities_8traverse_10traverse3d_1traverse3d(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static char __pyx_doc_4pept_9utilities_8traverse_10traverse3d_traverse3d[] = "traverse3d(double[:, :, :] voxels, double[:, :] lines, double[:] grid_x, double[:] grid_y, double[:] grid_z) -> void\n Fast voxel traversal for 3D lines (or LoRs).\n\n ::\n\n Function Signature:\n traverse3d(\n long[:, :, :] voxels, # Initialised!\n double[:, :] lines, # Has exactly 7 columns!\n double[:] grid_x, # Has voxels.shape[0] + 1 elements!\n double[:] grid_y, # Has voxels.shape[1] + 1 elements!\n double[:] grid_z # Has voxels.shape[2] + 1 elements!\n )\n\n This function computes the number of lines that passes through each voxel,\n saving the result in `voxels`. It does so in an efficient manner, in which\n for every line, only the voxels that is 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 `traverse3d`, as it WILL segfault on wrong input data. Details are given\n below, along with an example call.\n\n Parameters\n ----------\n voxels : numpy.ndarray(dtype = numpy.float64, ndim = 3)\n The `voxels` parameter is a numpy.ndarray of shape (X, Y, Z) that\n has been initialised to zeros before the function call. The values\n will be modified in-place in the function to reflect the number of\n lines that pass through each voxel.\n\n lines : numpy.ndarray(dtype = numpy.float64, ndim = 2)\n The `lines` parameter is a numpy.ndarray of shape(N, 7), where each\n row is formatted as [time, x1, y1, z1, x2, y2, z2]. Only indices 1:7\n will be used as the two points P1 = [x1, y1, z2] and P2 = [x2, y2, z2]\n defining the line (or 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\n voxels in the x-dimen""sion. It must be *sorted* in ascending order\n with *equally-spaced* numbers and length X + 1 (voxels.shape[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\n voxels in the y-dimension. It must be *sorted* in ascending order\n with *equally-spaced* numbers and length Y + 1 (voxels.shape[1] + 1).\n\n grid_z : numpy.ndarray(dtype = numpy.float64, ndim = 1)\n The grid_z parameter is a one-dimensional grid that delimits the\n voxels in the z-dimension. It must be *sorted* in ascending order\n with *equally-spaced* numbers and length Z + 1 (voxels.shape[2] + 1).\n\n Examples\n --------\n The input parameters can be easily generated using numpy before calling the\n function. For example, if a volume of 300 x 400 x 500 is split into\n 30 x 40 x 50 voxels, a possible code would be:\n\n >>> import numpy as np\n >>> from pept.utilities.traverse import traverse3d\n >>>\n >>> volume = [300, 400, 500]\n >>> number_of_voxels = [30, 40, 50]\n >>> voxels = np.zeros(number_of_voxels)\n\n The grid has one extra element than the number of voxels. For example, 5\n voxels 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, volume[0], number_of_voxels[0] + 1)\n >>> grid_y = np.linspace(0, volume[1], number_of_voxels[1] + 1)\n >>> grid_z = np.linspace(0, volume[2], number_of_voxels[2] + 1)\n >>>\n >>> random_lines = np.random.random((100, 7)) * 300\n\n Calling `traverse3d` will modify `voxels` in-place.\n\n >>> traverse3d(voxels, random_lines, grid_x, grid_y, grid_z)\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 trac""ing.\n InEurographics 1987 Aug 24 (Vol. 87, No. 3, pp. 3-10)..\n\n "; static PyObject *__pyx_pw_4pept_9utilities_8traverse_10traverse3d_1traverse3d(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { __Pyx_memviewslice __pyx_v_voxels = { 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 } }; __Pyx_memviewslice __pyx_v_grid_z = { 0, 0, { 0 }, { 0 }, { 0 } }; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("traverse3d (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_voxels,&__pyx_n_s_lines,&__pyx_n_s_grid_x,&__pyx_n_s_grid_y,&__pyx_n_s_grid_z,0}; PyObject* values[5] = {0,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 5: values[4] = PyTuple_GET_ITEM(__pyx_args, 4); CYTHON_FALLTHROUGH; 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_voxels)) != 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("traverse3d", 1, 5, 5, 1); __PYX_ERR(0, 110, __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("traverse3d", 1, 5, 5, 2); __PYX_ERR(0, 110, __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("traverse3d", 1, 5, 5, 3); __PYX_ERR(0, 110, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 4: if (likely((values[4] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_grid_z)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("traverse3d", 1, 5, 5, 4); __PYX_ERR(0, 110, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "traverse3d") < 0)) __PYX_ERR(0, 110, __pyx_L3_error) } } else if (PyTuple_GET_SIZE(__pyx_args) != 5) { 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); values[4] = PyTuple_GET_ITEM(__pyx_args, 4); } __pyx_v_voxels = __Pyx_PyObject_to_MemoryviewSlice_dsdsds_double(values[0], PyBUF_WRITABLE); if (unlikely(!__pyx_v_voxels.memview)) __PYX_ERR(0, 111, __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, 112, __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, 113, __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, 114, __pyx_L3_error) __pyx_v_grid_z = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[4], PyBUF_WRITABLE); if (unlikely(!__pyx_v_grid_z.memview)) __PYX_ERR(0, 115, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("traverse3d", 1, 5, 5, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 110, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("pept.utilities.traverse.traverse3d.traverse3d", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_4pept_9utilities_8traverse_10traverse3d_traverse3d(__pyx_self, __pyx_v_voxels, __pyx_v_lines, __pyx_v_grid_x, __pyx_v_grid_y, __pyx_v_grid_z); 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_10traverse3d_traverse3d(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_voxels, __Pyx_memviewslice __pyx_v_lines, __Pyx_memviewslice __pyx_v_grid_x, __Pyx_memviewslice __pyx_v_grid_y, __Pyx_memviewslice __pyx_v_grid_z) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("traverse3d", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = __Pyx_void_to_None(__pyx_f_4pept_9utilities_8traverse_10traverse3d_traverse3d(__pyx_v_voxels, __pyx_v_lines, __pyx_v_grid_x, __pyx_v_grid_y, __pyx_v_grid_z, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 110, __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.traverse3d.traverse3d", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __PYX_XDEC_MEMVIEW(&__pyx_v_voxels, 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_XDEC_MEMVIEW(&__pyx_v_grid_z, 1); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
111: double[:, :, :] voxels, # Initialised!
112: double[:, :] lines, # Has exactly 7 columns!
113: double[:] grid_x, # Has voxels.shape[0] + 1 elements!
114: double[:] grid_y, # Has voxels.shape[1] + 1 elements!
115: double[:] grid_z # Has voxels.shape[2] + 1 elements!
116: ) nogil:
117: ''' Fast voxel traversal for 3D lines (or LoRs).
118:
119: ::
120:
121: Function Signature:
122: traverse3d(
123: long[:, :, :] voxels, # Initialised!
124: double[:, :] lines, # Has exactly 7 columns!
125: double[:] grid_x, # Has voxels.shape[0] + 1 elements!
126: double[:] grid_y, # Has voxels.shape[1] + 1 elements!
127: double[:] grid_z # Has voxels.shape[2] + 1 elements!
128: )
129:
130: This function computes the number of lines that passes through each voxel,
131: saving the result in `voxels`. It does so in an efficient manner, in which
132: for every line, only the voxels that is passes through are traversed.
133:
134: As it is highly optimised, this function does not perform any checks on the
135: validity of the input data. Please check the parameters before calling
136: `traverse3d`, as it WILL segfault on wrong input data. Details are given
137: below, along with an example call.
138:
139: Parameters
140: ----------
141: voxels : numpy.ndarray(dtype = numpy.float64, ndim = 3)
142: The `voxels` parameter is a numpy.ndarray of shape (X, Y, Z) that
143: has been initialised to zeros before the function call. The values
144: will be modified in-place in the function to reflect the number of
145: lines that pass through each voxel.
146:
147: lines : numpy.ndarray(dtype = numpy.float64, ndim = 2)
148: The `lines` parameter is a numpy.ndarray of shape(N, 7), where each
149: row is formatted as [time, x1, y1, z1, x2, y2, z2]. Only indices 1:7
150: will be used as the two points P1 = [x1, y1, z2] and P2 = [x2, y2, z2]
151: defining the line (or LoR).
152:
153: grid_x : numpy.ndarray(dtype = numpy.float64, ndim = 1)
154: The grid_x parameter is a one-dimensional grid that delimits the
155: voxels in the x-dimension. It must be *sorted* in ascending order
156: with *equally-spaced* numbers and length X + 1 (voxels.shape[0] + 1).
157:
158: grid_y : numpy.ndarray(dtype = numpy.float64, ndim = 1)
159: The grid_y parameter is a one-dimensional grid that delimits the
160: voxels in the y-dimension. It must be *sorted* in ascending order
161: with *equally-spaced* numbers and length Y + 1 (voxels.shape[1] + 1).
162:
163: grid_z : numpy.ndarray(dtype = numpy.float64, ndim = 1)
164: The grid_z parameter is a one-dimensional grid that delimits the
165: voxels in the z-dimension. It must be *sorted* in ascending order
166: with *equally-spaced* numbers and length Z + 1 (voxels.shape[2] + 1).
167:
168: Examples
169: --------
170: The input parameters can be easily generated using numpy before calling the
171: function. For example, if a volume of 300 x 400 x 500 is split into
172: 30 x 40 x 50 voxels, a possible code would be:
173:
174: >>> import numpy as np
175: >>> from pept.utilities.traverse import traverse3d
176: >>>
177: >>> volume = [300, 400, 500]
178: >>> number_of_voxels = [30, 40, 50]
179: >>> voxels = np.zeros(number_of_voxels)
180:
181: The grid has one extra element than the number of voxels. For example, 5
182: voxels between 0 and 5 would be delimited by the grid [0, 1, 2, 3, 4, 5]
183: which has 6 elements (see off-by-one errors - story of my life).
184:
185: >>> grid_x = np.linspace(0, volume[0], number_of_voxels[0] + 1)
186: >>> grid_y = np.linspace(0, volume[1], number_of_voxels[1] + 1)
187: >>> grid_z = np.linspace(0, volume[2], number_of_voxels[2] + 1)
188: >>>
189: >>> random_lines = np.random.random((100, 7)) * 300
190:
191: Calling `traverse3d` will modify `voxels` in-place.
192:
193: >>> traverse3d(voxels, random_lines, grid_x, grid_y, grid_z)
194:
195: Notes
196: -----
197: This function is an adaptation of a widely-used algorithm [1]_, optimised
198: for PEPT LoRs traversal.
199:
200: .. [1] Amanatides J, Woo A. A fast voxel traversal algorithm for ray tracing.
201: InEurographics 1987 Aug 24 (Vol. 87, No. 3, pp. 3-10)..
202:
203: '''
204:
+205: cdef Py_ssize_t n_lines = lines.shape[0]
__pyx_v_n_lines = (__pyx_v_lines.shape[0]);
+206: cdef Py_ssize_t nx = voxels.shape[0]
__pyx_v_nx = (__pyx_v_voxels.shape[0]);
+207: cdef Py_ssize_t ny = voxels.shape[1]
__pyx_v_ny = (__pyx_v_voxels.shape[1]);
+208: cdef Py_ssize_t nz = voxels.shape[2]
__pyx_v_nz = (__pyx_v_voxels.shape[2]);
209:
210: # Grid size
+211: 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]) ))));
+212: 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]) ))));
+213: cdef double gsize_z = grid_z[1] - grid_z[0]
__pyx_t_1 = 1; __pyx_t_2 = 0; __pyx_v_gsize_z = ((*((double *) ( /* dim=0 */ (__pyx_v_grid_z.data + __pyx_t_1 * __pyx_v_grid_z.strides[0]) ))) - (*((double *) ( /* dim=0 */ (__pyx_v_grid_z.data + __pyx_t_2 * __pyx_v_grid_z.strides[0]) ))));
214:
215: # Delimiting grid
+216: cdef double xmin = grid_x[0]
__pyx_t_2 = 0; __pyx_v_xmin = (*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_2 * __pyx_v_grid_x.strides[0]) )));
+217: cdef double xmax = grid_x[nx]
__pyx_t_2 = __pyx_v_nx; __pyx_v_xmax = (*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_2 * __pyx_v_grid_x.strides[0]) )));
218:
+219: cdef double ymin = grid_y[0]
__pyx_t_2 = 0; __pyx_v_ymin = (*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_2 * __pyx_v_grid_y.strides[0]) )));
+220: cdef double ymax = grid_y[ny]
__pyx_t_2 = __pyx_v_ny; __pyx_v_ymax = (*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_2 * __pyx_v_grid_y.strides[0]) )));
221:
+222: cdef double zmin = grid_z[0]
__pyx_t_2 = 0; __pyx_v_zmin = (*((double *) ( /* dim=0 */ (__pyx_v_grid_z.data + __pyx_t_2 * __pyx_v_grid_z.strides[0]) )));
+223: cdef double zmax = grid_z[nz]
__pyx_t_2 = __pyx_v_nz; __pyx_v_zmax = (*((double *) ( /* dim=0 */ (__pyx_v_grid_z.data + __pyx_t_2 * __pyx_v_grid_z.strides[0]) )));
224:
225: # The current voxel indices [ix, iy, iz] that the line passes
226: # through.
227: cdef Py_ssize_t ix, iy, iz
228:
229: # Define a line as L(t) = U + t V
230: # If an LoR is defined as two points P1 and P2, then
231: # U = P1 and V = P2 - P1
232: cdef double[3] p1, p2, u, v
233: cdef double t
234:
235: # The step [step_x, step_y, step_z] defines the sense of the LoR.
236: # If V[0] is positive, then step_x = 1
237: # If V[0] is negative, then step_x = -1
238: cdef Py_ssize_t step_x, step_y, step_z
239:
240: # The value of t at which the line passes through to the next
241: # voxel, for each dimension.
242: cdef double tnext_x, tnext_y, tnext_z
243:
244: # deltat indicates how far along the ray we must move (in units of
245: # t) for each component to be equal to the size of the voxel in
246: # that dimension.
247: cdef double deltat_x, deltat_y, deltat_z
248:
249: cdef Py_ssize_t i, j
250:
+251: 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;
+252: for j in range(3):
for (__pyx_t_6 = 0; __pyx_t_6 < 3; __pyx_t_6+=1) { __pyx_v_j = __pyx_t_6;
+253: p1[j] = lines[i, 1 + j]
__pyx_t_2 = __pyx_v_i; __pyx_t_1 = (1 + __pyx_v_j); (__pyx_v_p1[__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]) )));
+254: p2[j] = lines[i, 4 + j]
__pyx_t_1 = __pyx_v_i; __pyx_t_2 = (4 + __pyx_v_j); (__pyx_v_p2[__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]) )));
+255: u[j] = p1[j]
(__pyx_v_u[__pyx_v_j]) = (__pyx_v_p1[__pyx_v_j]);
+256: v[j] = p2[j] - p1[j]
(__pyx_v_v[__pyx_v_j]) = ((__pyx_v_p2[__pyx_v_j]) - (__pyx_v_p1[__pyx_v_j])); }
257:
258: ##############################################################
259: # Initialisation stage
260:
+261: 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;
+262: 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;
+263: step_z = 1 if v[2] >= 0 else -1
if ((((__pyx_v_v[2]) >= 0.0) != 0)) { __pyx_t_6 = 1; } else { __pyx_t_6 = -1L; } __pyx_v_step_z = __pyx_t_6;
264:
265: # If the first point is outside the box, find the first voxel it hits
+266: 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) { /* … */ }
+267: u[1] < ymin or u[1] > ymax or
__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); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L8_bool_binop_done; }
+268: u[2] < zmin or u[2] > zmax):
__pyx_t_8 = (((__pyx_v_u[2]) < __pyx_v_zmin) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L8_bool_binop_done; } __pyx_t_8 = (((__pyx_v_u[2]) > __pyx_v_zmax) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L8_bool_binop_done:;
269:
+270: t = intersect(u, v, xmin, xmax, ymin, ymax, zmin, zmax)
__pyx_v_t = __pyx_f_4pept_9utilities_8traverse_10traverse3d_intersect(__pyx_v_u, __pyx_v_v, __pyx_v_xmin, __pyx_v_xmax, __pyx_v_ymin, __pyx_v_ymax, __pyx_v_zmin, __pyx_v_zmax);
271:
272: # No intersection
+273: if t == 0.0:
__pyx_t_7 = ((__pyx_v_t == 0.0) != 0); if (__pyx_t_7) { /* … */ }
+274: continue
goto __pyx_L3_continue;
275:
276: # Overwrite U to correspond to the first intersection point
+277: for j in range(3):
for (__pyx_t_6 = 0; __pyx_t_6 < 3; __pyx_t_6+=1) { __pyx_v_j = __pyx_t_6;
+278: 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]))); }
279:
280: # Corner case: every voxel is defined as lower boundary (inclusive) and
281: # upper boundary (exclusive). Therefore, at the upper end of the voxel
282: # grid an undefined case occurs. If a point lies right at the upper
283: # boundary of the voxel space, "move it" a bit lower on the line
+284: if u[0] == xmax or u[1] == ymax or u[2] == zmax:
__pyx_t_8 = (((__pyx_v_u[0]) == __pyx_v_xmax) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L18_bool_binop_done; } __pyx_t_8 = (((__pyx_v_u[1]) == __pyx_v_ymax) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L18_bool_binop_done; } __pyx_t_8 = (((__pyx_v_u[2]) == __pyx_v_zmax) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L18_bool_binop_done:; if (__pyx_t_7) { /* … */ }
+285: for j in range(3):
for (__pyx_t_6 = 0; __pyx_t_6 < 3; __pyx_t_6+=1) { __pyx_v_j = __pyx_t_6;
+286: 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]))); }
287:
288: # If, for dimension x, there are 5 voxels between coordinates 0
289: # and 5, then the delimiting grid is [0, 1, 2, 3, 4, 5].
290: # If the line starts at 1.5, then it is part of the voxel at
291: # index 1.
+292: ix = <Py_ssize_t>((u[0] - xmin) / gsize_x)
__pyx_v_ix = ((Py_ssize_t)(((__pyx_v_u[0]) - __pyx_v_xmin) / __pyx_v_gsize_x));
+293: iy = <Py_ssize_t>((u[1] - ymin) / gsize_y)
__pyx_v_iy = ((Py_ssize_t)(((__pyx_v_u[1]) - __pyx_v_ymin) / __pyx_v_gsize_y));
+294: iz = <Py_ssize_t>((u[2] - zmin) / gsize_z)
__pyx_v_iz = ((Py_ssize_t)(((__pyx_v_u[2]) - __pyx_v_zmin) / __pyx_v_gsize_z));
295:
296: # Check the indices are inside the voxel grid - just to be sure
+297: if (ix < 0 or ix >= nx or iy < 0 or iy >= ny or iz < 0 or iz >= nz):
__pyx_t_8 = ((__pyx_v_ix < 0) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L24_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_L24_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iy < 0) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L24_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_L24_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iz < 0) != 0); if (!__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L24_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iz >= __pyx_v_nz) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L24_bool_binop_done:; if (__pyx_t_7) { /* … */ }
+298: continue
goto __pyx_L3_continue;
299:
300: # If the line is going "up", the next voxel is the next one
301: # If the line is going "down", the next voxel is the current one
+302: if v[0] > 0:
__pyx_t_7 = (((__pyx_v_v[0]) > 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L30; }
+303: tnext_x = (grid_x[ix + 1] - u[0]) / v[0]
__pyx_t_2 = (__pyx_v_ix + 1); __pyx_v_tnext_x = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_2 * __pyx_v_grid_x.strides[0]) ))) - (__pyx_v_u[0])) / (__pyx_v_v[0]));
+304: elif v[0] < 0:
__pyx_t_7 = (((__pyx_v_v[0]) < 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L30; }
+305: tnext_x = (grid_x[ix] - u[0]) / v[0]
__pyx_t_2 = __pyx_v_ix; __pyx_v_tnext_x = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_x.data + __pyx_t_2 * __pyx_v_grid_x.strides[0]) ))) - (__pyx_v_u[0])) / (__pyx_v_v[0]));
306: else:
+307: tnext_x = DBL_MAX
/*else*/ { __pyx_v_tnext_x = DBL_MAX; } __pyx_L30:;
308:
+309: if v[1] > 0:
__pyx_t_7 = (((__pyx_v_v[1]) > 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L31; }
+310: tnext_y = (grid_y[iy + 1] - u[1]) / v[1]
__pyx_t_2 = (__pyx_v_iy + 1); __pyx_v_tnext_y = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_2 * __pyx_v_grid_y.strides[0]) ))) - (__pyx_v_u[1])) / (__pyx_v_v[1]));
+311: elif v[1] < 0:
__pyx_t_7 = (((__pyx_v_v[1]) < 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L31; }
+312: tnext_y = (grid_y[iy] - u[1]) / v[1]
__pyx_t_2 = __pyx_v_iy; __pyx_v_tnext_y = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_y.data + __pyx_t_2 * __pyx_v_grid_y.strides[0]) ))) - (__pyx_v_u[1])) / (__pyx_v_v[1]));
313: else:
+314: tnext_y = DBL_MAX
/*else*/ { __pyx_v_tnext_y = DBL_MAX; } __pyx_L31:;
315:
+316: if v[2] > 0:
__pyx_t_7 = (((__pyx_v_v[2]) > 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L32; }
+317: tnext_z = (grid_z[iz + 1] - u[2]) / v[2]
__pyx_t_2 = (__pyx_v_iz + 1); __pyx_v_tnext_z = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_z.data + __pyx_t_2 * __pyx_v_grid_z.strides[0]) ))) - (__pyx_v_u[2])) / (__pyx_v_v[2]));
+318: elif v[2] < 0:
__pyx_t_7 = (((__pyx_v_v[2]) < 0.0) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L32; }
+319: tnext_z = (grid_z[iz] - u[2]) / v[2]
__pyx_t_2 = __pyx_v_iz; __pyx_v_tnext_z = (((*((double *) ( /* dim=0 */ (__pyx_v_grid_z.data + __pyx_t_2 * __pyx_v_grid_z.strides[0]) ))) - (__pyx_v_u[2])) / (__pyx_v_v[2]));
320: else:
+321: tnext_z = DBL_MAX
/*else*/ { __pyx_v_tnext_z = DBL_MAX; } __pyx_L32:;
322:
+323: deltat_x = fabs(gsize_x / v[0]) if v[0] else 0
if (((__pyx_v_v[0]) != 0)) { __pyx_t_9 = __pyx_f_4pept_9utilities_8traverse_10traverse3d_fabs((__pyx_v_gsize_x / (__pyx_v_v[0]))); } else { __pyx_t_9 = 0.0; } __pyx_v_deltat_x = __pyx_t_9;
+324: deltat_y = fabs(gsize_y / v[1]) if v[1] else 0
if (((__pyx_v_v[1]) != 0)) { __pyx_t_9 = __pyx_f_4pept_9utilities_8traverse_10traverse3d_fabs((__pyx_v_gsize_y / (__pyx_v_v[1]))); } else { __pyx_t_9 = 0.0; } __pyx_v_deltat_y = __pyx_t_9;
+325: deltat_z = fabs(gsize_z / v[2]) if v[2] else 0
if (((__pyx_v_v[2]) != 0)) { __pyx_t_9 = __pyx_f_4pept_9utilities_8traverse_10traverse3d_fabs((__pyx_v_gsize_z / (__pyx_v_v[2]))); } else { __pyx_t_9 = 0.0; } __pyx_v_deltat_z = __pyx_t_9;
326:
327: ###############################################################
328: # Incremental traversal stage
329:
330: # Loop until we reach the last voxel in space
+331: while (ix < nx and iy < ny and iz < nz) and (ix >= 0 and iy >= 0 and iz >= 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_L35_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_L35_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iz < __pyx_v_nz) != 0); if (__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L35_bool_binop_done; } __pyx_t_8 = ((__pyx_v_ix >= 0) != 0); if (__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L35_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iy >= 0) != 0); if (__pyx_t_8) { } else { __pyx_t_7 = __pyx_t_8; goto __pyx_L35_bool_binop_done; } __pyx_t_8 = ((__pyx_v_iz >= 0) != 0); __pyx_t_7 = __pyx_t_8; __pyx_L35_bool_binop_done:; if (!__pyx_t_7) break;
332:
+333: voxels[ix, iy, iz] += 1.
__pyx_t_2 = __pyx_v_ix; __pyx_t_1 = __pyx_v_iy; __pyx_t_10 = __pyx_v_iz; *((double *) ( /* dim=2 */ (( /* dim=1 */ (( /* dim=0 */ (__pyx_v_voxels.data + __pyx_t_2 * __pyx_v_voxels.strides[0]) ) + __pyx_t_1 * __pyx_v_voxels.strides[1]) ) + __pyx_t_10 * __pyx_v_voxels.strides[2]) )) += 1.;
334:
335: # Select the minimum t that makes the line pass
336: # through to the next voxel
+337: if tnext_x < tnext_y:
__pyx_t_7 = ((__pyx_v_tnext_x < __pyx_v_tnext_y) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L41; }
+338: if tnext_x < tnext_z:
__pyx_t_7 = ((__pyx_v_tnext_x < __pyx_v_tnext_z) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L42; }
339: # If the next voxel falls beyond the end of the line (that is at
340: # t = 1), stop the traversal stage
+341: if tnext_x > 1.:
__pyx_t_7 = ((__pyx_v_tnext_x > 1.) != 0); if (__pyx_t_7) { /* … */ }
+342: break
goto __pyx_L34_break;
343:
+344: ix = ix + step_x
__pyx_v_ix = (__pyx_v_ix + __pyx_v_step_x);
+345: tnext_x = tnext_x + deltat_x
__pyx_v_tnext_x = (__pyx_v_tnext_x + __pyx_v_deltat_x);
346: else:
+347: if tnext_z > 1.:
/*else*/ { __pyx_t_7 = ((__pyx_v_tnext_z > 1.) != 0); if (__pyx_t_7) { /* … */ }
+348: break
goto __pyx_L34_break;
349:
+350: iz = iz + step_z
__pyx_v_iz = (__pyx_v_iz + __pyx_v_step_z);
+351: tnext_z = tnext_z + deltat_z
__pyx_v_tnext_z = (__pyx_v_tnext_z + __pyx_v_deltat_z); } __pyx_L42:;
352: else:
+353: if tnext_y < tnext_z:
/*else*/ { __pyx_t_7 = ((__pyx_v_tnext_y < __pyx_v_tnext_z) != 0); if (__pyx_t_7) { /* … */ goto __pyx_L45; }
+354: if tnext_y > 1.:
__pyx_t_7 = ((__pyx_v_tnext_y > 1.) != 0); if (__pyx_t_7) { /* … */ }
+355: break
goto __pyx_L34_break;
356:
+357: iy = iy + step_y
__pyx_v_iy = (__pyx_v_iy + __pyx_v_step_y);
+358: tnext_y = tnext_y + deltat_y
__pyx_v_tnext_y = (__pyx_v_tnext_y + __pyx_v_deltat_y);
359: else:
+360: if tnext_z > 1.:
/*else*/ { __pyx_t_7 = ((__pyx_v_tnext_z > 1.) != 0); if (__pyx_t_7) { /* … */ }
+361: break
goto __pyx_L34_break;
362:
+363: iz = iz + step_z
__pyx_v_iz = (__pyx_v_iz + __pyx_v_step_z);
+364: tnext_z = tnext_z + deltat_z
__pyx_v_tnext_z = (__pyx_v_tnext_z + __pyx_v_deltat_z); } __pyx_L45:; } __pyx_L41:; } __pyx_L34_break:; __pyx_L3_continue:; }
365:
366: