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: