Cython: Optimising for speed

This notebook aims to take the reader through a realworld example of increasing the speed on an algorithm. The given example is that of computing the normals for a given triangle mesh (points and a trilist). Computing the per-vertex normal for a mesh is an intensive operation that yields poor performance in pure Python. This is due to the need to loop over every triangle and and sum the per-triangle normal that every vertex is a member of. This notebook is not designed to describe Cython syntax or basics. It is assumed that the reader has some understanding and preferably experience with writing Cythonised functions.

Algorithm Pseudocode

foreach face in faces:
    face_normal = crossproduct(vertices[face[1]] - vertices[face[0]], 
                               vertices[face[2]] - vertices[face[0]])
    foreach v in face:
        normalise(face_normal)
        vertices[v].in_faces.append(face_normal)

foreach vertex in vertices:
    normal = (0,0,0)
    for face in vertex.in_faces:
        normal += face_normal
    normalise(normal)

crossproduct(v0, v1):
        v0.y * v1.z - v0.z * v1.y,
        v0.z * v1.x - v0.x * v1.z,
        v0.x * v1.y - v0.y * v1.x,

Begin

We begin by loading an appropriate mesh.

In [15]:
from pybug.io import auto_import
import numpy as np

mesh = auto_import('/vol/atlas/databases/frgc/spring2003/04201d302.abs')[0]
tris = mesh.trilist
points = mesh.points

print mesh
Found 1 files. (1/1) are importable
Creating importer for <pybug.io.mesh.base.ABSImporter object at 0xbff26d0> (1 of 1)
Found 1 files. (0/1) are importable

<class 'pybug.shape.mesh.textured.TexturedTriMesh'>: n_points: 61599, n_dims: 3
n_tris: 123160

Pure Python Implementation

This implementation uses numpy and broadcasting to achieve it's goals.

In [16]:
def normalise(vec):
    # Avoid divisions by almost 0 numbers
    # np.spacing(1) is equivalent to Matlab's eps
    d = np.sqrt(np.sum(vec ** 2, axis=1))
    d[d < np.spacing(1)] = 1.0
    return vec / d[..., None]

def py_compute_normal(vertex, face):
    nface = face.shape[0]
    nvert = vertex.shape[0]
    
    # Calculate the cross product (per-face normal)
    normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
                       vertex[face[:, 2], :] - vertex[face[:, 0], :])
    normalf = normalise(normalf)
    
    # Calculate per-vertex normal
    normal = np.zeros([nvert, 3])
    for i in xrange(nface):
        f = face[i, :]
        for j in xrange(3):
            normal[f[j], :] += normalf[i, :]
            
    # Normalize
    normal = normalise(normal)
    
    # Enforce that the normal are outward
    v = vertex - np.mean(vertex)[..., None]
    s = np.sum(v * normal, axis=1)
    if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
        # flip
        normal = -normal
        normalf = -normalf
    
    return normal, normalf

If we then time this function, we can see that it takes about 3 seconds (on my Intel(R) Xeon(R) CPU E5-1650 @ 3.20GHz with 32GB of RAM) for 123160 triangles and 61599 points.

In [17]:
%timeit py_compute_normal(points, tris)
1 loops, best of 3: 3.15 s per loop

Naive Cython

This is obviously far too slow to be of any use. Therefore, we naively port this method to Cython. Cython is useful for code where tight looping is unavoidable, as is the case in computing the per-vertex normal. This is because it pre-compiles as much of the code as possible down to C, which is very efficient at tight looping. To compile Cython code, we have to load the Cython magic extension

In [18]:
%load_ext cythonmagic
The cythonmagic extension is already loaded. To reload it, use:
  %reload_ext cythonmagic

The Cython extension gives us the %%cython cell magic where we can put raw Cython code which will compiled on execution. TO get started with Cython, we note that the majority of Cython's speedup comes from the fact that we statically type variables. Therefore, we always have to import some C code via the cimport statement. For example, to use numpy, we could use:

In [19]:
%%cython
import numpy as np

cimport cython
cimport numpy as np

Note that we have to Python import Numpy AND cimport it. Therefore, a simple Cython function using numpy would look like:

In [20]:
%%cython
import numpy as np

cimport cython
cimport numpy as np

def my_pow(double x):
    return np.power(x, 2)

print my_pow(2.0)

It's important to note that there are 3 kinds of functions definitions in Cython:

  • def
    • This is a Python function. It is called via Python and thus has all the overhead of being called by Python. Any C-code will have to call out of Python
    • Parameters are Python objects which are then explicitly converted to static types if specified
    • Returns a Python object
  • cdef
    • This is a C signature and can ONLY run from a Cython context. It cannot be called by pure Python code.
    • Parameters are converted to static type by the caller
    • Return type can be statitically defined
  • cpdef
    • This is a mixed signature whereby Cython automatically builds a pure Python wrapper around a cdef function. So Python calls the wrapper and C calls the cdef function.
    • Parameters are converted to C type of Python wrapper
    • Return types are statically defined and marshalled by Python wrapper

So, to create a naive implementation of our Python function, in Cython, we define cpdef function as follows:

In [21]:
%%cython

import numpy as np
cimport numpy as np
cimport cython


cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):
    # Avoid divisions by almost 0 numbers
    cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
    d[d < np.spacing(1)] = 1.0
    return vec / d[..., None]
 

cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
    cdef int nface = face.shape[0]
    cdef int nvert = vertex.shape[0]
    
    # Calculate the cross product (per-face normal)
    cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
                                                             vertex[face[:, 2], :] - vertex[face[:, 0], :])
    normalf = cy_normalise_naive(normalf)
    
    # Calculate per-vertex normal
    cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
    cdef np.ndarray[int, ndim=1] f
    for i in xrange(nface):
        f = face[i, :]
        for j in xrange(3):
            normal[f[j], :] += normalf[i, :]
    
    # Normalize
    normal = cy_normalise_naive(normal)
    
    # Enforce that the normal are outward
    cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
    cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
    if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
        # flip
        normal = -normal
        normalf = -normalf
    
    return normal, normalf

If we then time this function, we can see that it takes about 1.8 seconds (on my Intel(R) Xeon(R) CPU E5-1650 @ 3.20GHz with 32GB of RAM) for 123160 triangles and 61599 points. This represents an approximately 1.6x speedup just by naively moving the code in to a Cython function. Other than the static typing, the code is almost identical.

Note: There are decorators such as @cython.boundscheck(False) and @cython.wraparound(False) that can provide speedups by telling Cython that you guarantee the kinds of accesses arrays will have inside the function. See here for more information.

In [22]:
%timeit cy_compute_normal_naive(points, tris)
1 loops, best of 3: 1.9 s per loop

Optimising Cython

However, we can do better than this! In order to give us a better indiciaton, Cython provides the ability to pass flags in for execution. These can be compile time flags, or special running flags. The flag we are interested in is -a. This provides an output that colour codes the typing that is going on within the Cython function. Yellow backgrounds indicate function calls back in to Python (which is slow), and white/clear backgrounds represent pure C calls. If we run this on our naive implementaton, we get the following:

In [23]:
%%cython -a

import numpy as np
cimport numpy as np
cimport cython


cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):
    # Avoid divisions by almost 0 numbers
    cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
    d[d < np.spacing(1)] = 1.0
    return vec / d[..., None]
 

cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
    cdef int nface = face.shape[0]
    cdef int nvert = vertex.shape[0]
    
    # unit normals to the faces
    cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
                                                             vertex[face[:, 2], :] - vertex[face[:, 0], :])
    normalf = cy_normalise_naive(normalf)
    
    # unit normal to the vertex
    cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
    cdef double[:] f
    for i in xrange(nface):
        f = face[i, :]
        for j in xrange(3):
            normal[f[j], :] += normalf[i, :]
    
    # normalize
    normal = cy_normalise_naive(normal)
    
    # enforce that the normal are outward
    cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
    cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
    if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
        # flip
        normal = -normal
        normalf = -normalf
    
    return normal, normalf
Out[23]:

Generated by Cython 0.19.1 on Fri Aug 16 16:37:23 2013

 1: 
 2: import numpy as np
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":2
 * 
 * import numpy as np             # <<<<<<<<<<<<<<
 * cimport numpy as np
 * cimport cython
 */
  __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__numpy), 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s__np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":14
 * 
 * 
 * cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_d, __pyx_n_s____test__, ((PyObject *)__pyx_t_1)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
 3: cimport numpy as np
 4: cimport cython
 5: 
 6: 
 7: cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):
/* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":7
 * 
 * 
 * cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):             # <<<<<<<<<<<<<<
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 */

static PyArrayObject *__pyx_f_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_cy_normalise_naive(PyArrayObject *__pyx_v_vec) {
  PyArrayObject *__pyx_v_d = 0;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_d;
  __Pyx_Buffer __pyx_pybuffer_d;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vec;
  __Pyx_Buffer __pyx_pybuffer_vec;
  PyArrayObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_normalise_naive", 0);
  __pyx_pybuffer_d.pybuffer.buf = NULL;
  __pyx_pybuffer_d.refcount = 0;
  __pyx_pybuffernd_d.data = NULL;
  __pyx_pybuffernd_d.rcbuffer = &__pyx_pybuffer_d;
  __pyx_pybuffer_vec.pybuffer.buf = NULL;
  __pyx_pybuffer_vec.refcount = 0;
  __pyx_pybuffernd_vec.data = NULL;
  __pyx_pybuffernd_vec.rcbuffer = &__pyx_pybuffer_vec;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vec.rcbuffer->pybuffer, (PyObject*)__pyx_v_vec, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vec.diminfo[0].strides = __pyx_pybuffernd_vec.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vec.diminfo[0].shape = __pyx_pybuffernd_vec.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vec.diminfo[1].strides = __pyx_pybuffernd_vec.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vec.diminfo[1].shape = __pyx_pybuffernd_vec.rcbuffer->pybuffer.shape[1];
 8:     # Avoid divisions by almost 0 numbers
 9:     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":9
 * cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))             # <<<<<<<<<<<<<<
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sqrt); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sum); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Power(((PyObject *)__pyx_v_vec), __pyx_int_2, Py_None); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_t_1, ((PyObject *)__pyx_n_s__axis), __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_5 = PyObject_Call(__pyx_t_3, ((PyObject *)__pyx_t_4), ((PyObject *)__pyx_t_1)); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __pyx_t_5 = 0;
  __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_6 = ((PyArrayObject *)__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_d.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_d = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_d.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_d.diminfo[0].strides = __pyx_pybuffernd_d.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_d.diminfo[0].shape = __pyx_pybuffernd_d.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_6 = 0;
  __pyx_v_d = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
 10:     d[d < np.spacing(1)] = 1.0
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":10
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0             # <<<<<<<<<<<<<<
 *     return vec / d[..., None]
 * 
 */
  __pyx_t_5 = PyFloat_FromDouble(1.0); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__spacing); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_k_tuple_1), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_RichCompare(((PyObject *)__pyx_v_d), __pyx_t_1, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  if (PyObject_SetItem(((PyObject *)__pyx_v_d), __pyx_t_2, __pyx_t_5) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":10
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0             # <<<<<<<<<<<<<<
 *     return vec / d[..., None]
 * 
 */
  __pyx_k_tuple_1 = PyTuple_Pack(1, __pyx_int_1); if (unlikely(!__pyx_k_tuple_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_1);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_1));
 11:     return vec / d[..., None]
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":11
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]             # <<<<<<<<<<<<<<
 * 
 * 
 */
  __Pyx_XDECREF(((PyObject *)__pyx_r));
  __pyx_t_5 = PyObject_GetItem(((PyObject *)__pyx_v_d), ((PyObject *)__pyx_k_tuple_2)); if (!__pyx_t_5) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_2 = __Pyx_PyNumber_Divide(((PyObject *)__pyx_v_vec), __pyx_t_5); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_r = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;
  goto __pyx_L0;

  __pyx_r = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_XDECREF(__pyx_t_5);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_d.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vec.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_8a17498f1f23780b33414dc2e93cc08d.cy_normalise_naive", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_d.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vec.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_d);
  __Pyx_XGIVEREF((PyObject *)__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":11
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]             # <<<<<<<<<<<<<<
 * 
 * 
 */
  __pyx_k_tuple_2 = PyTuple_Pack(2, Py_Ellipsis, Py_None); if (unlikely(!__pyx_k_tuple_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_2);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_2));
 12: 
 13: 
 14: cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
/* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":14
 * 
 * 
 * cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */

static PyObject *__pyx_pw_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_1cy_compute_normal_naive(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_f_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_cy_compute_normal_naive(PyArrayObject *__pyx_v_vertex, PyArrayObject *__pyx_v_face, CYTHON_UNUSED int __pyx_skip_dispatch) {
  int __pyx_v_nface;
  int __pyx_v_nvert;
  PyArrayObject *__pyx_v_normalf = 0;
  PyArrayObject *__pyx_v_normal = 0;
  __Pyx_memviewslice __pyx_v_f = { 0, 0, { 0 }, { 0 }, { 0 } };
  int __pyx_v_i;
  long __pyx_v_j;
  PyArrayObject *__pyx_v_v = 0;
  PyArrayObject *__pyx_v_s = 0;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_face;
  __Pyx_Buffer __pyx_pybuffer_face;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_normal;
  __Pyx_Buffer __pyx_pybuffer_normal;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_normalf;
  __Pyx_Buffer __pyx_pybuffer_normalf;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_s;
  __Pyx_Buffer __pyx_pybuffer_s;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_v;
  __Pyx_Buffer __pyx_pybuffer_v;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vertex;
  __Pyx_Buffer __pyx_pybuffer_vertex;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal_naive", 0);
  __pyx_pybuffer_normalf.pybuffer.buf = NULL;
  __pyx_pybuffer_normalf.refcount = 0;
  __pyx_pybuffernd_normalf.data = NULL;
  __pyx_pybuffernd_normalf.rcbuffer = &__pyx_pybuffer_normalf;
  __pyx_pybuffer_normal.pybuffer.buf = NULL;
  __pyx_pybuffer_normal.refcount = 0;
  __pyx_pybuffernd_normal.data = NULL;
  __pyx_pybuffernd_normal.rcbuffer = &__pyx_pybuffer_normal;
  __pyx_pybuffer_v.pybuffer.buf = NULL;
  __pyx_pybuffer_v.refcount = 0;
  __pyx_pybuffernd_v.data = NULL;
  __pyx_pybuffernd_v.rcbuffer = &__pyx_pybuffer_v;
  __pyx_pybuffer_s.pybuffer.buf = NULL;
  __pyx_pybuffer_s.refcount = 0;
  __pyx_pybuffernd_s.data = NULL;
  __pyx_pybuffernd_s.rcbuffer = &__pyx_pybuffer_s;
  __pyx_pybuffer_vertex.pybuffer.buf = NULL;
  __pyx_pybuffer_vertex.refcount = 0;
  __pyx_pybuffernd_vertex.data = NULL;
  __pyx_pybuffernd_vertex.rcbuffer = &__pyx_pybuffer_vertex;
  __pyx_pybuffer_face.pybuffer.buf = NULL;
  __pyx_pybuffer_face.refcount = 0;
  __pyx_pybuffernd_face.data = NULL;
  __pyx_pybuffernd_face.rcbuffer = &__pyx_pybuffer_face;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer, (PyObject*)__pyx_v_vertex, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vertex.diminfo[0].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vertex.diminfo[0].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vertex.diminfo[1].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vertex.diminfo[1].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[1];
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_face.rcbuffer->pybuffer, (PyObject*)__pyx_v_face, &__Pyx_TypeInfo_int, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_face.diminfo[0].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_face.diminfo[0].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_face.diminfo[1].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_face.diminfo[1].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[1];

/* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":14
 * 
 * 
 * cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */

static PyObject *__pyx_pf_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_cy_compute_normal_naive(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_vertex, PyArrayObject *__pyx_v_face) {
  __Pyx_LocalBuf_ND __pyx_pybuffernd_face;
  __Pyx_Buffer __pyx_pybuffer_face;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vertex;
  __Pyx_Buffer __pyx_pybuffer_vertex;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal_naive", 0);
  __pyx_pybuffer_vertex.pybuffer.buf = NULL;
  __pyx_pybuffer_vertex.refcount = 0;
  __pyx_pybuffernd_vertex.data = NULL;
  __pyx_pybuffernd_vertex.rcbuffer = &__pyx_pybuffer_vertex;
  __pyx_pybuffer_face.pybuffer.buf = NULL;
  __pyx_pybuffer_face.refcount = 0;
  __pyx_pybuffernd_face.data = NULL;
  __pyx_pybuffernd_face.rcbuffer = &__pyx_pybuffer_face;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer, (PyObject*)__pyx_v_vertex, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vertex.diminfo[0].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vertex.diminfo[0].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vertex.diminfo[1].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vertex.diminfo[1].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[1];
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_face.rcbuffer->pybuffer, (PyObject*)__pyx_v_face, &__Pyx_TypeInfo_int, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_face.diminfo[0].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_face.diminfo[0].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_face.diminfo[1].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_face.diminfo[1].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[1];
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_1 = __pyx_f_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_cy_compute_normal_naive(__pyx_v_vertex, __pyx_v_face, 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_r = __pyx_t_1;
  __pyx_t_1 = 0;
  goto __pyx_L0;

  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_8a17498f1f23780b33414dc2e93cc08d.cy_compute_normal_naive", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static CYTHON_UNUSED int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags); /*proto*/
static CYTHON_UNUSED int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags) {
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("__getbuffer__ (wrapper)", 0);
  __pyx_r = __pyx_pf_5numpy_7ndarray___getbuffer__(((PyArrayObject *)__pyx_v_self), ((Py_buffer *)__pyx_v_info), ((int)__pyx_v_flags));
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 15:     cdef int nface = face.shape[0]
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":15
 * 
 * cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
 *     cdef int nface = face.shape[0]             # <<<<<<<<<<<<<<
 *     cdef int nvert = vertex.shape[0]
 * 
 */
  __pyx_v_nface = (__pyx_v_face->dimensions[0]);
 16:     cdef int nvert = vertex.shape[0]
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":16
 * cpdef cy_compute_normal_naive(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]             # <<<<<<<<<<<<<<
 * 
 *     # unit normals to the faces
 */
  __pyx_v_nvert = (__pyx_v_vertex->dimensions[0]);
 17: 
 18:     # unit normals to the faces
 19:     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":19
 * 
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],             # <<<<<<<<<<<<<<
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = cy_normalise_naive(normalf)
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__cross); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":19
 * 
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],             # <<<<<<<<<<<<<<
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = cy_normalise_naive(normalf)
 */
  __pyx_k_slice_3 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_3);
  __Pyx_GIVEREF(__pyx_k_slice_3);
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_4)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_k_tuple_4 = PyTuple_Pack(2, __pyx_k_slice_3, __pyx_int_1); if (unlikely(!__pyx_k_tuple_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_4);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_4));
  __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __Pyx_INCREF(__pyx_k_slice_5);
  PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_k_slice_5);
  __Pyx_GIVEREF(__pyx_k_slice_5);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_3)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0;
  __pyx_k_slice_5 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_5);
  __Pyx_GIVEREF(__pyx_k_slice_5);
  __pyx_k_slice_6 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_6)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_6);
  __Pyx_GIVEREF(__pyx_k_slice_6);
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_7)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_k_tuple_7 = PyTuple_Pack(2, __pyx_k_slice_6, __pyx_int_0); if (unlikely(!__pyx_k_tuple_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_7);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_7));
  __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __Pyx_INCREF(__pyx_k_slice_8);
  PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_k_slice_8);
  __Pyx_GIVEREF(__pyx_k_slice_8);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_4)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __pyx_t_4 = PyNumber_Subtract(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_k_slice_8 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_8);
  __Pyx_GIVEREF(__pyx_k_slice_8);
 20:                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":20
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])             # <<<<<<<<<<<<<<
 *     normalf = cy_normalise_naive(normalf)
 * 
 */
  __pyx_k_slice_9 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_9);
  __Pyx_GIVEREF(__pyx_k_slice_9);

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":20
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])             # <<<<<<<<<<<<<<
 *     normalf = cy_normalise_naive(normalf)
 * 
 */
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_10)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_k_tuple_10 = PyTuple_Pack(2, __pyx_k_slice_9, __pyx_int_2); if (unlikely(!__pyx_k_tuple_10)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_10);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_10));
  __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __Pyx_INCREF(__pyx_k_slice_11);
  PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_k_slice_11);
  __Pyx_GIVEREF(__pyx_k_slice_11);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_1)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __pyx_k_slice_11 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_11);
  __Pyx_GIVEREF(__pyx_k_slice_11);
  __pyx_k_slice_12 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_12);
  __Pyx_GIVEREF(__pyx_k_slice_12);
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_13)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_k_tuple_13 = PyTuple_Pack(2, __pyx_k_slice_12, __pyx_int_0); if (unlikely(!__pyx_k_tuple_13)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_13);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_13));
  __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __Pyx_INCREF(__pyx_k_slice_14);
  PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_k_slice_14);
  __Pyx_GIVEREF(__pyx_k_slice_14);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_5)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
  __pyx_t_5 = PyNumber_Subtract(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_4);
  __Pyx_GIVEREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __pyx_t_4 = 0;
  __pyx_t_5 = 0;
  __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_6 = ((PyArrayObject *)__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_normalf = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_6 = 0;
  __pyx_v_normalf = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
  __pyx_k_slice_14 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_14)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_14);
  __Pyx_GIVEREF(__pyx_k_slice_14);
 21:     normalf = cy_normalise_naive(normalf)
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":21
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = cy_normalise_naive(normalf)             # <<<<<<<<<<<<<<
 * 
 *     # unit normal to the vertex
 */
  __pyx_t_5 = ((PyObject *)__pyx_f_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_cy_normalise_naive(((PyArrayObject *)__pyx_v_normalf))); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
    __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)((PyArrayObject *)__pyx_t_5), &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
    if (unlikely(__pyx_t_7 < 0)) {
      PyErr_Fetch(&__pyx_t_8, &__pyx_t_9, &__pyx_t_10);
      if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_v_normalf, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
        Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_10);
        __Pyx_RaiseBufferFallbackError();
      } else {
        PyErr_Restore(__pyx_t_8, __pyx_t_9, __pyx_t_10);
      }
    }
    __pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
    if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __Pyx_DECREF(((PyObject *)__pyx_v_normalf));
  __pyx_v_normalf = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
 22: 
 23:     # unit normal to the vertex
 24:     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":24
 * 
 *     # unit normal to the vertex
 *     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])             # <<<<<<<<<<<<<<
 *     cdef double[:] f
 *     for i in xrange(nface):
 */
  __pyx_t_5 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s__zeros); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __pyx_t_5 = PyInt_FromLong(__pyx_v_nvert); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_2 = PyList_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  PyList_SET_ITEM(__pyx_t_2, 0, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __Pyx_INCREF(__pyx_int_3);
  PyList_SET_ITEM(__pyx_t_2, 1, __pyx_int_3);
  __Pyx_GIVEREF(__pyx_int_3);
  __pyx_t_5 = 0;
  __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  PyTuple_SET_ITEM(__pyx_t_5, 0, ((PyObject *)__pyx_t_2));
  __Pyx_GIVEREF(((PyObject *)__pyx_t_2));
  __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_5), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_11 = ((PyArrayObject *)__pyx_t_2);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_t_11, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_normal = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_11 = 0;
  __pyx_v_normal = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;
 25:     cdef double[:] f
 26:     for i in xrange(nface):
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":26
 *     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
 *     cdef double[:] f
 *     for i in xrange(nface):             # <<<<<<<<<<<<<<
 *         f = face[i, :]
 *         for j in xrange(3):
 */
  __pyx_t_7 = __pyx_v_nface;
  for (__pyx_t_12 = 0; __pyx_t_12 < __pyx_t_7; __pyx_t_12+=1) {
    __pyx_v_i = __pyx_t_12;
 27:         f = face[i, :]
    /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":27
 *     cdef double[:] f
 *     for i in xrange(nface):
 *         f = face[i, :]             # <<<<<<<<<<<<<<
 *         for j in xrange(3):
 *             normal[f[j], :] += normalf[i, :]
 */
    __pyx_t_2 = PyInt_FromLong(__pyx_v_i); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_5);
    PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_2);
    __Pyx_GIVEREF(__pyx_t_2);
    __Pyx_INCREF(__pyx_k_slice_15);
    PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_k_slice_15);
    __Pyx_GIVEREF(__pyx_k_slice_15);
    __pyx_t_2 = 0;
    __pyx_t_2 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_t_5)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
    __pyx_t_13 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(__pyx_t_2);
    if (unlikely(!__pyx_t_13.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
    __PYX_XDEC_MEMVIEW(&__pyx_v_f, 1);
    __pyx_v_f = __pyx_t_13;
    __pyx_t_13.memview = NULL;
    __pyx_t_13.data = NULL;

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":27
 *     cdef double[:] f
 *     for i in xrange(nface):
 *         f = face[i, :]             # <<<<<<<<<<<<<<
 *         for j in xrange(3):
 *             normal[f[j], :] += normalf[i, :]
 */
  __pyx_k_slice_15 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_15)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_15);
  __Pyx_GIVEREF(__pyx_k_slice_15);
 28:         for j in xrange(3):
    /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":28
 *     for i in xrange(nface):
 *         f = face[i, :]
 *         for j in xrange(3):             # <<<<<<<<<<<<<<
 *             normal[f[j], :] += normalf[i, :]
 * 
 */
    for (__pyx_t_14 = 0; __pyx_t_14 < 3; __pyx_t_14+=1) {
      __pyx_v_j = __pyx_t_14;
 29:             normal[f[j], :] += normalf[i, :]
      /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":29
 *         f = face[i, :]
 *         for j in xrange(3):
 *             normal[f[j], :] += normalf[i, :]             # <<<<<<<<<<<<<<
 * 
 *     # normalize
 */
      __pyx_t_15 = __pyx_v_j;
      __pyx_t_16 = -1;
      if (__pyx_t_15 < 0) {
        __pyx_t_15 += __pyx_v_f.shape[0];
        if (unlikely(__pyx_t_15 < 0)) __pyx_t_16 = 0;
      } else if (unlikely(__pyx_t_15 >= __pyx_v_f.shape[0])) __pyx_t_16 = 0;
      if (unlikely(__pyx_t_16 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_16);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      __pyx_t_2 = PyFloat_FromDouble((*((double *) ( /* dim=0 */ (__pyx_v_f.data + __pyx_t_15 * __pyx_v_f.strides[0]) )))); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_GOTREF(__pyx_t_2);
      __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_GOTREF(__pyx_t_5);
      PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_2);
      __Pyx_GIVEREF(__pyx_t_2);
      __Pyx_INCREF(__pyx_k_slice_16);
      PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_k_slice_16);
      __Pyx_GIVEREF(__pyx_k_slice_16);
      __pyx_t_2 = 0;
      __pyx_t_2 = PyObject_GetItem(((PyObject *)__pyx_v_normal), ((PyObject *)__pyx_t_5)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_GOTREF(__pyx_t_2);
      __pyx_t_1 = PyInt_FromLong(__pyx_v_i); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_GOTREF(__pyx_t_1);

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":29
 *         f = face[i, :]
 *         for j in xrange(3):
 *             normal[f[j], :] += normalf[i, :]             # <<<<<<<<<<<<<<
 * 
 *     # normalize
 */
  __pyx_k_slice_16 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_16)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_16);
  __Pyx_GIVEREF(__pyx_k_slice_16);
      __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_GOTREF(__pyx_t_4);
      PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_1);
      __Pyx_GIVEREF(__pyx_t_1);
      __Pyx_INCREF(__pyx_k_slice_17);
      PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_k_slice_17);
      __Pyx_GIVEREF(__pyx_k_slice_17);
      __pyx_t_1 = 0;
      __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_normalf), ((PyObject *)__pyx_t_4)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_GOTREF(__pyx_t_1);
      __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
      __pyx_t_4 = PyNumber_InPlaceAdd(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_GOTREF(__pyx_t_4);
      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
      if (PyObject_SetItem(((PyObject *)__pyx_v_normal), ((PyObject *)__pyx_t_5), __pyx_t_4) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
      __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
    }
  }
  __pyx_k_slice_17 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_17)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_17);
  __Pyx_GIVEREF(__pyx_k_slice_17);
 30: 
 31:     # normalize
 32:     normal = cy_normalise_naive(normal)
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":32
 * 
 *     # normalize
 *     normal = cy_normalise_naive(normal)             # <<<<<<<<<<<<<<
 * 
 *     # enforce that the normal are outward
 */
  __pyx_t_5 = ((PyObject *)__pyx_f_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_cy_normalise_naive(((PyArrayObject *)__pyx_v_normal))); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
    __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)((PyArrayObject *)__pyx_t_5), &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
    if (unlikely(__pyx_t_7 < 0)) {
      PyErr_Fetch(&__pyx_t_10, &__pyx_t_9, &__pyx_t_8);
      if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_v_normal, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
        Py_XDECREF(__pyx_t_10); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_8);
        __Pyx_RaiseBufferFallbackError();
      } else {
        PyErr_Restore(__pyx_t_10, __pyx_t_9, __pyx_t_8);
      }
    }
    __pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
    if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __Pyx_DECREF(((PyObject *)__pyx_v_normal));
  __pyx_v_normal = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
 33: 
 34:     # enforce that the normal are outward
 35:     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":35
 * 
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]             # <<<<<<<<<<<<<<
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 */
  __pyx_t_5 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s__mean); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_INCREF(((PyObject *)__pyx_v_vertex));
  PyTuple_SET_ITEM(__pyx_t_5, 0, ((PyObject *)__pyx_v_vertex));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_vertex));
  __pyx_t_1 = PyObject_Call(__pyx_t_4, ((PyObject *)__pyx_t_5), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
  __pyx_t_5 = PyObject_GetItem(__pyx_t_1, ((PyObject *)__pyx_k_tuple_18)); if (!__pyx_t_5) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Subtract(((PyObject *)__pyx_v_vertex), __pyx_t_5); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_1) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_1, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_17 = ((PyArrayObject *)__pyx_t_1);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_v.rcbuffer->pybuffer, (PyObject*)__pyx_t_17, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_v = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_v.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_v.diminfo[0].strides = __pyx_pybuffernd_v.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_v.diminfo[0].shape = __pyx_pybuffernd_v.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_v.diminfo[1].strides = __pyx_pybuffernd_v.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_v.diminfo[1].shape = __pyx_pybuffernd_v.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_17 = 0;
  __pyx_v_v = ((PyArrayObject *)__pyx_t_1);
  __pyx_t_1 = 0;

  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":35
 * 
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]             # <<<<<<<<<<<<<<
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 */
  __pyx_k_tuple_18 = PyTuple_Pack(2, Py_Ellipsis, Py_None); if (unlikely(!__pyx_k_tuple_18)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_18);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_18));
 36:     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":36
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)             # <<<<<<<<<<<<<<
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 *         # flip
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sum); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Multiply(((PyObject *)__pyx_v_v), ((PyObject *)__pyx_v_normal)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_t_1, ((PyObject *)__pyx_n_s__axis), __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_2 = PyObject_Call(__pyx_t_5, ((PyObject *)__pyx_t_4), ((PyObject *)__pyx_t_1)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_18 = ((PyArrayObject *)__pyx_t_2);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_s.rcbuffer->pybuffer, (PyObject*)__pyx_t_18, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_s = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_s.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_s.diminfo[0].strides = __pyx_pybuffernd_s.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_s.diminfo[0].shape = __pyx_pybuffernd_s.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_18 = 0;
  __pyx_v_s = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;
 37:     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":37
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):             # <<<<<<<<<<<<<<
 *         # flip
 *         normal = -normal
 */
  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s__sum); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s__greater); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_INCREF(((PyObject *)__pyx_v_s));
  PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject *)__pyx_v_s));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_s));
  __Pyx_INCREF(__pyx_int_0);
  PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_int_0);
  __Pyx_GIVEREF(__pyx_int_0);
  __pyx_t_5 = PyObject_Call(__pyx_t_4, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __pyx_t_5 = 0;
  __pyx_t_5 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s__sum); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s__less); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_INCREF(((PyObject *)__pyx_v_s));
  PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject *)__pyx_v_s));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_s));
  __Pyx_INCREF(__pyx_int_0);
  PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_int_0);
  __Pyx_GIVEREF(__pyx_int_0);
  __pyx_t_3 = PyObject_Call(__pyx_t_4, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_RichCompare(__pyx_t_5, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_t_19 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely(__pyx_t_19 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  if (__pyx_t_19) {
 38:         # flip
 39:         normal = -normal
    /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":39
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 *         # flip
 *         normal = -normal             # <<<<<<<<<<<<<<
 *         normalf = -normalf
 * 
 */
    __pyx_t_2 = PyNumber_Negative(((PyObject *)__pyx_v_normal)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __pyx_t_11 = ((PyArrayObject *)__pyx_t_2);
    {
      __Pyx_BufFmt_StackElem __pyx_stack[1];
      __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
      __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_t_11, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
      if (unlikely(__pyx_t_7 < 0)) {
        PyErr_Fetch(&__pyx_t_8, &__pyx_t_9, &__pyx_t_10);
        if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_v_normal, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
          Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_10);
          __Pyx_RaiseBufferFallbackError();
        } else {
          PyErr_Restore(__pyx_t_8, __pyx_t_9, __pyx_t_10);
        }
      }
      __pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
      if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_11 = 0;
    __Pyx_DECREF(((PyObject *)__pyx_v_normal));
    __pyx_v_normal = ((PyArrayObject *)__pyx_t_2);
    __pyx_t_2 = 0;
 40:         normalf = -normalf
    /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":40
 *         # flip
 *         normal = -normal
 *         normalf = -normalf             # <<<<<<<<<<<<<<
 * 
 *     return normal, normalf
 */
    __pyx_t_2 = PyNumber_Negative(((PyObject *)__pyx_v_normalf)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __pyx_t_6 = ((PyArrayObject *)__pyx_t_2);
    {
      __Pyx_BufFmt_StackElem __pyx_stack[1];
      __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
      __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
      if (unlikely(__pyx_t_7 < 0)) {
        PyErr_Fetch(&__pyx_t_10, &__pyx_t_9, &__pyx_t_8);
        if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_v_normalf, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
          Py_XDECREF(__pyx_t_10); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_8);
          __Pyx_RaiseBufferFallbackError();
        } else {
          PyErr_Restore(__pyx_t_10, __pyx_t_9, __pyx_t_8);
        }
      }
      __pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
      if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_6 = 0;
    __Pyx_DECREF(((PyObject *)__pyx_v_normalf));
    __pyx_v_normalf = ((PyArrayObject *)__pyx_t_2);
    __pyx_t_2 = 0;
    goto __pyx_L7;
  }
  __pyx_L7:;
 41: 
 42:     return normal, normalf
  /* "_cython_magic_8a17498f1f23780b33414dc2e93cc08d.pyx":42
 *         normalf = -normalf
 * 
 *     return normal, normalf             # <<<<<<<<<<<<<<
 */
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 42; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_INCREF(((PyObject *)__pyx_v_normal));
  PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject *)__pyx_v_normal));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_normal));
  __Pyx_INCREF(((PyObject *)__pyx_v_normalf));
  PyTuple_SET_ITEM(__pyx_t_2, 1, ((PyObject *)__pyx_v_normalf));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_normalf));
  __pyx_r = ((PyObject *)__pyx_t_2);
  __pyx_t_2 = 0;
  goto __pyx_L0;

  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_XDECREF(__pyx_t_5);
  __PYX_XDEC_MEMVIEW(&__pyx_t_13, 1);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_s.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_v.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_8a17498f1f23780b33414dc2e93cc08d.cy_compute_normal_naive", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_s.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_v.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_normalf);
  __Pyx_XDECREF((PyObject *)__pyx_v_normal);
  __PYX_XDEC_MEMVIEW(&__pyx_v_f, 1);
  __Pyx_XDECREF((PyObject *)__pyx_v_v);
  __Pyx_XDECREF((PyObject *)__pyx_v_s);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_1cy_compute_normal_naive(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_pw_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_1cy_compute_normal_naive(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
  PyArrayObject *__pyx_v_vertex = 0;
  PyArrayObject *__pyx_v_face = 0;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal_naive (wrapper)", 0);
  {
    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__vertex,&__pyx_n_s__face,0};
    PyObject* values[2] = {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  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
        case  0: break;
        default: goto __pyx_L5_argtuple_error;
      }
      kw_args = PyDict_Size(__pyx_kwds);
      switch (pos_args) {
        case  0:
        if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__vertex)) != 0)) kw_args--;
        else goto __pyx_L5_argtuple_error;
        case  1:
        if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__face)) != 0)) kw_args--;
        else {
          __Pyx_RaiseArgtupleInvalid("cy_compute_normal_naive", 1, 2, 2, 1); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
        }
      }
      if (unlikely(kw_args > 0)) {
        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "cy_compute_normal_naive") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
      }
    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
      goto __pyx_L5_argtuple_error;
    } else {
      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
    }
    __pyx_v_vertex = ((PyArrayObject *)values[0]);
    __pyx_v_face = ((PyArrayObject *)values[1]);
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L5_argtuple_error:;
  __Pyx_RaiseArgtupleInvalid("cy_compute_normal_naive", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_8a17498f1f23780b33414dc2e93cc08d.cy_compute_normal_naive", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_vertex), __pyx_ptype_5numpy_ndarray, 1, "vertex", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_face), __pyx_ptype_5numpy_ndarray, 1, "face", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_r = __pyx_pf_46_cython_magic_8a17498f1f23780b33414dc2e93cc08d_cy_compute_normal_naive(__pyx_self, __pyx_v_vertex, __pyx_v_face);
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;
  goto __pyx_L0;
  __pyx_L1_error:;
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

Looking above, we see that the majority of the code is still making calls back in to Python. In particular, the slow vetex loop is making a Python call every iteration. Therefore, we want to try and remove this.

In [24]:
%%cython -a

import numpy as np
cimport numpy as np
cimport cython


cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):
    # Avoid divisions by almost 0 numbers
    cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
    d[d < np.spacing(1)] = 1.0
    return vec / d[..., None]
 
    
cpdef cy_compute_normal_better(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
    cdef int nface = face.shape[0]
    cdef int nvert = vertex.shape[0]
    
    # unit normals to the faces
    cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
                                                             vertex[face[:, 2], :] - vertex[face[:, 0], :])
    normalf = cy_normalise_naive(normalf)
    
    # unit normal to the vertex
    cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
    cdef int f0, f1, f2
    for i in range(nface):
        f0 = face[i, 0]
        f1 = face[i, 1]
        f2 = face[i, 2]
        for j in range(3):
            normal[f0, j] += normalf[i, j]   
            normal[f1, j] += normalf[i, j]       
            normal[f2, j] += normalf[i, j]
    
    # normalize
    normal = cy_normalise_naive(normal)
    
    # enforce that the normal are outward
    cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
    cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
    if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
        # flip
        normal = -normal
        normalf = -normalf
    
    return normal, normalf
Out[24]:

Generated by Cython 0.19.1 on Fri Aug 16 16:37:23 2013

 1: 
 2: import numpy as np
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":2
 * 
 * import numpy as np             # <<<<<<<<<<<<<<
 * cimport numpy as np
 * cimport cython
 */
  __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__numpy), 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s__np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":14
 * 
 * 
 * cpdef cy_compute_normal_better(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_d, __pyx_n_s____test__, ((PyObject *)__pyx_t_1)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
 3: cimport numpy as np
 4: cimport cython
 5: 
 6: 
 7: cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):
/* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":7
 * 
 * 
 * cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):             # <<<<<<<<<<<<<<
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 */

static PyArrayObject *__pyx_f_46_cython_magic_e68c2952022b03511661b5dfa01f749b_cy_normalise_naive(PyArrayObject *__pyx_v_vec) {
  PyArrayObject *__pyx_v_d = 0;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_d;
  __Pyx_Buffer __pyx_pybuffer_d;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vec;
  __Pyx_Buffer __pyx_pybuffer_vec;
  PyArrayObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_normalise_naive", 0);
  __pyx_pybuffer_d.pybuffer.buf = NULL;
  __pyx_pybuffer_d.refcount = 0;
  __pyx_pybuffernd_d.data = NULL;
  __pyx_pybuffernd_d.rcbuffer = &__pyx_pybuffer_d;
  __pyx_pybuffer_vec.pybuffer.buf = NULL;
  __pyx_pybuffer_vec.refcount = 0;
  __pyx_pybuffernd_vec.data = NULL;
  __pyx_pybuffernd_vec.rcbuffer = &__pyx_pybuffer_vec;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vec.rcbuffer->pybuffer, (PyObject*)__pyx_v_vec, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vec.diminfo[0].strides = __pyx_pybuffernd_vec.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vec.diminfo[0].shape = __pyx_pybuffernd_vec.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vec.diminfo[1].strides = __pyx_pybuffernd_vec.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vec.diminfo[1].shape = __pyx_pybuffernd_vec.rcbuffer->pybuffer.shape[1];
 8:     # Avoid divisions by almost 0 numbers
 9:     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":9
 * cdef np.ndarray[np.float64_t, ndim=2] cy_normalise_naive(np.ndarray[np.float64_t, ndim=2] vec):
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))             # <<<<<<<<<<<<<<
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sqrt); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sum); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Power(((PyObject *)__pyx_v_vec), __pyx_int_2, Py_None); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_t_1, ((PyObject *)__pyx_n_s__axis), __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_5 = PyObject_Call(__pyx_t_3, ((PyObject *)__pyx_t_4), ((PyObject *)__pyx_t_1)); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __pyx_t_5 = 0;
  __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_6 = ((PyArrayObject *)__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_d.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_d = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_d.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_d.diminfo[0].strides = __pyx_pybuffernd_d.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_d.diminfo[0].shape = __pyx_pybuffernd_d.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_6 = 0;
  __pyx_v_d = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
 10:     d[d < np.spacing(1)] = 1.0
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":10
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0             # <<<<<<<<<<<<<<
 *     return vec / d[..., None]
 * 
 */
  __pyx_t_5 = PyFloat_FromDouble(1.0); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__spacing); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_k_tuple_1), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_RichCompare(((PyObject *)__pyx_v_d), __pyx_t_1, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  if (PyObject_SetItem(((PyObject *)__pyx_v_d), __pyx_t_2, __pyx_t_5) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;

  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":10
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0             # <<<<<<<<<<<<<<
 *     return vec / d[..., None]
 * 
 */
  __pyx_k_tuple_1 = PyTuple_Pack(1, __pyx_int_1); if (unlikely(!__pyx_k_tuple_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_1);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_1));
 11:     return vec / d[..., None]
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":11
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]             # <<<<<<<<<<<<<<
 * 
 * 
 */
  __Pyx_XDECREF(((PyObject *)__pyx_r));
  __pyx_t_5 = PyObject_GetItem(((PyObject *)__pyx_v_d), ((PyObject *)__pyx_k_tuple_2)); if (!__pyx_t_5) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_2 = __Pyx_PyNumber_Divide(((PyObject *)__pyx_v_vec), __pyx_t_5); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_r = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;
  goto __pyx_L0;

  __pyx_r = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_XDECREF(__pyx_t_5);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_d.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vec.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_e68c2952022b03511661b5dfa01f749b.cy_normalise_naive", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_d.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vec.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_d);
  __Pyx_XGIVEREF((PyObject *)__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":11
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]             # <<<<<<<<<<<<<<
 * 
 * 
 */
  __pyx_k_tuple_2 = PyTuple_Pack(2, Py_Ellipsis, Py_None); if (unlikely(!__pyx_k_tuple_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_2);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_2));
 12: 
 13: 
 14: cpdef cy_compute_normal_better(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
/* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":14
 * 
 * 
 * cpdef cy_compute_normal_better(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */

static PyObject *__pyx_pw_46_cython_magic_e68c2952022b03511661b5dfa01f749b_1cy_compute_normal_better(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_f_46_cython_magic_e68c2952022b03511661b5dfa01f749b_cy_compute_normal_better(PyArrayObject *__pyx_v_vertex, PyArrayObject *__pyx_v_face, CYTHON_UNUSED int __pyx_skip_dispatch) {
  int __pyx_v_nface;
  int __pyx_v_nvert;
  PyArrayObject *__pyx_v_normalf = 0;
  PyArrayObject *__pyx_v_normal = 0;
  int __pyx_v_f0;
  int __pyx_v_f1;
  int __pyx_v_f2;
  int __pyx_v_i;
  long __pyx_v_j;
  PyArrayObject *__pyx_v_v = 0;
  PyArrayObject *__pyx_v_s = 0;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_face;
  __Pyx_Buffer __pyx_pybuffer_face;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_normal;
  __Pyx_Buffer __pyx_pybuffer_normal;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_normalf;
  __Pyx_Buffer __pyx_pybuffer_normalf;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_s;
  __Pyx_Buffer __pyx_pybuffer_s;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_v;
  __Pyx_Buffer __pyx_pybuffer_v;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vertex;
  __Pyx_Buffer __pyx_pybuffer_vertex;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal_better", 0);
  __pyx_pybuffer_normalf.pybuffer.buf = NULL;
  __pyx_pybuffer_normalf.refcount = 0;
  __pyx_pybuffernd_normalf.data = NULL;
  __pyx_pybuffernd_normalf.rcbuffer = &__pyx_pybuffer_normalf;
  __pyx_pybuffer_normal.pybuffer.buf = NULL;
  __pyx_pybuffer_normal.refcount = 0;
  __pyx_pybuffernd_normal.data = NULL;
  __pyx_pybuffernd_normal.rcbuffer = &__pyx_pybuffer_normal;
  __pyx_pybuffer_v.pybuffer.buf = NULL;
  __pyx_pybuffer_v.refcount = 0;
  __pyx_pybuffernd_v.data = NULL;
  __pyx_pybuffernd_v.rcbuffer = &__pyx_pybuffer_v;
  __pyx_pybuffer_s.pybuffer.buf = NULL;
  __pyx_pybuffer_s.refcount = 0;
  __pyx_pybuffernd_s.data = NULL;
  __pyx_pybuffernd_s.rcbuffer = &__pyx_pybuffer_s;
  __pyx_pybuffer_vertex.pybuffer.buf = NULL;
  __pyx_pybuffer_vertex.refcount = 0;
  __pyx_pybuffernd_vertex.data = NULL;
  __pyx_pybuffernd_vertex.rcbuffer = &__pyx_pybuffer_vertex;
  __pyx_pybuffer_face.pybuffer.buf = NULL;
  __pyx_pybuffer_face.refcount = 0;
  __pyx_pybuffernd_face.data = NULL;
  __pyx_pybuffernd_face.rcbuffer = &__pyx_pybuffer_face;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer, (PyObject*)__pyx_v_vertex, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vertex.diminfo[0].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vertex.diminfo[0].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vertex.diminfo[1].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vertex.diminfo[1].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[1];
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_face.rcbuffer->pybuffer, (PyObject*)__pyx_v_face, &__Pyx_TypeInfo_int, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_face.diminfo[0].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_face.diminfo[0].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_face.diminfo[1].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_face.diminfo[1].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[1];

/* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":14
 * 
 * 
 * cpdef cy_compute_normal_better(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */

static PyObject *__pyx_pf_46_cython_magic_e68c2952022b03511661b5dfa01f749b_cy_compute_normal_better(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_vertex, PyArrayObject *__pyx_v_face) {
  __Pyx_LocalBuf_ND __pyx_pybuffernd_face;
  __Pyx_Buffer __pyx_pybuffer_face;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vertex;
  __Pyx_Buffer __pyx_pybuffer_vertex;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal_better", 0);
  __pyx_pybuffer_vertex.pybuffer.buf = NULL;
  __pyx_pybuffer_vertex.refcount = 0;
  __pyx_pybuffernd_vertex.data = NULL;
  __pyx_pybuffernd_vertex.rcbuffer = &__pyx_pybuffer_vertex;
  __pyx_pybuffer_face.pybuffer.buf = NULL;
  __pyx_pybuffer_face.refcount = 0;
  __pyx_pybuffernd_face.data = NULL;
  __pyx_pybuffernd_face.rcbuffer = &__pyx_pybuffer_face;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer, (PyObject*)__pyx_v_vertex, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vertex.diminfo[0].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vertex.diminfo[0].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vertex.diminfo[1].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vertex.diminfo[1].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[1];
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_face.rcbuffer->pybuffer, (PyObject*)__pyx_v_face, &__Pyx_TypeInfo_int, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_face.diminfo[0].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_face.diminfo[0].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_face.diminfo[1].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_face.diminfo[1].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[1];
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_1 = __pyx_f_46_cython_magic_e68c2952022b03511661b5dfa01f749b_cy_compute_normal_better(__pyx_v_vertex, __pyx_v_face, 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_r = __pyx_t_1;
  __pyx_t_1 = 0;
  goto __pyx_L0;

  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_e68c2952022b03511661b5dfa01f749b.cy_compute_normal_better", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static CYTHON_UNUSED int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags); /*proto*/
static CYTHON_UNUSED int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags) {
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("__getbuffer__ (wrapper)", 0);
  __pyx_r = __pyx_pf_5numpy_7ndarray___getbuffer__(((PyArrayObject *)__pyx_v_self), ((Py_buffer *)__pyx_v_info), ((int)__pyx_v_flags));
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 15:     cdef int nface = face.shape[0]
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":15
 * 
 * cpdef cy_compute_normal_better(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
 *     cdef int nface = face.shape[0]             # <<<<<<<<<<<<<<
 *     cdef int nvert = vertex.shape[0]
 * 
 */
  __pyx_v_nface = (__pyx_v_face->dimensions[0]);
 16:     cdef int nvert = vertex.shape[0]
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":16
 * cpdef cy_compute_normal_better(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]             # <<<<<<<<<<<<<<
 * 
 *     # unit normals to the faces
 */
  __pyx_v_nvert = (__pyx_v_vertex->dimensions[0]);
 17: 
 18:     # unit normals to the faces
 19:     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":19
 * 
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],             # <<<<<<<<<<<<<<
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = cy_normalise_naive(normalf)
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__cross); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":19
 * 
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],             # <<<<<<<<<<<<<<
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = cy_normalise_naive(normalf)
 */
  __pyx_k_slice_3 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_3);
  __Pyx_GIVEREF(__pyx_k_slice_3);
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_4)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_k_tuple_4 = PyTuple_Pack(2, __pyx_k_slice_3, __pyx_int_1); if (unlikely(!__pyx_k_tuple_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_4);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_4));
  __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __Pyx_INCREF(__pyx_k_slice_5);
  PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_k_slice_5);
  __Pyx_GIVEREF(__pyx_k_slice_5);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_3)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0;
  __pyx_k_slice_5 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_5);
  __Pyx_GIVEREF(__pyx_k_slice_5);
  __pyx_k_slice_6 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_6)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_6);
  __Pyx_GIVEREF(__pyx_k_slice_6);
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_7)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_k_tuple_7 = PyTuple_Pack(2, __pyx_k_slice_6, __pyx_int_0); if (unlikely(!__pyx_k_tuple_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_7);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_7));
  __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __Pyx_INCREF(__pyx_k_slice_8);
  PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_k_slice_8);
  __Pyx_GIVEREF(__pyx_k_slice_8);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_4)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __pyx_t_4 = PyNumber_Subtract(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_k_slice_8 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_8);
  __Pyx_GIVEREF(__pyx_k_slice_8);
 20:                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":20
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])             # <<<<<<<<<<<<<<
 *     normalf = cy_normalise_naive(normalf)
 * 
 */
  __pyx_k_slice_9 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_9);
  __Pyx_GIVEREF(__pyx_k_slice_9);

  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":20
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])             # <<<<<<<<<<<<<<
 *     normalf = cy_normalise_naive(normalf)
 * 
 */
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_10)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_k_tuple_10 = PyTuple_Pack(2, __pyx_k_slice_9, __pyx_int_2); if (unlikely(!__pyx_k_tuple_10)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_10);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_10));
  __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __Pyx_INCREF(__pyx_k_slice_11);
  PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_k_slice_11);
  __Pyx_GIVEREF(__pyx_k_slice_11);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_1)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __pyx_k_slice_11 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_11);
  __Pyx_GIVEREF(__pyx_k_slice_11);
  __pyx_k_slice_12 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_12);
  __Pyx_GIVEREF(__pyx_k_slice_12);
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_13)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_k_tuple_13 = PyTuple_Pack(2, __pyx_k_slice_12, __pyx_int_0); if (unlikely(!__pyx_k_tuple_13)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_13);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_13));
  __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __Pyx_INCREF(__pyx_k_slice_14);
  PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_k_slice_14);
  __Pyx_GIVEREF(__pyx_k_slice_14);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_5)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
  __pyx_t_5 = PyNumber_Subtract(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_4);
  __Pyx_GIVEREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __pyx_t_4 = 0;
  __pyx_t_5 = 0;
  __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_6 = ((PyArrayObject *)__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_normalf = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_6 = 0;
  __pyx_v_normalf = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
  __pyx_k_slice_14 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_14)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_14);
  __Pyx_GIVEREF(__pyx_k_slice_14);
 21:     normalf = cy_normalise_naive(normalf)
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":21
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = np.cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                              vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = cy_normalise_naive(normalf)             # <<<<<<<<<<<<<<
 * 
 *     # unit normal to the vertex
 */
  __pyx_t_5 = ((PyObject *)__pyx_f_46_cython_magic_e68c2952022b03511661b5dfa01f749b_cy_normalise_naive(((PyArrayObject *)__pyx_v_normalf))); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
    __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)((PyArrayObject *)__pyx_t_5), &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
    if (unlikely(__pyx_t_7 < 0)) {
      PyErr_Fetch(&__pyx_t_8, &__pyx_t_9, &__pyx_t_10);
      if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_v_normalf, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
        Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_10);
        __Pyx_RaiseBufferFallbackError();
      } else {
        PyErr_Restore(__pyx_t_8, __pyx_t_9, __pyx_t_10);
      }
    }
    __pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
    if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 21; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __Pyx_DECREF(((PyObject *)__pyx_v_normalf));
  __pyx_v_normalf = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
 22: 
 23:     # unit normal to the vertex
 24:     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":24
 * 
 *     # unit normal to the vertex
 *     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])             # <<<<<<<<<<<<<<
 *     cdef int f0, f1, f2
 *     for i in range(nface):
 */
  __pyx_t_5 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s__zeros); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __pyx_t_5 = PyInt_FromLong(__pyx_v_nvert); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_2 = PyList_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  PyList_SET_ITEM(__pyx_t_2, 0, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __Pyx_INCREF(__pyx_int_3);
  PyList_SET_ITEM(__pyx_t_2, 1, __pyx_int_3);
  __Pyx_GIVEREF(__pyx_int_3);
  __pyx_t_5 = 0;
  __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  PyTuple_SET_ITEM(__pyx_t_5, 0, ((PyObject *)__pyx_t_2));
  __Pyx_GIVEREF(((PyObject *)__pyx_t_2));
  __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_5), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_11 = ((PyArrayObject *)__pyx_t_2);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_t_11, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_normal = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 24; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_11 = 0;
  __pyx_v_normal = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;
 25:     cdef int f0, f1, f2
 26:     for i in range(nface):
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":26
 *     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
 *     cdef int f0, f1, f2
 *     for i in range(nface):             # <<<<<<<<<<<<<<
 *         f0 = face[i, 0]
 *         f1 = face[i, 1]
 */
  __pyx_t_7 = __pyx_v_nface;
  for (__pyx_t_12 = 0; __pyx_t_12 < __pyx_t_7; __pyx_t_12+=1) {
    __pyx_v_i = __pyx_t_12;
 27:         f0 = face[i, 0]
    /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":27
 *     cdef int f0, f1, f2
 *     for i in range(nface):
 *         f0 = face[i, 0]             # <<<<<<<<<<<<<<
 *         f1 = face[i, 1]
 *         f2 = face[i, 2]
 */
    __pyx_t_13 = __pyx_v_i;
    __pyx_t_14 = 0;
    __pyx_t_15 = -1;
    if (__pyx_t_13 < 0) {
      __pyx_t_13 += __pyx_pybuffernd_face.diminfo[0].shape;
      if (unlikely(__pyx_t_13 < 0)) __pyx_t_15 = 0;
    } else if (unlikely(__pyx_t_13 >= __pyx_pybuffernd_face.diminfo[0].shape)) __pyx_t_15 = 0;
    if (__pyx_t_14 < 0) {
      __pyx_t_14 += __pyx_pybuffernd_face.diminfo[1].shape;
      if (unlikely(__pyx_t_14 < 0)) __pyx_t_15 = 1;
    } else if (unlikely(__pyx_t_14 >= __pyx_pybuffernd_face.diminfo[1].shape)) __pyx_t_15 = 1;
    if (unlikely(__pyx_t_15 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_15);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_v_f0 = (*__Pyx_BufPtrStrided2d(int *, __pyx_pybuffernd_face.rcbuffer->pybuffer.buf, __pyx_t_13, __pyx_pybuffernd_face.diminfo[0].strides, __pyx_t_14, __pyx_pybuffernd_face.diminfo[1].strides));
 28:         f1 = face[i, 1]
    /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":28
 *     for i in range(nface):
 *         f0 = face[i, 0]
 *         f1 = face[i, 1]             # <<<<<<<<<<<<<<
 *         f2 = face[i, 2]
 *         for j in range(3):
 */
    __pyx_t_15 = __pyx_v_i;
    __pyx_t_16 = 1;
    __pyx_t_17 = -1;
    if (__pyx_t_15 < 0) {
      __pyx_t_15 += __pyx_pybuffernd_face.diminfo[0].shape;
      if (unlikely(__pyx_t_15 < 0)) __pyx_t_17 = 0;
    } else if (unlikely(__pyx_t_15 >= __pyx_pybuffernd_face.diminfo[0].shape)) __pyx_t_17 = 0;
    if (__pyx_t_16 < 0) {
      __pyx_t_16 += __pyx_pybuffernd_face.diminfo[1].shape;
      if (unlikely(__pyx_t_16 < 0)) __pyx_t_17 = 1;
    } else if (unlikely(__pyx_t_16 >= __pyx_pybuffernd_face.diminfo[1].shape)) __pyx_t_17 = 1;
    if (unlikely(__pyx_t_17 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_17);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_v_f1 = (*__Pyx_BufPtrStrided2d(int *, __pyx_pybuffernd_face.rcbuffer->pybuffer.buf, __pyx_t_15, __pyx_pybuffernd_face.diminfo[0].strides, __pyx_t_16, __pyx_pybuffernd_face.diminfo[1].strides));
 29:         f2 = face[i, 2]
    /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":29
 *         f0 = face[i, 0]
 *         f1 = face[i, 1]
 *         f2 = face[i, 2]             # <<<<<<<<<<<<<<
 *         for j in range(3):
 *             normal[f0, j] += normalf[i, j]
 */
    __pyx_t_17 = __pyx_v_i;
    __pyx_t_18 = 2;
    __pyx_t_19 = -1;
    if (__pyx_t_17 < 0) {
      __pyx_t_17 += __pyx_pybuffernd_face.diminfo[0].shape;
      if (unlikely(__pyx_t_17 < 0)) __pyx_t_19 = 0;
    } else if (unlikely(__pyx_t_17 >= __pyx_pybuffernd_face.diminfo[0].shape)) __pyx_t_19 = 0;
    if (__pyx_t_18 < 0) {
      __pyx_t_18 += __pyx_pybuffernd_face.diminfo[1].shape;
      if (unlikely(__pyx_t_18 < 0)) __pyx_t_19 = 1;
    } else if (unlikely(__pyx_t_18 >= __pyx_pybuffernd_face.diminfo[1].shape)) __pyx_t_19 = 1;
    if (unlikely(__pyx_t_19 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_19);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 29; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_v_f2 = (*__Pyx_BufPtrStrided2d(int *, __pyx_pybuffernd_face.rcbuffer->pybuffer.buf, __pyx_t_17, __pyx_pybuffernd_face.diminfo[0].strides, __pyx_t_18, __pyx_pybuffernd_face.diminfo[1].strides));
 30:         for j in range(3):
    /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":30
 *         f1 = face[i, 1]
 *         f2 = face[i, 2]
 *         for j in range(3):             # <<<<<<<<<<<<<<
 *             normal[f0, j] += normalf[i, j]
 *             normal[f1, j] += normalf[i, j]
 */
    for (__pyx_t_20 = 0; __pyx_t_20 < 3; __pyx_t_20+=1) {
      __pyx_v_j = __pyx_t_20;
 31:             normal[f0, j] += normalf[i, j]
      /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":31
 *         f2 = face[i, 2]
 *         for j in range(3):
 *             normal[f0, j] += normalf[i, j]             # <<<<<<<<<<<<<<
 *             normal[f1, j] += normalf[i, j]
 *             normal[f2, j] += normalf[i, j]
 */
      __pyx_t_19 = __pyx_v_i;
      __pyx_t_21 = __pyx_v_j;
      __pyx_t_22 = -1;
      if (__pyx_t_19 < 0) {
        __pyx_t_19 += __pyx_pybuffernd_normalf.diminfo[0].shape;
        if (unlikely(__pyx_t_19 < 0)) __pyx_t_22 = 0;
      } else if (unlikely(__pyx_t_19 >= __pyx_pybuffernd_normalf.diminfo[0].shape)) __pyx_t_22 = 0;
      if (__pyx_t_21 < 0) {
        __pyx_t_21 += __pyx_pybuffernd_normalf.diminfo[1].shape;
        if (unlikely(__pyx_t_21 < 0)) __pyx_t_22 = 1;
      } else if (unlikely(__pyx_t_21 >= __pyx_pybuffernd_normalf.diminfo[1].shape)) __pyx_t_22 = 1;
      if (unlikely(__pyx_t_22 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_22);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      __pyx_t_22 = __pyx_v_f0;
      __pyx_t_23 = __pyx_v_j;
      __pyx_t_24 = -1;
      if (__pyx_t_22 < 0) {
        __pyx_t_22 += __pyx_pybuffernd_normal.diminfo[0].shape;
        if (unlikely(__pyx_t_22 < 0)) __pyx_t_24 = 0;
      } else if (unlikely(__pyx_t_22 >= __pyx_pybuffernd_normal.diminfo[0].shape)) __pyx_t_24 = 0;
      if (__pyx_t_23 < 0) {
        __pyx_t_23 += __pyx_pybuffernd_normal.diminfo[1].shape;
        if (unlikely(__pyx_t_23 < 0)) __pyx_t_24 = 1;
      } else if (unlikely(__pyx_t_23 >= __pyx_pybuffernd_normal.diminfo[1].shape)) __pyx_t_24 = 1;
      if (unlikely(__pyx_t_24 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_24);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf, __pyx_t_22, __pyx_pybuffernd_normal.diminfo[0].strides, __pyx_t_23, __pyx_pybuffernd_normal.diminfo[1].strides) += (*__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf, __pyx_t_19, __pyx_pybuffernd_normalf.diminfo[0].strides, __pyx_t_21, __pyx_pybuffernd_normalf.diminfo[1].strides));
 32:             normal[f1, j] += normalf[i, j]
      /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":32
 *         for j in range(3):
 *             normal[f0, j] += normalf[i, j]
 *             normal[f1, j] += normalf[i, j]             # <<<<<<<<<<<<<<
 *             normal[f2, j] += normalf[i, j]
 * 
 */
      __pyx_t_24 = __pyx_v_i;
      __pyx_t_25 = __pyx_v_j;
      __pyx_t_26 = -1;
      if (__pyx_t_24 < 0) {
        __pyx_t_24 += __pyx_pybuffernd_normalf.diminfo[0].shape;
        if (unlikely(__pyx_t_24 < 0)) __pyx_t_26 = 0;
      } else if (unlikely(__pyx_t_24 >= __pyx_pybuffernd_normalf.diminfo[0].shape)) __pyx_t_26 = 0;
      if (__pyx_t_25 < 0) {
        __pyx_t_25 += __pyx_pybuffernd_normalf.diminfo[1].shape;
        if (unlikely(__pyx_t_25 < 0)) __pyx_t_26 = 1;
      } else if (unlikely(__pyx_t_25 >= __pyx_pybuffernd_normalf.diminfo[1].shape)) __pyx_t_26 = 1;
      if (unlikely(__pyx_t_26 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_26);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      __pyx_t_26 = __pyx_v_f1;
      __pyx_t_27 = __pyx_v_j;
      __pyx_t_28 = -1;
      if (__pyx_t_26 < 0) {
        __pyx_t_26 += __pyx_pybuffernd_normal.diminfo[0].shape;
        if (unlikely(__pyx_t_26 < 0)) __pyx_t_28 = 0;
      } else if (unlikely(__pyx_t_26 >= __pyx_pybuffernd_normal.diminfo[0].shape)) __pyx_t_28 = 0;
      if (__pyx_t_27 < 0) {
        __pyx_t_27 += __pyx_pybuffernd_normal.diminfo[1].shape;
        if (unlikely(__pyx_t_27 < 0)) __pyx_t_28 = 1;
      } else if (unlikely(__pyx_t_27 >= __pyx_pybuffernd_normal.diminfo[1].shape)) __pyx_t_28 = 1;
      if (unlikely(__pyx_t_28 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_28);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf, __pyx_t_26, __pyx_pybuffernd_normal.diminfo[0].strides, __pyx_t_27, __pyx_pybuffernd_normal.diminfo[1].strides) += (*__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf, __pyx_t_24, __pyx_pybuffernd_normalf.diminfo[0].strides, __pyx_t_25, __pyx_pybuffernd_normalf.diminfo[1].strides));
 33:             normal[f2, j] += normalf[i, j]
      /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":33
 *             normal[f0, j] += normalf[i, j]
 *             normal[f1, j] += normalf[i, j]
 *             normal[f2, j] += normalf[i, j]             # <<<<<<<<<<<<<<
 * 
 *     # normalize
 */
      __pyx_t_28 = __pyx_v_i;
      __pyx_t_29 = __pyx_v_j;
      __pyx_t_30 = -1;
      if (__pyx_t_28 < 0) {
        __pyx_t_28 += __pyx_pybuffernd_normalf.diminfo[0].shape;
        if (unlikely(__pyx_t_28 < 0)) __pyx_t_30 = 0;
      } else if (unlikely(__pyx_t_28 >= __pyx_pybuffernd_normalf.diminfo[0].shape)) __pyx_t_30 = 0;
      if (__pyx_t_29 < 0) {
        __pyx_t_29 += __pyx_pybuffernd_normalf.diminfo[1].shape;
        if (unlikely(__pyx_t_29 < 0)) __pyx_t_30 = 1;
      } else if (unlikely(__pyx_t_29 >= __pyx_pybuffernd_normalf.diminfo[1].shape)) __pyx_t_30 = 1;
      if (unlikely(__pyx_t_30 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_30);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 33; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      __pyx_t_30 = __pyx_v_f2;
      __pyx_t_31 = __pyx_v_j;
      __pyx_t_32 = -1;
      if (__pyx_t_30 < 0) {
        __pyx_t_30 += __pyx_pybuffernd_normal.diminfo[0].shape;
        if (unlikely(__pyx_t_30 < 0)) __pyx_t_32 = 0;
      } else if (unlikely(__pyx_t_30 >= __pyx_pybuffernd_normal.diminfo[0].shape)) __pyx_t_32 = 0;
      if (__pyx_t_31 < 0) {
        __pyx_t_31 += __pyx_pybuffernd_normal.diminfo[1].shape;
        if (unlikely(__pyx_t_31 < 0)) __pyx_t_32 = 1;
      } else if (unlikely(__pyx_t_31 >= __pyx_pybuffernd_normal.diminfo[1].shape)) __pyx_t_32 = 1;
      if (unlikely(__pyx_t_32 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_32);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 33; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf, __pyx_t_30, __pyx_pybuffernd_normal.diminfo[0].strides, __pyx_t_31, __pyx_pybuffernd_normal.diminfo[1].strides) += (*__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf, __pyx_t_28, __pyx_pybuffernd_normalf.diminfo[0].strides, __pyx_t_29, __pyx_pybuffernd_normalf.diminfo[1].strides));
    }
  }
 34: 
 35:     # normalize
 36:     normal = cy_normalise_naive(normal)
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":36
 * 
 *     # normalize
 *     normal = cy_normalise_naive(normal)             # <<<<<<<<<<<<<<
 * 
 *     # enforce that the normal are outward
 */
  __pyx_t_2 = ((PyObject *)__pyx_f_46_cython_magic_e68c2952022b03511661b5dfa01f749b_cy_normalise_naive(((PyArrayObject *)__pyx_v_normal))); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
    __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)((PyArrayObject *)__pyx_t_2), &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack);
    if (unlikely(__pyx_t_7 < 0)) {
      PyErr_Fetch(&__pyx_t_10, &__pyx_t_9, &__pyx_t_8);
      if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_v_normal, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
        Py_XDECREF(__pyx_t_10); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_8);
        __Pyx_RaiseBufferFallbackError();
      } else {
        PyErr_Restore(__pyx_t_10, __pyx_t_9, __pyx_t_8);
      }
    }
    __pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
    if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 36; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __Pyx_DECREF(((PyObject *)__pyx_v_normal));
  __pyx_v_normal = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;
 37: 
 38:     # enforce that the normal are outward
 39:     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":39
 * 
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]             # <<<<<<<<<<<<<<
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 */
  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s__mean); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_INCREF(((PyObject *)__pyx_v_vertex));
  PyTuple_SET_ITEM(__pyx_t_2, 0, ((PyObject *)__pyx_v_vertex));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_vertex));
  __pyx_t_1 = PyObject_Call(__pyx_t_5, ((PyObject *)__pyx_t_2), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_GetItem(__pyx_t_1, ((PyObject *)__pyx_k_tuple_15)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Subtract(((PyObject *)__pyx_v_vertex), __pyx_t_2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  if (!(likely(((__pyx_t_1) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_1, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_33 = ((PyArrayObject *)__pyx_t_1);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_v.rcbuffer->pybuffer, (PyObject*)__pyx_t_33, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_v = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_v.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_v.diminfo[0].strides = __pyx_pybuffernd_v.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_v.diminfo[0].shape = __pyx_pybuffernd_v.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_v.diminfo[1].strides = __pyx_pybuffernd_v.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_v.diminfo[1].shape = __pyx_pybuffernd_v.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_33 = 0;
  __pyx_v_v = ((PyArrayObject *)__pyx_t_1);
  __pyx_t_1 = 0;

  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":39
 * 
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]             # <<<<<<<<<<<<<<
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 */
  __pyx_k_tuple_15 = PyTuple_Pack(2, Py_Ellipsis, Py_None); if (unlikely(!__pyx_k_tuple_15)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_15);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_15));
 40:     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":40
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)             # <<<<<<<<<<<<<<
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 *         # flip
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sum); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Multiply(((PyObject *)__pyx_v_v), ((PyObject *)__pyx_v_normal)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_t_1, ((PyObject *)__pyx_n_s__axis), __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_4 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_5), ((PyObject *)__pyx_t_1)); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_4) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_4, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_34 = ((PyArrayObject *)__pyx_t_4);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_s.rcbuffer->pybuffer, (PyObject*)__pyx_t_34, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_s = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_s.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_s.diminfo[0].strides = __pyx_pybuffernd_s.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_s.diminfo[0].shape = __pyx_pybuffernd_s.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_34 = 0;
  __pyx_v_s = ((PyArrayObject *)__pyx_t_4);
  __pyx_t_4 = 0;
 41:     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":41
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):             # <<<<<<<<<<<<<<
 *         # flip
 *         normal = -normal
 */
  __pyx_t_4 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s__sum); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __pyx_t_4 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s__greater); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_INCREF(((PyObject *)__pyx_v_s));
  PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)__pyx_v_s));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_s));
  __Pyx_INCREF(__pyx_int_0);
  PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_int_0);
  __Pyx_GIVEREF(__pyx_int_0);
  __pyx_t_2 = PyObject_Call(__pyx_t_5, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_2);
  __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __pyx_t_4 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s__sum); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __pyx_t_4 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s__less); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_INCREF(((PyObject *)__pyx_v_s));
  PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)__pyx_v_s));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_s));
  __Pyx_INCREF(__pyx_int_0);
  PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_int_0);
  __Pyx_GIVEREF(__pyx_int_0);
  __pyx_t_3 = PyObject_Call(__pyx_t_5, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __pyx_t_4 = PyObject_RichCompare(__pyx_t_2, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_4); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_t_35 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely(__pyx_t_35 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 41; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  if (__pyx_t_35) {
 42:         # flip
 43:         normal = -normal
    /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":43
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 *         # flip
 *         normal = -normal             # <<<<<<<<<<<<<<
 *         normalf = -normalf
 * 
 */
    __pyx_t_4 = PyNumber_Negative(((PyObject *)__pyx_v_normal)); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 43; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_4);
    if (!(likely(((__pyx_t_4) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_4, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 43; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __pyx_t_11 = ((PyArrayObject *)__pyx_t_4);
    {
      __Pyx_BufFmt_StackElem __pyx_stack[1];
      __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
      __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_t_11, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack);
      if (unlikely(__pyx_t_7 < 0)) {
        PyErr_Fetch(&__pyx_t_8, &__pyx_t_9, &__pyx_t_10);
        if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_v_normal, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
          Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_10);
          __Pyx_RaiseBufferFallbackError();
        } else {
          PyErr_Restore(__pyx_t_8, __pyx_t_9, __pyx_t_10);
        }
      }
      __pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
      if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 43; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_11 = 0;
    __Pyx_DECREF(((PyObject *)__pyx_v_normal));
    __pyx_v_normal = ((PyArrayObject *)__pyx_t_4);
    __pyx_t_4 = 0;
 44:         normalf = -normalf
    /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":44
 *         # flip
 *         normal = -normal
 *         normalf = -normalf             # <<<<<<<<<<<<<<
 * 
 *     return normal, normalf
 */
    __pyx_t_4 = PyNumber_Negative(((PyObject *)__pyx_v_normalf)); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_4);
    if (!(likely(((__pyx_t_4) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_4, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __pyx_t_6 = ((PyArrayObject *)__pyx_t_4);
    {
      __Pyx_BufFmt_StackElem __pyx_stack[1];
      __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
      __pyx_t_7 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
      if (unlikely(__pyx_t_7 < 0)) {
        PyErr_Fetch(&__pyx_t_10, &__pyx_t_9, &__pyx_t_8);
        if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_v_normalf, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
          Py_XDECREF(__pyx_t_10); Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_8);
          __Pyx_RaiseBufferFallbackError();
        } else {
          PyErr_Restore(__pyx_t_10, __pyx_t_9, __pyx_t_8);
        }
      }
      __pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
      if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_6 = 0;
    __Pyx_DECREF(((PyObject *)__pyx_v_normalf));
    __pyx_v_normalf = ((PyArrayObject *)__pyx_t_4);
    __pyx_t_4 = 0;
    goto __pyx_L7;
  }
  __pyx_L7:;
 45: 
 46:     return normal, normalf
  /* "_cython_magic_e68c2952022b03511661b5dfa01f749b.pyx":46
 *         normalf = -normalf
 * 
 *     return normal, normalf             # <<<<<<<<<<<<<<
 */
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 46; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_INCREF(((PyObject *)__pyx_v_normal));
  PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)__pyx_v_normal));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_normal));
  __Pyx_INCREF(((PyObject *)__pyx_v_normalf));
  PyTuple_SET_ITEM(__pyx_t_4, 1, ((PyObject *)__pyx_v_normalf));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_normalf));
  __pyx_r = ((PyObject *)__pyx_t_4);
  __pyx_t_4 = 0;
  goto __pyx_L0;

  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_XDECREF(__pyx_t_5);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_s.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_v.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_e68c2952022b03511661b5dfa01f749b.cy_compute_normal_better", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_s.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_v.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_normalf);
  __Pyx_XDECREF((PyObject *)__pyx_v_normal);
  __Pyx_XDECREF((PyObject *)__pyx_v_v);
  __Pyx_XDECREF((PyObject *)__pyx_v_s);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_e68c2952022b03511661b5dfa01f749b_1cy_compute_normal_better(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_pw_46_cython_magic_e68c2952022b03511661b5dfa01f749b_1cy_compute_normal_better(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
  PyArrayObject *__pyx_v_vertex = 0;
  PyArrayObject *__pyx_v_face = 0;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal_better (wrapper)", 0);
  {
    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__vertex,&__pyx_n_s__face,0};
    PyObject* values[2] = {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  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
        case  0: break;
        default: goto __pyx_L5_argtuple_error;
      }
      kw_args = PyDict_Size(__pyx_kwds);
      switch (pos_args) {
        case  0:
        if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__vertex)) != 0)) kw_args--;
        else goto __pyx_L5_argtuple_error;
        case  1:
        if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__face)) != 0)) kw_args--;
        else {
          __Pyx_RaiseArgtupleInvalid("cy_compute_normal_better", 1, 2, 2, 1); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
        }
      }
      if (unlikely(kw_args > 0)) {
        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "cy_compute_normal_better") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
      }
    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
      goto __pyx_L5_argtuple_error;
    } else {
      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
    }
    __pyx_v_vertex = ((PyArrayObject *)values[0]);
    __pyx_v_face = ((PyArrayObject *)values[1]);
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L5_argtuple_error:;
  __Pyx_RaiseArgtupleInvalid("cy_compute_normal_better", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_e68c2952022b03511661b5dfa01f749b.cy_compute_normal_better", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_vertex), __pyx_ptype_5numpy_ndarray, 1, "vertex", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_face), __pyx_ptype_5numpy_ndarray, 1, "face", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 14; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_r = __pyx_pf_46_cython_magic_e68c2952022b03511661b5dfa01f749b_cy_compute_normal_better(__pyx_self, __pyx_v_vertex, __pyx_v_face);
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;
  goto __pyx_L0;
  __pyx_L1_error:;
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

Eureka! By turning lines 24-36 to pure C, just by guaranteeing their accesses as C types, we have sped up our function to approximately 80 ms. This represents an approximate 38x speedup from the original! And all we did was partially unwrap a single loop. This is the key when trying to optimise Cython code. You need to ensure that all loops make as few calls in to Python code as possible.

In [25]:
%timeit cy_compute_normal_better(points, tris)
10 loops, best of 3: 73 ms per loop

Diminishing Returns

So now the game has become trying to turn as much of that Yellow code in to white code. Note that there is certainly a diminishing law of returns going on here. Our previous optimisation was almost certainly the largest jump in performance we will be able to achieve. Given that we don't gave any other loops, we are unlikely to get large 100+% jumps in performance. Numpy calls are already vectorized and manually unrolling them in to loops will not yield a very big performance boost. If we run the magic function %prun, this will give us profiling information about the functions that are called. We use the -r flag in order to return the profiler object so that we can print it in to the cell. Normally, this need just be called as:

%prun cy_compute_normal_better(points, tris)

which opens up a seperate window in the notebook.

In [26]:
p = %prun -r cy_compute_normal_better(points, tris)
p.print_stats()
          40 function calls in 0.075 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.043    0.043    0.075    0.075 {_cython_magic_e68c2952022b03511661b5dfa01f749b.cy_compute_normal_better}
        4    0.028    0.007    0.028    0.007 {numpy.core.multiarray.array}
        1    0.002    0.002    0.030    0.030 numeric.py:1214(cross)
        6    0.002    0.000    0.002    0.000 {method 'reduce' of 'numpy.ufunc' objects}
        5    0.000    0.000    0.002    0.000 fromnumeric.py:1422(sum)
        1    0.000    0.000    0.000    0.000 _methods.py:42(_mean)
        5    0.000    0.000    0.002    0.000 _methods.py:16(_sum)
        1    0.000    0.000    0.000    0.000 _methods.py:32(_count_reduce_items)
        3    0.000    0.000    0.000    0.000 {method 'swapaxes' of 'numpy.ndarray' objects}
        2    0.000    0.000    0.000    0.000 numeric.py:252(asarray)
        7    0.000    0.000    0.000    0.000 {isinstance}
        1    0.000    0.000    0.075    0.075 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:2405(mean)
        1    0.000    0.000    0.000    0.000 numeric.py:322(asanyarray)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


Out[26]:
<pstats.Stats instance at 0x96f6290>

The profiling output tells us that the majority of the time is spent inside the Cython function. However, almost half the time is spent inside the numpy cross product function. Looking at the source code of numpy's cross product shows us that it does a bunch of checks to try and ensure that the shapes of the vectors match. However, we know that are guaranteed to have standard Nx3 vectors. So, what happens if we roll our own cross product method?

In [27]:
%%cython -a

import numpy as np
cimport numpy as np
cimport cython

        
cdef np.ndarray[np.float64_t, ndim=2] normalise(np.ndarray[np.float64_t, ndim=2] vec):
    # Avoid divisions by almost 0 numbers
    cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
    d[d < np.spacing(1)] = 1.0
    return vec / d[..., None]
     

cdef inline np.ndarray[np.float64_t, ndim=2] cross(double[:, :] x, double[:, :] y):
    cdef np.ndarray[np.float64_t, ndim=2] z = np.empty_like(x)
    cdef int n = x.shape[0]
    for i in range(n):
        z[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]
        z[i, 1] = x[i, 2] * y[i, 0] - x[i, 0] * y[i, 2]
        z[i, 2] = x[i, 0] * y[i, 1] - x[i, 1] * y[i, 0]
    
    return z


cpdef cy_compute_normal(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
    cdef int nface = face.shape[0]
    cdef int nvert = vertex.shape[0]
    
    # unit normals to the faces
    cdef np.ndarray[np.float64_t, ndim=2] normalf = cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
                                                          vertex[face[:, 2], :] - vertex[face[:, 0], :])
    normalf = normalise(normalf)
    
    # unit normal to the vertex
    cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
    cdef int f0, f1, f2
    for i in range(nface):
        f0 = face[i, 0]
        f1 = face[i, 1]
        f2 = face[i, 2]
        for j in range(3):
            normal[f0, j] += normalf[i, j]   
            normal[f1, j] += normalf[i, j]       
            normal[f2, j] += normalf[i, j]
    
    # normalize
    normal = normalise(normal)
    
    # enforce that the normal are outward
    cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
    cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
    if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
        # flip
        normal = -normal
        normalf = -normalf
    
    return normal, normalf
Out[27]:

Generated by Cython 0.19.1 on Fri Aug 16 16:37:23 2013

 1: 
 2: import numpy as np
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":2
 * 
 * import numpy as np             # <<<<<<<<<<<<<<
 * cimport numpy as np
 * cimport cython
 */
  __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__numpy), 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s__np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":25
 * 
 * 
 * cpdef cy_compute_normal(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_d, __pyx_n_s____test__, ((PyObject *)__pyx_t_1)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
 3: cimport numpy as np
 4: cimport cython
 5: 
 6: 
 7: cdef np.ndarray[np.float64_t, ndim=2] normalise(np.ndarray[np.float64_t, ndim=2] vec):
/* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":7
 * 
 * 
 * cdef np.ndarray[np.float64_t, ndim=2] normalise(np.ndarray[np.float64_t, ndim=2] vec):             # <<<<<<<<<<<<<<
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 */

static PyArrayObject *__pyx_f_46_cython_magic_637c713b986c525e98b048fef3dc5c09_normalise(PyArrayObject *__pyx_v_vec) {
  PyArrayObject *__pyx_v_d = 0;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_d;
  __Pyx_Buffer __pyx_pybuffer_d;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vec;
  __Pyx_Buffer __pyx_pybuffer_vec;
  PyArrayObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("normalise", 0);
  __pyx_pybuffer_d.pybuffer.buf = NULL;
  __pyx_pybuffer_d.refcount = 0;
  __pyx_pybuffernd_d.data = NULL;
  __pyx_pybuffernd_d.rcbuffer = &__pyx_pybuffer_d;
  __pyx_pybuffer_vec.pybuffer.buf = NULL;
  __pyx_pybuffer_vec.refcount = 0;
  __pyx_pybuffernd_vec.data = NULL;
  __pyx_pybuffernd_vec.rcbuffer = &__pyx_pybuffer_vec;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vec.rcbuffer->pybuffer, (PyObject*)__pyx_v_vec, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vec.diminfo[0].strides = __pyx_pybuffernd_vec.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vec.diminfo[0].shape = __pyx_pybuffernd_vec.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vec.diminfo[1].strides = __pyx_pybuffernd_vec.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vec.diminfo[1].shape = __pyx_pybuffernd_vec.rcbuffer->pybuffer.shape[1];
 8:     # Avoid divisions by almost 0 numbers
 9:     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":9
 * cdef np.ndarray[np.float64_t, ndim=2] normalise(np.ndarray[np.float64_t, ndim=2] vec):
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))             # <<<<<<<<<<<<<<
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sqrt); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__sum); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Power(((PyObject *)__pyx_v_vec), __pyx_int_2, Py_None); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
  if (PyDict_SetItem(__pyx_t_1, ((PyObject *)__pyx_n_s__axis), __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_5 = PyObject_Call(__pyx_t_3, ((PyObject *)__pyx_t_4), ((PyObject *)__pyx_t_1)); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_5);
  __pyx_t_5 = 0;
  __pyx_t_5 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_5) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_5, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_6 = ((PyArrayObject *)__pyx_t_5);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_d.rcbuffer->pybuffer, (PyObject*)__pyx_t_6, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_d = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_d.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_d.diminfo[0].strides = __pyx_pybuffernd_d.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_d.diminfo[0].shape = __pyx_pybuffernd_d.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_6 = 0;
  __pyx_v_d = ((PyArrayObject *)__pyx_t_5);
  __pyx_t_5 = 0;
 10:     d[d < np.spacing(1)] = 1.0
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":10
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0             # <<<<<<<<<<<<<<
 *     return vec / d[..., None]
 * 
 */
  __pyx_t_5 = PyFloat_FromDouble(1.0); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__spacing); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_k_tuple_1), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_RichCompare(((PyObject *)__pyx_v_d), __pyx_t_1, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  if (PyObject_SetItem(((PyObject *)__pyx_v_d), __pyx_t_2, __pyx_t_5) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;

  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":10
 *     # Avoid divisions by almost 0 numbers
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0             # <<<<<<<<<<<<<<
 *     return vec / d[..., None]
 * 
 */
  __pyx_k_tuple_1 = PyTuple_Pack(1, __pyx_int_1); if (unlikely(!__pyx_k_tuple_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_1);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_1));
 11:     return vec / d[..., None]
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":11
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]             # <<<<<<<<<<<<<<
 * 
 * 
 */
  __Pyx_XDECREF(((PyObject *)__pyx_r));
  __pyx_t_5 = PyObject_GetItem(((PyObject *)__pyx_v_d), ((PyObject *)__pyx_k_tuple_2)); if (!__pyx_t_5) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_2 = __Pyx_PyNumber_Divide(((PyObject *)__pyx_v_vec), __pyx_t_5); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_r = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;
  goto __pyx_L0;

  __pyx_r = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_XDECREF(__pyx_t_5);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_d.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vec.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_637c713b986c525e98b048fef3dc5c09.normalise", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_d.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vec.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_d);
  __Pyx_XGIVEREF((PyObject *)__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":11
 *     cdef np.ndarray[np.float64_t, ndim=1] d = np.sqrt(np.sum(vec ** 2, axis=1))
 *     d[d < np.spacing(1)] = 1.0
 *     return vec / d[..., None]             # <<<<<<<<<<<<<<
 * 
 * 
 */
  __pyx_k_tuple_2 = PyTuple_Pack(2, Py_Ellipsis, Py_None); if (unlikely(!__pyx_k_tuple_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_2);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_2));
 12: 
 13: 
 14: cdef inline np.ndarray[np.float64_t, ndim=2] cross(double[:, :] x, double[:, :] y):
/* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":14
 * 
 * 
 * cdef inline np.ndarray[np.float64_t, ndim=2] cross(double[:, :] x, double[:, :] y):             # <<<<<<<<<<<<<<
 *     cdef np.ndarray[np.float64_t, ndim=2] z = np.empty_like(x)
 *     cdef int n = x.shape[0]
 */

static CYTHON_INLINE PyArrayObject *__pyx_f_46_cython_magic_637c713b986c525e98b048fef3dc5c09_cross(__Pyx_memviewslice __pyx_v_x, __Pyx_memviewslice __pyx_v_y) {
  PyArrayObject *__pyx_v_z = 0;
  int __pyx_v_n;
  int __pyx_v_i;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_z;
  __Pyx_Buffer __pyx_pybuffer_z;
  PyArrayObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cross", 0);
  __pyx_pybuffer_z.pybuffer.buf = NULL;
  __pyx_pybuffer_z.refcount = 0;
  __pyx_pybuffernd_z.data = NULL;
  __pyx_pybuffernd_z.rcbuffer = &__pyx_pybuffer_z;
 15:     cdef np.ndarray[np.float64_t, ndim=2] z = np.empty_like(x)
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":15
 * 
 * cdef inline np.ndarray[np.float64_t, ndim=2] cross(double[:, :] x, double[:, :] y):
 *     cdef np.ndarray[np.float64_t, ndim=2] z = np.empty_like(x)             # <<<<<<<<<<<<<<
 *     cdef int n = x.shape[0]
 *     for i in range(n):
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__empty_like); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __pyx_memoryview_fromslice(__pyx_v_x, 2, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_3), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0;
  if (!(likely(((__pyx_t_1) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_1, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_4 = ((PyArrayObject *)__pyx_t_1);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_z.rcbuffer->pybuffer, (PyObject*)__pyx_t_4, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_z = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_z.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 15; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_z.diminfo[0].strides = __pyx_pybuffernd_z.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_z.diminfo[0].shape = __pyx_pybuffernd_z.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_z.diminfo[1].strides = __pyx_pybuffernd_z.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_z.diminfo[1].shape = __pyx_pybuffernd_z.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_4 = 0;
  __pyx_v_z = ((PyArrayObject *)__pyx_t_1);
  __pyx_t_1 = 0;
 16:     cdef int n = x.shape[0]
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":16
 * cdef inline np.ndarray[np.float64_t, ndim=2] cross(double[:, :] x, double[:, :] y):
 *     cdef np.ndarray[np.float64_t, ndim=2] z = np.empty_like(x)
 *     cdef int n = x.shape[0]             # <<<<<<<<<<<<<<
 *     for i in range(n):
 *         z[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]
 */
  __pyx_v_n = (__pyx_v_x.shape[0]);
 17:     for i in range(n):
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":17
 *     cdef np.ndarray[np.float64_t, ndim=2] z = np.empty_like(x)
 *     cdef int n = x.shape[0]
 *     for i in range(n):             # <<<<<<<<<<<<<<
 *         z[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]
 *         z[i, 1] = x[i, 2] * y[i, 0] - x[i, 0] * y[i, 2]
 */
  __pyx_t_5 = __pyx_v_n;
  for (__pyx_t_6 = 0; __pyx_t_6 < __pyx_t_5; __pyx_t_6+=1) {
    __pyx_v_i = __pyx_t_6;
 18:         z[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":18
 *     cdef int n = x.shape[0]
 *     for i in range(n):
 *         z[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]             # <<<<<<<<<<<<<<
 *         z[i, 1] = x[i, 2] * y[i, 0] - x[i, 0] * y[i, 2]
 *         z[i, 2] = x[i, 0] * y[i, 1] - x[i, 1] * y[i, 0]
 */
    __pyx_t_7 = __pyx_v_i;
    __pyx_t_8 = 1;
    __pyx_t_9 = -1;
    if (__pyx_t_7 < 0) {
      __pyx_t_7 += __pyx_v_x.shape[0];
      if (unlikely(__pyx_t_7 < 0)) __pyx_t_9 = 0;
    } else if (unlikely(__pyx_t_7 >= __pyx_v_x.shape[0])) __pyx_t_9 = 0;
    if (__pyx_t_8 < 0) {
      __pyx_t_8 += __pyx_v_x.shape[1];
      if (unlikely(__pyx_t_8 < 0)) __pyx_t_9 = 1;
    } else if (unlikely(__pyx_t_8 >= __pyx_v_x.shape[1])) __pyx_t_9 = 1;
    if (unlikely(__pyx_t_9 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_9);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_9 = __pyx_v_i;
    __pyx_t_10 = 2;
    __pyx_t_11 = -1;
    if (__pyx_t_9 < 0) {
      __pyx_t_9 += __pyx_v_y.shape[0];
      if (unlikely(__pyx_t_9 < 0)) __pyx_t_11 = 0;
    } else if (unlikely(__pyx_t_9 >= __pyx_v_y.shape[0])) __pyx_t_11 = 0;
    if (__pyx_t_10 < 0) {
      __pyx_t_10 += __pyx_v_y.shape[1];
      if (unlikely(__pyx_t_10 < 0)) __pyx_t_11 = 1;
    } else if (unlikely(__pyx_t_10 >= __pyx_v_y.shape[1])) __pyx_t_11 = 1;
    if (unlikely(__pyx_t_11 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_11);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_11 = __pyx_v_i;
    __pyx_t_12 = 2;
    __pyx_t_13 = -1;
    if (__pyx_t_11 < 0) {
      __pyx_t_11 += __pyx_v_x.shape[0];
      if (unlikely(__pyx_t_11 < 0)) __pyx_t_13 = 0;
    } else if (unlikely(__pyx_t_11 >= __pyx_v_x.shape[0])) __pyx_t_13 = 0;
    if (__pyx_t_12 < 0) {
      __pyx_t_12 += __pyx_v_x.shape[1];
      if (unlikely(__pyx_t_12 < 0)) __pyx_t_13 = 1;
    } else if (unlikely(__pyx_t_12 >= __pyx_v_x.shape[1])) __pyx_t_13 = 1;
    if (unlikely(__pyx_t_13 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_13);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_13 = __pyx_v_i;
    __pyx_t_14 = 1;
    __pyx_t_15 = -1;
    if (__pyx_t_13 < 0) {
      __pyx_t_13 += __pyx_v_y.shape[0];
      if (unlikely(__pyx_t_13 < 0)) __pyx_t_15 = 0;
    } else if (unlikely(__pyx_t_13 >= __pyx_v_y.shape[0])) __pyx_t_15 = 0;
    if (__pyx_t_14 < 0) {
      __pyx_t_14 += __pyx_v_y.shape[1];
      if (unlikely(__pyx_t_14 < 0)) __pyx_t_15 = 1;
    } else if (unlikely(__pyx_t_14 >= __pyx_v_y.shape[1])) __pyx_t_15 = 1;
    if (unlikely(__pyx_t_15 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_15);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_15 = __pyx_v_i;
    __pyx_t_16 = 0;
    __pyx_t_17 = -1;
    if (__pyx_t_15 < 0) {
      __pyx_t_15 += __pyx_pybuffernd_z.diminfo[0].shape;
      if (unlikely(__pyx_t_15 < 0)) __pyx_t_17 = 0;
    } else if (unlikely(__pyx_t_15 >= __pyx_pybuffernd_z.diminfo[0].shape)) __pyx_t_17 = 0;
    if (__pyx_t_16 < 0) {
      __pyx_t_16 += __pyx_pybuffernd_z.diminfo[1].shape;
      if (unlikely(__pyx_t_16 < 0)) __pyx_t_17 = 1;
    } else if (unlikely(__pyx_t_16 >= __pyx_pybuffernd_z.diminfo[1].shape)) __pyx_t_17 = 1;
    if (unlikely(__pyx_t_17 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_17);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_z.rcbuffer->pybuffer.buf, __pyx_t_15, __pyx_pybuffernd_z.diminfo[0].strides, __pyx_t_16, __pyx_pybuffernd_z.diminfo[1].strides) = (((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_x.data + __pyx_t_7 * __pyx_v_x.strides[0]) ) + __pyx_t_8 * __pyx_v_x.strides[1]) ))) * (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_y.data + __pyx_t_9 * __pyx_v_y.strides[0]) ) + __pyx_t_10 * __pyx_v_y.strides[1]) )))) - ((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_x.data + __pyx_t_11 * __pyx_v_x.strides[0]) ) + __pyx_t_12 * __pyx_v_x.strides[1]) ))) * (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_y.data + __pyx_t_13 * __pyx_v_y.strides[0]) ) + __pyx_t_14 * __pyx_v_y.strides[1]) )))));
 19:         z[i, 1] = x[i, 2] * y[i, 0] - x[i, 0] * y[i, 2]
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":19
 *     for i in range(n):
 *         z[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]
 *         z[i, 1] = x[i, 2] * y[i, 0] - x[i, 0] * y[i, 2]             # <<<<<<<<<<<<<<
 *         z[i, 2] = x[i, 0] * y[i, 1] - x[i, 1] * y[i, 0]
 * 
 */
    __pyx_t_17 = __pyx_v_i;
    __pyx_t_18 = 2;
    __pyx_t_19 = -1;
    if (__pyx_t_17 < 0) {
      __pyx_t_17 += __pyx_v_x.shape[0];
      if (unlikely(__pyx_t_17 < 0)) __pyx_t_19 = 0;
    } else if (unlikely(__pyx_t_17 >= __pyx_v_x.shape[0])) __pyx_t_19 = 0;
    if (__pyx_t_18 < 0) {
      __pyx_t_18 += __pyx_v_x.shape[1];
      if (unlikely(__pyx_t_18 < 0)) __pyx_t_19 = 1;
    } else if (unlikely(__pyx_t_18 >= __pyx_v_x.shape[1])) __pyx_t_19 = 1;
    if (unlikely(__pyx_t_19 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_19);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_19 = __pyx_v_i;
    __pyx_t_20 = 0;
    __pyx_t_21 = -1;
    if (__pyx_t_19 < 0) {
      __pyx_t_19 += __pyx_v_y.shape[0];
      if (unlikely(__pyx_t_19 < 0)) __pyx_t_21 = 0;
    } else if (unlikely(__pyx_t_19 >= __pyx_v_y.shape[0])) __pyx_t_21 = 0;
    if (__pyx_t_20 < 0) {
      __pyx_t_20 += __pyx_v_y.shape[1];
      if (unlikely(__pyx_t_20 < 0)) __pyx_t_21 = 1;
    } else if (unlikely(__pyx_t_20 >= __pyx_v_y.shape[1])) __pyx_t_21 = 1;
    if (unlikely(__pyx_t_21 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_21);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_21 = __pyx_v_i;
    __pyx_t_22 = 0;
    __pyx_t_23 = -1;
    if (__pyx_t_21 < 0) {
      __pyx_t_21 += __pyx_v_x.shape[0];
      if (unlikely(__pyx_t_21 < 0)) __pyx_t_23 = 0;
    } else if (unlikely(__pyx_t_21 >= __pyx_v_x.shape[0])) __pyx_t_23 = 0;
    if (__pyx_t_22 < 0) {
      __pyx_t_22 += __pyx_v_x.shape[1];
      if (unlikely(__pyx_t_22 < 0)) __pyx_t_23 = 1;
    } else if (unlikely(__pyx_t_22 >= __pyx_v_x.shape[1])) __pyx_t_23 = 1;
    if (unlikely(__pyx_t_23 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_23);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_23 = __pyx_v_i;
    __pyx_t_24 = 2;
    __pyx_t_25 = -1;
    if (__pyx_t_23 < 0) {
      __pyx_t_23 += __pyx_v_y.shape[0];
      if (unlikely(__pyx_t_23 < 0)) __pyx_t_25 = 0;
    } else if (unlikely(__pyx_t_23 >= __pyx_v_y.shape[0])) __pyx_t_25 = 0;
    if (__pyx_t_24 < 0) {
      __pyx_t_24 += __pyx_v_y.shape[1];
      if (unlikely(__pyx_t_24 < 0)) __pyx_t_25 = 1;
    } else if (unlikely(__pyx_t_24 >= __pyx_v_y.shape[1])) __pyx_t_25 = 1;
    if (unlikely(__pyx_t_25 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_25);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_25 = __pyx_v_i;
    __pyx_t_26 = 1;
    __pyx_t_27 = -1;
    if (__pyx_t_25 < 0) {
      __pyx_t_25 += __pyx_pybuffernd_z.diminfo[0].shape;
      if (unlikely(__pyx_t_25 < 0)) __pyx_t_27 = 0;
    } else if (unlikely(__pyx_t_25 >= __pyx_pybuffernd_z.diminfo[0].shape)) __pyx_t_27 = 0;
    if (__pyx_t_26 < 0) {
      __pyx_t_26 += __pyx_pybuffernd_z.diminfo[1].shape;
      if (unlikely(__pyx_t_26 < 0)) __pyx_t_27 = 1;
    } else if (unlikely(__pyx_t_26 >= __pyx_pybuffernd_z.diminfo[1].shape)) __pyx_t_27 = 1;
    if (unlikely(__pyx_t_27 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_27);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 19; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_z.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_z.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_z.diminfo[1].strides) = (((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_x.data + __pyx_t_17 * __pyx_v_x.strides[0]) ) + __pyx_t_18 * __pyx_v_x.strides[1]) ))) * (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_y.data + __pyx_t_19 * __pyx_v_y.strides[0]) ) + __pyx_t_20 * __pyx_v_y.strides[1]) )))) - ((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_x.data + __pyx_t_21 * __pyx_v_x.strides[0]) ) + __pyx_t_22 * __pyx_v_x.strides[1]) ))) * (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_y.data + __pyx_t_23 * __pyx_v_y.strides[0]) ) + __pyx_t_24 * __pyx_v_y.strides[1]) )))));
 20:         z[i, 2] = x[i, 0] * y[i, 1] - x[i, 1] * y[i, 0]
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":20
 *         z[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]
 *         z[i, 1] = x[i, 2] * y[i, 0] - x[i, 0] * y[i, 2]
 *         z[i, 2] = x[i, 0] * y[i, 1] - x[i, 1] * y[i, 0]             # <<<<<<<<<<<<<<
 * 
 *     return z
 */
    __pyx_t_27 = __pyx_v_i;
    __pyx_t_28 = 0;
    __pyx_t_29 = -1;
    if (__pyx_t_27 < 0) {
      __pyx_t_27 += __pyx_v_x.shape[0];
      if (unlikely(__pyx_t_27 < 0)) __pyx_t_29 = 0;
    } else if (unlikely(__pyx_t_27 >= __pyx_v_x.shape[0])) __pyx_t_29 = 0;
    if (__pyx_t_28 < 0) {
      __pyx_t_28 += __pyx_v_x.shape[1];
      if (unlikely(__pyx_t_28 < 0)) __pyx_t_29 = 1;
    } else if (unlikely(__pyx_t_28 >= __pyx_v_x.shape[1])) __pyx_t_29 = 1;
    if (unlikely(__pyx_t_29 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_29);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_29 = __pyx_v_i;
    __pyx_t_30 = 1;
    __pyx_t_31 = -1;
    if (__pyx_t_29 < 0) {
      __pyx_t_29 += __pyx_v_y.shape[0];
      if (unlikely(__pyx_t_29 < 0)) __pyx_t_31 = 0;
    } else if (unlikely(__pyx_t_29 >= __pyx_v_y.shape[0])) __pyx_t_31 = 0;
    if (__pyx_t_30 < 0) {
      __pyx_t_30 += __pyx_v_y.shape[1];
      if (unlikely(__pyx_t_30 < 0)) __pyx_t_31 = 1;
    } else if (unlikely(__pyx_t_30 >= __pyx_v_y.shape[1])) __pyx_t_31 = 1;
    if (unlikely(__pyx_t_31 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_31);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_31 = __pyx_v_i;
    __pyx_t_32 = 1;
    __pyx_t_33 = -1;
    if (__pyx_t_31 < 0) {
      __pyx_t_31 += __pyx_v_x.shape[0];
      if (unlikely(__pyx_t_31 < 0)) __pyx_t_33 = 0;
    } else if (unlikely(__pyx_t_31 >= __pyx_v_x.shape[0])) __pyx_t_33 = 0;
    if (__pyx_t_32 < 0) {
      __pyx_t_32 += __pyx_v_x.shape[1];
      if (unlikely(__pyx_t_32 < 0)) __pyx_t_33 = 1;
    } else if (unlikely(__pyx_t_32 >= __pyx_v_x.shape[1])) __pyx_t_33 = 1;
    if (unlikely(__pyx_t_33 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_33);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_33 = __pyx_v_i;
    __pyx_t_34 = 0;
    __pyx_t_35 = -1;
    if (__pyx_t_33 < 0) {
      __pyx_t_33 += __pyx_v_y.shape[0];
      if (unlikely(__pyx_t_33 < 0)) __pyx_t_35 = 0;
    } else if (unlikely(__pyx_t_33 >= __pyx_v_y.shape[0])) __pyx_t_35 = 0;
    if (__pyx_t_34 < 0) {
      __pyx_t_34 += __pyx_v_y.shape[1];
      if (unlikely(__pyx_t_34 < 0)) __pyx_t_35 = 1;
    } else if (unlikely(__pyx_t_34 >= __pyx_v_y.shape[1])) __pyx_t_35 = 1;
    if (unlikely(__pyx_t_35 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_35);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_35 = __pyx_v_i;
    __pyx_t_36 = 2;
    __pyx_t_37 = -1;
    if (__pyx_t_35 < 0) {
      __pyx_t_35 += __pyx_pybuffernd_z.diminfo[0].shape;
      if (unlikely(__pyx_t_35 < 0)) __pyx_t_37 = 0;
    } else if (unlikely(__pyx_t_35 >= __pyx_pybuffernd_z.diminfo[0].shape)) __pyx_t_37 = 0;
    if (__pyx_t_36 < 0) {
      __pyx_t_36 += __pyx_pybuffernd_z.diminfo[1].shape;
      if (unlikely(__pyx_t_36 < 0)) __pyx_t_37 = 1;
    } else if (unlikely(__pyx_t_36 >= __pyx_pybuffernd_z.diminfo[1].shape)) __pyx_t_37 = 1;
    if (unlikely(__pyx_t_37 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_37);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 20; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_z.rcbuffer->pybuffer.buf, __pyx_t_35, __pyx_pybuffernd_z.diminfo[0].strides, __pyx_t_36, __pyx_pybuffernd_z.diminfo[1].strides) = (((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_x.data + __pyx_t_27 * __pyx_v_x.strides[0]) ) + __pyx_t_28 * __pyx_v_x.strides[1]) ))) * (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_y.data + __pyx_t_29 * __pyx_v_y.strides[0]) ) + __pyx_t_30 * __pyx_v_y.strides[1]) )))) - ((*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_x.data + __pyx_t_31 * __pyx_v_x.strides[0]) ) + __pyx_t_32 * __pyx_v_x.strides[1]) ))) * (*((double *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_y.data + __pyx_t_33 * __pyx_v_y.strides[0]) ) + __pyx_t_34 * __pyx_v_y.strides[1]) )))));
  }
 21: 
 22:     return z
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":22
 *         z[i, 2] = x[i, 0] * y[i, 1] - x[i, 1] * y[i, 0]
 * 
 *     return z             # <<<<<<<<<<<<<<
 * 
 * 
 */
  __Pyx_XDECREF(((PyObject *)__pyx_r));
  __Pyx_INCREF(((PyObject *)__pyx_v_z));
  __pyx_r = ((PyArrayObject *)__pyx_v_z);
  goto __pyx_L0;

  __pyx_r = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_z.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_637c713b986c525e98b048fef3dc5c09.cross", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_z.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_z);
  __Pyx_XGIVEREF((PyObject *)__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 23: 
 24: 
 25: cpdef cy_compute_normal(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
/* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":25
 * 
 * 
 * cpdef cy_compute_normal(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */

static PyObject *__pyx_pw_46_cython_magic_637c713b986c525e98b048fef3dc5c09_1cy_compute_normal(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_f_46_cython_magic_637c713b986c525e98b048fef3dc5c09_cy_compute_normal(PyArrayObject *__pyx_v_vertex, PyArrayObject *__pyx_v_face, CYTHON_UNUSED int __pyx_skip_dispatch) {
  int __pyx_v_nface;
  int __pyx_v_nvert;
  PyArrayObject *__pyx_v_normalf = 0;
  PyArrayObject *__pyx_v_normal = 0;
  int __pyx_v_f0;
  int __pyx_v_f1;
  int __pyx_v_f2;
  int __pyx_v_i;
  long __pyx_v_j;
  PyArrayObject *__pyx_v_v = 0;
  PyArrayObject *__pyx_v_s = 0;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_face;
  __Pyx_Buffer __pyx_pybuffer_face;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_normal;
  __Pyx_Buffer __pyx_pybuffer_normal;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_normalf;
  __Pyx_Buffer __pyx_pybuffer_normalf;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_s;
  __Pyx_Buffer __pyx_pybuffer_s;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_v;
  __Pyx_Buffer __pyx_pybuffer_v;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vertex;
  __Pyx_Buffer __pyx_pybuffer_vertex;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal", 0);
  __pyx_pybuffer_normalf.pybuffer.buf = NULL;
  __pyx_pybuffer_normalf.refcount = 0;
  __pyx_pybuffernd_normalf.data = NULL;
  __pyx_pybuffernd_normalf.rcbuffer = &__pyx_pybuffer_normalf;
  __pyx_pybuffer_normal.pybuffer.buf = NULL;
  __pyx_pybuffer_normal.refcount = 0;
  __pyx_pybuffernd_normal.data = NULL;
  __pyx_pybuffernd_normal.rcbuffer = &__pyx_pybuffer_normal;
  __pyx_pybuffer_v.pybuffer.buf = NULL;
  __pyx_pybuffer_v.refcount = 0;
  __pyx_pybuffernd_v.data = NULL;
  __pyx_pybuffernd_v.rcbuffer = &__pyx_pybuffer_v;
  __pyx_pybuffer_s.pybuffer.buf = NULL;
  __pyx_pybuffer_s.refcount = 0;
  __pyx_pybuffernd_s.data = NULL;
  __pyx_pybuffernd_s.rcbuffer = &__pyx_pybuffer_s;
  __pyx_pybuffer_vertex.pybuffer.buf = NULL;
  __pyx_pybuffer_vertex.refcount = 0;
  __pyx_pybuffernd_vertex.data = NULL;
  __pyx_pybuffernd_vertex.rcbuffer = &__pyx_pybuffer_vertex;
  __pyx_pybuffer_face.pybuffer.buf = NULL;
  __pyx_pybuffer_face.refcount = 0;
  __pyx_pybuffernd_face.data = NULL;
  __pyx_pybuffernd_face.rcbuffer = &__pyx_pybuffer_face;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer, (PyObject*)__pyx_v_vertex, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vertex.diminfo[0].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vertex.diminfo[0].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vertex.diminfo[1].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vertex.diminfo[1].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[1];
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_face.rcbuffer->pybuffer, (PyObject*)__pyx_v_face, &__Pyx_TypeInfo_int, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_face.diminfo[0].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_face.diminfo[0].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_face.diminfo[1].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_face.diminfo[1].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[1];

/* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":25
 * 
 * 
 * cpdef cy_compute_normal(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):             # <<<<<<<<<<<<<<
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]
 */

static PyObject *__pyx_pf_46_cython_magic_637c713b986c525e98b048fef3dc5c09_cy_compute_normal(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_vertex, PyArrayObject *__pyx_v_face) {
  __Pyx_LocalBuf_ND __pyx_pybuffernd_face;
  __Pyx_Buffer __pyx_pybuffer_face;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_vertex;
  __Pyx_Buffer __pyx_pybuffer_vertex;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal", 0);
  __pyx_pybuffer_vertex.pybuffer.buf = NULL;
  __pyx_pybuffer_vertex.refcount = 0;
  __pyx_pybuffernd_vertex.data = NULL;
  __pyx_pybuffernd_vertex.rcbuffer = &__pyx_pybuffer_vertex;
  __pyx_pybuffer_face.pybuffer.buf = NULL;
  __pyx_pybuffer_face.refcount = 0;
  __pyx_pybuffernd_face.data = NULL;
  __pyx_pybuffernd_face.rcbuffer = &__pyx_pybuffer_face;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer, (PyObject*)__pyx_v_vertex, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_vertex.diminfo[0].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_vertex.diminfo[0].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_vertex.diminfo[1].strides = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_vertex.diminfo[1].shape = __pyx_pybuffernd_vertex.rcbuffer->pybuffer.shape[1];
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_face.rcbuffer->pybuffer, (PyObject*)__pyx_v_face, &__Pyx_TypeInfo_int, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_pybuffernd_face.diminfo[0].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_face.diminfo[0].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_face.diminfo[1].strides = __pyx_pybuffernd_face.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_face.diminfo[1].shape = __pyx_pybuffernd_face.rcbuffer->pybuffer.shape[1];
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_1 = __pyx_f_46_cython_magic_637c713b986c525e98b048fef3dc5c09_cy_compute_normal(__pyx_v_vertex, __pyx_v_face, 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_r = __pyx_t_1;
  __pyx_t_1 = 0;
  goto __pyx_L0;

  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_637c713b986c525e98b048fef3dc5c09.cy_compute_normal", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static CYTHON_UNUSED int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags); /*proto*/
static CYTHON_UNUSED int __pyx_pw_5numpy_7ndarray_1__getbuffer__(PyObject *__pyx_v_self, Py_buffer *__pyx_v_info, int __pyx_v_flags) {
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("__getbuffer__ (wrapper)", 0);
  __pyx_r = __pyx_pf_5numpy_7ndarray___getbuffer__(((PyArrayObject *)__pyx_v_self), ((Py_buffer *)__pyx_v_info), ((int)__pyx_v_flags));
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 26:     cdef int nface = face.shape[0]
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":26
 * 
 * cpdef cy_compute_normal(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
 *     cdef int nface = face.shape[0]             # <<<<<<<<<<<<<<
 *     cdef int nvert = vertex.shape[0]
 * 
 */
  __pyx_v_nface = (__pyx_v_face->dimensions[0]);
 27:     cdef int nvert = vertex.shape[0]
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":27
 * cpdef cy_compute_normal(np.ndarray[np.float64_t, ndim=2] vertex, np.ndarray[int, ndim=2] face):
 *     cdef int nface = face.shape[0]
 *     cdef int nvert = vertex.shape[0]             # <<<<<<<<<<<<<<
 * 
 *     # unit normals to the faces
 */
  __pyx_v_nvert = (__pyx_v_vertex->dimensions[0]);
 28: 
 29:     # unit normals to the faces
 30:     cdef np.ndarray[np.float64_t, ndim=2] normalf = cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":30
 * 
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],             # <<<<<<<<<<<<<<
 *                                                           vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = normalise(normalf)
 */
  __pyx_k_slice_3 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_3);
  __Pyx_GIVEREF(__pyx_k_slice_3);

  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":30
 * 
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],             # <<<<<<<<<<<<<<
 *                                                           vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = normalise(normalf)
 */
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_4)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_k_tuple_4 = PyTuple_Pack(2, __pyx_k_slice_3, __pyx_int_1); if (unlikely(!__pyx_k_tuple_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_4);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_4));
  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __Pyx_INCREF(__pyx_k_slice_5);
  PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_k_slice_5);
  __Pyx_GIVEREF(__pyx_k_slice_5);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_2)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  __pyx_k_slice_5 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_5);
  __Pyx_GIVEREF(__pyx_k_slice_5);
  __pyx_k_slice_6 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_6)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_6);
  __Pyx_GIVEREF(__pyx_k_slice_6);
  __pyx_t_2 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_7)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_k_tuple_7 = PyTuple_Pack(2, __pyx_k_slice_6, __pyx_int_0); if (unlikely(!__pyx_k_tuple_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_7);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_7));
  __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_2);
  __Pyx_INCREF(__pyx_k_slice_8);
  PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_k_slice_8);
  __Pyx_GIVEREF(__pyx_k_slice_8);
  __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_3)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0;
  __pyx_t_3 = PyNumber_Subtract(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_4 = __Pyx_PyObject_to_MemoryviewSlice_dsds_double(__pyx_t_3);
  if (unlikely(!__pyx_t_4.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_k_slice_8 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_8);
  __Pyx_GIVEREF(__pyx_k_slice_8);
 31:                                                           vertex[face[:, 2], :] - vertex[face[:, 0], :])
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":31
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                           vertex[face[:, 2], :] - vertex[face[:, 0], :])             # <<<<<<<<<<<<<<
 *     normalf = normalise(normalf)
 * 
 */
  __pyx_k_slice_9 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_9);
  __Pyx_GIVEREF(__pyx_k_slice_9);

  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":31
 *     # unit normals to the faces
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                           vertex[face[:, 2], :] - vertex[face[:, 0], :])             # <<<<<<<<<<<<<<
 *     normalf = normalise(normalf)
 * 
 */
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_10)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_k_tuple_10 = PyTuple_Pack(2, __pyx_k_slice_9, __pyx_int_2); if (unlikely(!__pyx_k_tuple_10)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_10);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_10));
  __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __Pyx_INCREF(__pyx_k_slice_11);
  PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_k_slice_11);
  __Pyx_GIVEREF(__pyx_k_slice_11);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_2)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  __pyx_k_slice_11 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_11);
  __Pyx_GIVEREF(__pyx_k_slice_11);
  __pyx_k_slice_12 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_12);
  __Pyx_GIVEREF(__pyx_k_slice_12);
  __pyx_t_2 = PyObject_GetItem(((PyObject *)__pyx_v_face), ((PyObject *)__pyx_k_tuple_13)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_k_tuple_13 = PyTuple_Pack(2, __pyx_k_slice_12, __pyx_int_0); if (unlikely(!__pyx_k_tuple_13)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_13);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_13));
  __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_2);
  __Pyx_INCREF(__pyx_k_slice_14);
  PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_k_slice_14);
  __Pyx_GIVEREF(__pyx_k_slice_14);
  __pyx_t_2 = 0;
  __pyx_t_2 = PyObject_GetItem(((PyObject *)__pyx_v_vertex), ((PyObject *)__pyx_t_1)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __pyx_t_1 = PyNumber_Subtract(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_5 = __Pyx_PyObject_to_MemoryviewSlice_dsds_double(__pyx_t_1);
  if (unlikely(!__pyx_t_5.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = ((PyObject *)__pyx_f_46_cython_magic_637c713b986c525e98b048fef3dc5c09_cross(__pyx_t_4, __pyx_t_5)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __PYX_XDEC_MEMVIEW(&__pyx_t_4, 1);
  __PYX_XDEC_MEMVIEW(&__pyx_t_5, 1);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)((PyArrayObject *)__pyx_t_1), &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_normalf = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_v_normalf = ((PyArrayObject *)__pyx_t_1);
  __pyx_t_1 = 0;
  __pyx_k_slice_14 = PySlice_New(Py_None, Py_None, Py_None); if (unlikely(!__pyx_k_slice_14)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_slice_14);
  __Pyx_GIVEREF(__pyx_k_slice_14);
 32:     normalf = normalise(normalf)
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":32
 *     cdef np.ndarray[np.float64_t, ndim=2] normalf = cross(vertex[face[:, 1], :] - vertex[face[:, 0], :],
 *                                                           vertex[face[:, 2], :] - vertex[face[:, 0], :])
 *     normalf = normalise(normalf)             # <<<<<<<<<<<<<<
 * 
 *     # unit normal to the vertex
 */
  __pyx_t_1 = ((PyObject *)__pyx_f_46_cython_magic_637c713b986c525e98b048fef3dc5c09_normalise(((PyArrayObject *)__pyx_v_normalf))); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
    __pyx_t_6 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)((PyArrayObject *)__pyx_t_1), &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
    if (unlikely(__pyx_t_6 < 0)) {
      PyErr_Fetch(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9);
      if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_v_normalf, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
        Py_XDECREF(__pyx_t_7); Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_9);
        __Pyx_RaiseBufferFallbackError();
      } else {
        PyErr_Restore(__pyx_t_7, __pyx_t_8, __pyx_t_9);
      }
    }
    __pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
    if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __Pyx_DECREF(((PyObject *)__pyx_v_normalf));
  __pyx_v_normalf = ((PyArrayObject *)__pyx_t_1);
  __pyx_t_1 = 0;
 33: 
 34:     # unit normal to the vertex
 35:     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":35
 * 
 *     # unit normal to the vertex
 *     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])             # <<<<<<<<<<<<<<
 *     cdef int f0, f1, f2
 *     for i in range(nface):
 */
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s__zeros); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = PyInt_FromLong(__pyx_v_nvert); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_3 = PyList_New(2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  PyList_SET_ITEM(__pyx_t_3, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_1);
  __Pyx_INCREF(__pyx_int_3);
  PyList_SET_ITEM(__pyx_t_3, 1, __pyx_int_3);
  __Pyx_GIVEREF(__pyx_int_3);
  __pyx_t_1 = 0;
  __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, ((PyObject *)__pyx_t_3));
  __Pyx_GIVEREF(((PyObject *)__pyx_t_3));
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_3) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_3, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_10 = ((PyArrayObject *)__pyx_t_3);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_t_10, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_normal = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 35; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_10 = 0;
  __pyx_v_normal = ((PyArrayObject *)__pyx_t_3);
  __pyx_t_3 = 0;
 36:     cdef int f0, f1, f2
 37:     for i in range(nface):
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":37
 *     cdef np.ndarray[np.float64_t, ndim=2] normal = np.zeros([nvert, 3])
 *     cdef int f0, f1, f2
 *     for i in range(nface):             # <<<<<<<<<<<<<<
 *         f0 = face[i, 0]
 *         f1 = face[i, 1]
 */
  __pyx_t_6 = __pyx_v_nface;
  for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_6; __pyx_t_11+=1) {
    __pyx_v_i = __pyx_t_11;
 38:         f0 = face[i, 0]
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":38
 *     cdef int f0, f1, f2
 *     for i in range(nface):
 *         f0 = face[i, 0]             # <<<<<<<<<<<<<<
 *         f1 = face[i, 1]
 *         f2 = face[i, 2]
 */
    __pyx_t_12 = __pyx_v_i;
    __pyx_t_13 = 0;
    __pyx_t_14 = -1;
    if (__pyx_t_12 < 0) {
      __pyx_t_12 += __pyx_pybuffernd_face.diminfo[0].shape;
      if (unlikely(__pyx_t_12 < 0)) __pyx_t_14 = 0;
    } else if (unlikely(__pyx_t_12 >= __pyx_pybuffernd_face.diminfo[0].shape)) __pyx_t_14 = 0;
    if (__pyx_t_13 < 0) {
      __pyx_t_13 += __pyx_pybuffernd_face.diminfo[1].shape;
      if (unlikely(__pyx_t_13 < 0)) __pyx_t_14 = 1;
    } else if (unlikely(__pyx_t_13 >= __pyx_pybuffernd_face.diminfo[1].shape)) __pyx_t_14 = 1;
    if (unlikely(__pyx_t_14 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_14);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 38; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_v_f0 = (*__Pyx_BufPtrStrided2d(int *, __pyx_pybuffernd_face.rcbuffer->pybuffer.buf, __pyx_t_12, __pyx_pybuffernd_face.diminfo[0].strides, __pyx_t_13, __pyx_pybuffernd_face.diminfo[1].strides));
 39:         f1 = face[i, 1]
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":39
 *     for i in range(nface):
 *         f0 = face[i, 0]
 *         f1 = face[i, 1]             # <<<<<<<<<<<<<<
 *         f2 = face[i, 2]
 *         for j in range(3):
 */
    __pyx_t_14 = __pyx_v_i;
    __pyx_t_15 = 1;
    __pyx_t_16 = -1;
    if (__pyx_t_14 < 0) {
      __pyx_t_14 += __pyx_pybuffernd_face.diminfo[0].shape;
      if (unlikely(__pyx_t_14 < 0)) __pyx_t_16 = 0;
    } else if (unlikely(__pyx_t_14 >= __pyx_pybuffernd_face.diminfo[0].shape)) __pyx_t_16 = 0;
    if (__pyx_t_15 < 0) {
      __pyx_t_15 += __pyx_pybuffernd_face.diminfo[1].shape;
      if (unlikely(__pyx_t_15 < 0)) __pyx_t_16 = 1;
    } else if (unlikely(__pyx_t_15 >= __pyx_pybuffernd_face.diminfo[1].shape)) __pyx_t_16 = 1;
    if (unlikely(__pyx_t_16 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_16);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 39; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_v_f1 = (*__Pyx_BufPtrStrided2d(int *, __pyx_pybuffernd_face.rcbuffer->pybuffer.buf, __pyx_t_14, __pyx_pybuffernd_face.diminfo[0].strides, __pyx_t_15, __pyx_pybuffernd_face.diminfo[1].strides));
 40:         f2 = face[i, 2]
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":40
 *         f0 = face[i, 0]
 *         f1 = face[i, 1]
 *         f2 = face[i, 2]             # <<<<<<<<<<<<<<
 *         for j in range(3):
 *             normal[f0, j] += normalf[i, j]
 */
    __pyx_t_16 = __pyx_v_i;
    __pyx_t_17 = 2;
    __pyx_t_18 = -1;
    if (__pyx_t_16 < 0) {
      __pyx_t_16 += __pyx_pybuffernd_face.diminfo[0].shape;
      if (unlikely(__pyx_t_16 < 0)) __pyx_t_18 = 0;
    } else if (unlikely(__pyx_t_16 >= __pyx_pybuffernd_face.diminfo[0].shape)) __pyx_t_18 = 0;
    if (__pyx_t_17 < 0) {
      __pyx_t_17 += __pyx_pybuffernd_face.diminfo[1].shape;
      if (unlikely(__pyx_t_17 < 0)) __pyx_t_18 = 1;
    } else if (unlikely(__pyx_t_17 >= __pyx_pybuffernd_face.diminfo[1].shape)) __pyx_t_18 = 1;
    if (unlikely(__pyx_t_18 != -1)) {
      __Pyx_RaiseBufferIndexError(__pyx_t_18);
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_v_f2 = (*__Pyx_BufPtrStrided2d(int *, __pyx_pybuffernd_face.rcbuffer->pybuffer.buf, __pyx_t_16, __pyx_pybuffernd_face.diminfo[0].strides, __pyx_t_17, __pyx_pybuffernd_face.diminfo[1].strides));
 41:         for j in range(3):
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":41
 *         f1 = face[i, 1]
 *         f2 = face[i, 2]
 *         for j in range(3):             # <<<<<<<<<<<<<<
 *             normal[f0, j] += normalf[i, j]
 *             normal[f1, j] += normalf[i, j]
 */
    for (__pyx_t_19 = 0; __pyx_t_19 < 3; __pyx_t_19+=1) {
      __pyx_v_j = __pyx_t_19;
 42:             normal[f0, j] += normalf[i, j]
      /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":42
 *         f2 = face[i, 2]
 *         for j in range(3):
 *             normal[f0, j] += normalf[i, j]             # <<<<<<<<<<<<<<
 *             normal[f1, j] += normalf[i, j]
 *             normal[f2, j] += normalf[i, j]
 */
      __pyx_t_18 = __pyx_v_i;
      __pyx_t_20 = __pyx_v_j;
      __pyx_t_21 = -1;
      if (__pyx_t_18 < 0) {
        __pyx_t_18 += __pyx_pybuffernd_normalf.diminfo[0].shape;
        if (unlikely(__pyx_t_18 < 0)) __pyx_t_21 = 0;
      } else if (unlikely(__pyx_t_18 >= __pyx_pybuffernd_normalf.diminfo[0].shape)) __pyx_t_21 = 0;
      if (__pyx_t_20 < 0) {
        __pyx_t_20 += __pyx_pybuffernd_normalf.diminfo[1].shape;
        if (unlikely(__pyx_t_20 < 0)) __pyx_t_21 = 1;
      } else if (unlikely(__pyx_t_20 >= __pyx_pybuffernd_normalf.diminfo[1].shape)) __pyx_t_21 = 1;
      if (unlikely(__pyx_t_21 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_21);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 42; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      __pyx_t_21 = __pyx_v_f0;
      __pyx_t_22 = __pyx_v_j;
      __pyx_t_23 = -1;
      if (__pyx_t_21 < 0) {
        __pyx_t_21 += __pyx_pybuffernd_normal.diminfo[0].shape;
        if (unlikely(__pyx_t_21 < 0)) __pyx_t_23 = 0;
      } else if (unlikely(__pyx_t_21 >= __pyx_pybuffernd_normal.diminfo[0].shape)) __pyx_t_23 = 0;
      if (__pyx_t_22 < 0) {
        __pyx_t_22 += __pyx_pybuffernd_normal.diminfo[1].shape;
        if (unlikely(__pyx_t_22 < 0)) __pyx_t_23 = 1;
      } else if (unlikely(__pyx_t_22 >= __pyx_pybuffernd_normal.diminfo[1].shape)) __pyx_t_23 = 1;
      if (unlikely(__pyx_t_23 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_23);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 42; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf, __pyx_t_21, __pyx_pybuffernd_normal.diminfo[0].strides, __pyx_t_22, __pyx_pybuffernd_normal.diminfo[1].strides) += (*__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf, __pyx_t_18, __pyx_pybuffernd_normalf.diminfo[0].strides, __pyx_t_20, __pyx_pybuffernd_normalf.diminfo[1].strides));
 43:             normal[f1, j] += normalf[i, j]
      /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":43
 *         for j in range(3):
 *             normal[f0, j] += normalf[i, j]
 *             normal[f1, j] += normalf[i, j]             # <<<<<<<<<<<<<<
 *             normal[f2, j] += normalf[i, j]
 * 
 */
      __pyx_t_23 = __pyx_v_i;
      __pyx_t_24 = __pyx_v_j;
      __pyx_t_25 = -1;
      if (__pyx_t_23 < 0) {
        __pyx_t_23 += __pyx_pybuffernd_normalf.diminfo[0].shape;
        if (unlikely(__pyx_t_23 < 0)) __pyx_t_25 = 0;
      } else if (unlikely(__pyx_t_23 >= __pyx_pybuffernd_normalf.diminfo[0].shape)) __pyx_t_25 = 0;
      if (__pyx_t_24 < 0) {
        __pyx_t_24 += __pyx_pybuffernd_normalf.diminfo[1].shape;
        if (unlikely(__pyx_t_24 < 0)) __pyx_t_25 = 1;
      } else if (unlikely(__pyx_t_24 >= __pyx_pybuffernd_normalf.diminfo[1].shape)) __pyx_t_25 = 1;
      if (unlikely(__pyx_t_25 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_25);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 43; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      __pyx_t_25 = __pyx_v_f1;
      __pyx_t_26 = __pyx_v_j;
      __pyx_t_27 = -1;
      if (__pyx_t_25 < 0) {
        __pyx_t_25 += __pyx_pybuffernd_normal.diminfo[0].shape;
        if (unlikely(__pyx_t_25 < 0)) __pyx_t_27 = 0;
      } else if (unlikely(__pyx_t_25 >= __pyx_pybuffernd_normal.diminfo[0].shape)) __pyx_t_27 = 0;
      if (__pyx_t_26 < 0) {
        __pyx_t_26 += __pyx_pybuffernd_normal.diminfo[1].shape;
        if (unlikely(__pyx_t_26 < 0)) __pyx_t_27 = 1;
      } else if (unlikely(__pyx_t_26 >= __pyx_pybuffernd_normal.diminfo[1].shape)) __pyx_t_27 = 1;
      if (unlikely(__pyx_t_27 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_27);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 43; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_normal.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_normal.diminfo[1].strides) += (*__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf, __pyx_t_23, __pyx_pybuffernd_normalf.diminfo[0].strides, __pyx_t_24, __pyx_pybuffernd_normalf.diminfo[1].strides));
 44:             normal[f2, j] += normalf[i, j]
      /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":44
 *             normal[f0, j] += normalf[i, j]
 *             normal[f1, j] += normalf[i, j]
 *             normal[f2, j] += normalf[i, j]             # <<<<<<<<<<<<<<
 * 
 *     # normalize
 */
      __pyx_t_27 = __pyx_v_i;
      __pyx_t_28 = __pyx_v_j;
      __pyx_t_29 = -1;
      if (__pyx_t_27 < 0) {
        __pyx_t_27 += __pyx_pybuffernd_normalf.diminfo[0].shape;
        if (unlikely(__pyx_t_27 < 0)) __pyx_t_29 = 0;
      } else if (unlikely(__pyx_t_27 >= __pyx_pybuffernd_normalf.diminfo[0].shape)) __pyx_t_29 = 0;
      if (__pyx_t_28 < 0) {
        __pyx_t_28 += __pyx_pybuffernd_normalf.diminfo[1].shape;
        if (unlikely(__pyx_t_28 < 0)) __pyx_t_29 = 1;
      } else if (unlikely(__pyx_t_28 >= __pyx_pybuffernd_normalf.diminfo[1].shape)) __pyx_t_29 = 1;
      if (unlikely(__pyx_t_29 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_29);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      __pyx_t_29 = __pyx_v_f2;
      __pyx_t_30 = __pyx_v_j;
      __pyx_t_31 = -1;
      if (__pyx_t_29 < 0) {
        __pyx_t_29 += __pyx_pybuffernd_normal.diminfo[0].shape;
        if (unlikely(__pyx_t_29 < 0)) __pyx_t_31 = 0;
      } else if (unlikely(__pyx_t_29 >= __pyx_pybuffernd_normal.diminfo[0].shape)) __pyx_t_31 = 0;
      if (__pyx_t_30 < 0) {
        __pyx_t_30 += __pyx_pybuffernd_normal.diminfo[1].shape;
        if (unlikely(__pyx_t_30 < 0)) __pyx_t_31 = 1;
      } else if (unlikely(__pyx_t_30 >= __pyx_pybuffernd_normal.diminfo[1].shape)) __pyx_t_31 = 1;
      if (unlikely(__pyx_t_31 != -1)) {
        __Pyx_RaiseBufferIndexError(__pyx_t_31);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 44; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
      *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normal.rcbuffer->pybuffer.buf, __pyx_t_29, __pyx_pybuffernd_normal.diminfo[0].strides, __pyx_t_30, __pyx_pybuffernd_normal.diminfo[1].strides) += (*__Pyx_BufPtrStrided2d(__pyx_t_5numpy_float64_t *, __pyx_pybuffernd_normalf.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_normalf.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_normalf.diminfo[1].strides));
    }
  }
 45: 
 46:     # normalize
 47:     normal = normalise(normal)
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":47
 * 
 *     # normalize
 *     normal = normalise(normal)             # <<<<<<<<<<<<<<
 * 
 *     # enforce that the normal are outward
 */
  __pyx_t_3 = ((PyObject *)__pyx_f_46_cython_magic_637c713b986c525e98b048fef3dc5c09_normalise(((PyArrayObject *)__pyx_v_normal))); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 47; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
    __pyx_t_6 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)((PyArrayObject *)__pyx_t_3), &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack);
    if (unlikely(__pyx_t_6 < 0)) {
      PyErr_Fetch(&__pyx_t_9, &__pyx_t_8, &__pyx_t_7);
      if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_v_normal, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
        Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_7);
        __Pyx_RaiseBufferFallbackError();
      } else {
        PyErr_Restore(__pyx_t_9, __pyx_t_8, __pyx_t_7);
      }
    }
    __pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
    if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 47; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __Pyx_DECREF(((PyObject *)__pyx_v_normal));
  __pyx_v_normal = ((PyArrayObject *)__pyx_t_3);
  __pyx_t_3 = 0;
 48: 
 49:     # enforce that the normal are outward
 50:     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":50
 * 
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]             # <<<<<<<<<<<<<<
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 */
  __pyx_t_3 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s__mean); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_INCREF(((PyObject *)__pyx_v_vertex));
  PyTuple_SET_ITEM(__pyx_t_3, 0, ((PyObject *)__pyx_v_vertex));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_vertex));
  __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_3), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_GetItem(__pyx_t_2, ((PyObject *)__pyx_k_tuple_15)); if (!__pyx_t_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyNumber_Subtract(((PyObject *)__pyx_v_vertex), __pyx_t_3); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_32 = ((PyArrayObject *)__pyx_t_2);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_v.rcbuffer->pybuffer, (PyObject*)__pyx_t_32, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
      __pyx_v_v = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_v.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_v.diminfo[0].strides = __pyx_pybuffernd_v.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_v.diminfo[0].shape = __pyx_pybuffernd_v.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_v.diminfo[1].strides = __pyx_pybuffernd_v.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_v.diminfo[1].shape = __pyx_pybuffernd_v.rcbuffer->pybuffer.shape[1];
    }
  }
  __pyx_t_32 = 0;
  __pyx_v_v = ((PyArrayObject *)__pyx_t_2);
  __pyx_t_2 = 0;

  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":50
 * 
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]             # <<<<<<<<<<<<<<
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 */
  __pyx_k_tuple_15 = PyTuple_Pack(2, Py_Ellipsis, Py_None); if (unlikely(!__pyx_k_tuple_15)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_k_tuple_15);
  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_15));
 51:     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":51
 *     # enforce that the normal are outward
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)             # <<<<<<<<<<<<<<
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 *         # flip
 */
  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s__sum); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = PyNumber_Multiply(((PyObject *)__pyx_v_v), ((PyObject *)__pyx_v_normal)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_2);
  __pyx_t_2 = 0;
  __pyx_t_2 = PyDict_New(); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(((PyObject *)__pyx_t_2));
  if (PyDict_SetItem(__pyx_t_2, ((PyObject *)__pyx_n_s__axis), __pyx_int_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_33 = PyObject_Call(__pyx_t_3, ((PyObject *)__pyx_t_1), ((PyObject *)__pyx_t_2)); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
  if (!(likely(((__pyx_t_33) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_33, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_34 = ((PyArrayObject *)__pyx_t_33);
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_s.rcbuffer->pybuffer, (PyObject*)__pyx_t_34, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 1, 0, __pyx_stack) == -1)) {
      __pyx_v_s = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_s.rcbuffer->pybuffer.buf = NULL;
      {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    } else {__pyx_pybuffernd_s.diminfo[0].strides = __pyx_pybuffernd_s.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_s.diminfo[0].shape = __pyx_pybuffernd_s.rcbuffer->pybuffer.shape[0];
    }
  }
  __pyx_t_34 = 0;
  __pyx_v_s = ((PyArrayObject *)__pyx_t_33);
  __pyx_t_33 = 0;
 52:     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":52
 *     cdef np.ndarray[np.float64_t, ndim=2] v = vertex - np.mean(vertex)[..., None]
 *     cdef np.ndarray[np.float64_t, ndim=1] s = np.sum(v * normal, axis=1)
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):             # <<<<<<<<<<<<<<
 *         # flip
 *         normal = -normal
 */
  __pyx_t_33 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_33, __pyx_n_s__sum); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_33); __pyx_t_33 = 0;
  __pyx_t_33 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_33, __pyx_n_s__greater); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_33); __pyx_t_33 = 0;
  __pyx_t_33 = PyTuple_New(2); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __Pyx_INCREF(((PyObject *)__pyx_v_s));
  PyTuple_SET_ITEM(__pyx_t_33, 0, ((PyObject *)__pyx_v_s));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_s));
  __Pyx_INCREF(__pyx_int_0);
  PyTuple_SET_ITEM(__pyx_t_33, 1, __pyx_int_0);
  __Pyx_GIVEREF(__pyx_int_0);
  __pyx_t_3 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_33), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_33)); __pyx_t_33 = 0;
  __pyx_t_33 = PyTuple_New(1); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  PyTuple_SET_ITEM(__pyx_t_33, 0, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_3);
  __pyx_t_3 = 0;
  __pyx_t_3 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_33), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_33)); __pyx_t_33 = 0;
  __pyx_t_33 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_33, __pyx_n_s__sum); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_33); __pyx_t_33 = 0;
  __pyx_t_33 = __Pyx_GetModuleGlobalName(__pyx_n_s__np); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_33, __pyx_n_s__less); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_33); __pyx_t_33 = 0;
  __pyx_t_33 = PyTuple_New(2); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __Pyx_INCREF(((PyObject *)__pyx_v_s));
  PyTuple_SET_ITEM(__pyx_t_33, 0, ((PyObject *)__pyx_v_s));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_s));
  __Pyx_INCREF(__pyx_int_0);
  PyTuple_SET_ITEM(__pyx_t_33, 1, __pyx_int_0);
  __Pyx_GIVEREF(__pyx_int_0);
  __pyx_t_35 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_33), NULL); if (unlikely(!__pyx_t_35)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_35);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_33)); __pyx_t_33 = 0;
  __pyx_t_33 = PyTuple_New(1); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  PyTuple_SET_ITEM(__pyx_t_33, 0, __pyx_t_35);
  __Pyx_GIVEREF(__pyx_t_35);
  __pyx_t_35 = 0;
  __pyx_t_35 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_33), NULL); if (unlikely(!__pyx_t_35)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_35);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(((PyObject *)__pyx_t_33)); __pyx_t_33 = 0;
  __pyx_t_33 = PyObject_RichCompare(__pyx_t_3, __pyx_t_35, Py_LT); __Pyx_XGOTREF(__pyx_t_33); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __Pyx_DECREF(__pyx_t_35); __pyx_t_35 = 0;
  __pyx_t_36 = __Pyx_PyObject_IsTrue(__pyx_t_33); if (unlikely(__pyx_t_36 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_33); __pyx_t_33 = 0;
  if (__pyx_t_36) {
 53:         # flip
 54:         normal = -normal
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":54
 *     if np.sum(np.greater(s, 0)) < np.sum(np.less(s, 0)):
 *         # flip
 *         normal = -normal             # <<<<<<<<<<<<<<
 *         normalf = -normalf
 * 
 */
    __pyx_t_33 = PyNumber_Negative(((PyObject *)__pyx_v_normal)); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_33);
    if (!(likely(((__pyx_t_33) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_33, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __pyx_t_10 = ((PyArrayObject *)__pyx_t_33);
    {
      __Pyx_BufFmt_StackElem __pyx_stack[1];
      __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
      __pyx_t_6 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_t_10, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack);
      if (unlikely(__pyx_t_6 < 0)) {
        PyErr_Fetch(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9);
        if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normal.rcbuffer->pybuffer, (PyObject*)__pyx_v_normal, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
          Py_XDECREF(__pyx_t_7); Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_9);
          __Pyx_RaiseBufferFallbackError();
        } else {
          PyErr_Restore(__pyx_t_7, __pyx_t_8, __pyx_t_9);
        }
      }
      __pyx_pybuffernd_normal.diminfo[0].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normal.diminfo[0].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normal.diminfo[1].strides = __pyx_pybuffernd_normal.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normal.diminfo[1].shape = __pyx_pybuffernd_normal.rcbuffer->pybuffer.shape[1];
      if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 54; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_10 = 0;
    __Pyx_DECREF(((PyObject *)__pyx_v_normal));
    __pyx_v_normal = ((PyArrayObject *)__pyx_t_33);
    __pyx_t_33 = 0;
 55:         normalf = -normalf
    /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":55
 *         # flip
 *         normal = -normal
 *         normalf = -normalf             # <<<<<<<<<<<<<<
 * 
 *     return normal, normalf
 */
    __pyx_t_33 = PyNumber_Negative(((PyObject *)__pyx_v_normalf)); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_33);
    if (!(likely(((__pyx_t_33) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_33, __pyx_ptype_5numpy_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __pyx_t_37 = ((PyArrayObject *)__pyx_t_33);
    {
      __Pyx_BufFmt_StackElem __pyx_stack[1];
      __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
      __pyx_t_6 = __Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_t_37, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack);
      if (unlikely(__pyx_t_6 < 0)) {
        PyErr_Fetch(&__pyx_t_9, &__pyx_t_8, &__pyx_t_7);
        if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer, (PyObject*)__pyx_v_normalf, &__Pyx_TypeInfo_nn___pyx_t_5numpy_float64_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) {
          Py_XDECREF(__pyx_t_9); Py_XDECREF(__pyx_t_8); Py_XDECREF(__pyx_t_7);
          __Pyx_RaiseBufferFallbackError();
        } else {
          PyErr_Restore(__pyx_t_9, __pyx_t_8, __pyx_t_7);
        }
      }
      __pyx_pybuffernd_normalf.diminfo[0].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_normalf.diminfo[0].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_normalf.diminfo[1].strides = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_normalf.diminfo[1].shape = __pyx_pybuffernd_normalf.rcbuffer->pybuffer.shape[1];
      if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    }
    __pyx_t_37 = 0;
    __Pyx_DECREF(((PyObject *)__pyx_v_normalf));
    __pyx_v_normalf = ((PyArrayObject *)__pyx_t_33);
    __pyx_t_33 = 0;
    goto __pyx_L7;
  }
  __pyx_L7:;
 56: 
 57:     return normal, normalf
  /* "_cython_magic_637c713b986c525e98b048fef3dc5c09.pyx":57
 *         normalf = -normalf
 * 
 *     return normal, normalf             # <<<<<<<<<<<<<<
 */
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_33 = PyTuple_New(2); if (unlikely(!__pyx_t_33)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 57; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_33);
  __Pyx_INCREF(((PyObject *)__pyx_v_normal));
  PyTuple_SET_ITEM(__pyx_t_33, 0, ((PyObject *)__pyx_v_normal));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_normal));
  __Pyx_INCREF(((PyObject *)__pyx_v_normalf));
  PyTuple_SET_ITEM(__pyx_t_33, 1, ((PyObject *)__pyx_v_normalf));
  __Pyx_GIVEREF(((PyObject *)__pyx_v_normalf));
  __pyx_r = ((PyObject *)__pyx_t_33);
  __pyx_t_33 = 0;
  goto __pyx_L0;

  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __PYX_XDEC_MEMVIEW(&__pyx_t_4, 1);
  __PYX_XDEC_MEMVIEW(&__pyx_t_5, 1);
  __Pyx_XDECREF(__pyx_t_33);
  __Pyx_XDECREF(__pyx_t_35);
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_s.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_v.rcbuffer->pybuffer);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_AddTraceback("_cython_magic_637c713b986c525e98b048fef3dc5c09.cy_compute_normal", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_face.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normal.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_normalf.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_s.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_v.rcbuffer->pybuffer);
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_vertex.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_XDECREF((PyObject *)__pyx_v_normalf);
  __Pyx_XDECREF((PyObject *)__pyx_v_normal);
  __Pyx_XDECREF((PyObject *)__pyx_v_v);
  __Pyx_XDECREF((PyObject *)__pyx_v_s);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_637c713b986c525e98b048fef3dc5c09_1cy_compute_normal(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_pw_46_cython_magic_637c713b986c525e98b048fef3dc5c09_1cy_compute_normal(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
  PyArrayObject *__pyx_v_vertex = 0;
  PyArrayObject *__pyx_v_face = 0;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("cy_compute_normal (wrapper)", 0);
  {
    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__vertex,&__pyx_n_s__face,0};
    PyObject* values[2] = {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  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
        case  0: break;
        default: goto __pyx_L5_argtuple_error;
      }
      kw_args = PyDict_Size(__pyx_kwds);
      switch (pos_args) {
        case  0:
        if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__vertex)) != 0)) kw_args--;
        else goto __pyx_L5_argtuple_error;
        case  1:
        if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__face)) != 0)) kw_args--;
        else {
          __Pyx_RaiseArgtupleInvalid("cy_compute_normal", 1, 2, 2, 1); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
        }
      }
      if (unlikely(kw_args > 0)) {
        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "cy_compute_normal") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
      }
    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
      goto __pyx_L5_argtuple_error;
    } else {
      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
    }
    __pyx_v_vertex = ((PyArrayObject *)values[0]);
    __pyx_v_face = ((PyArrayObject *)values[1]);
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L5_argtuple_error:;
  __Pyx_RaiseArgtupleInvalid("cy_compute_normal", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_637c713b986c525e98b048fef3dc5c09.cy_compute_normal", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_vertex), __pyx_ptype_5numpy_ndarray, 1, "vertex", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_face), __pyx_ptype_5numpy_ndarray, 1, "face", 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_r = __pyx_pf_46_cython_magic_637c713b986c525e98b048fef3dc5c09_cy_compute_normal(__pyx_self, __pyx_v_vertex, __pyx_v_face);
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;
  goto __pyx_L0;
  __pyx_L1_error:;
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
In [28]:
%timeit cy_compute_normal(points, tris)
print ""
p = %prun -r cy_compute_normal(points, tris)
p.print_stats()
10 loops, best of 3: 51.7 ms per loop

          39 function calls in 0.045 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.042    0.042    0.045    0.045 {_cython_magic_637c713b986c525e98b048fef3dc5c09.cy_compute_normal}
        6    0.003    0.000    0.003    0.000 {method 'reduce' of 'numpy.ufunc' objects}
        1    0.000    0.000    0.000    0.000 _internal.py:356(_dtype_from_pep3118)
        5    0.000    0.000    0.003    0.001 fromnumeric.py:1422(sum)
        1    0.000    0.000    0.000    0.000 _methods.py:42(_mean)
        5    0.000    0.000    0.003    0.001 _methods.py:16(_sum)
        1    0.000    0.000    0.000    0.000 _methods.py:32(_count_reduce_items)
        1    0.000    0.000    0.000    0.000 _internal.py:370(get_dummy_name)
        7    0.000    0.000    0.000    0.000 {isinstance}
        1    0.000    0.000    0.045    0.045 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 numeric.py:322(asanyarray)
        1    0.000    0.000    0.000    0.000 {numpy.core.multiarray.array}
        1    0.000    0.000    0.000    0.000 fromnumeric.py:2405(mean)
        1    0.000    0.000    0.000    0.000 {method 'isdigit' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {method 'keys' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 _internal.py:561(_gcd)
        1    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 _internal.py:368(next_dummy_name)


Out[28]:
<pstats.Stats instance at 0xd5cb368>

Basic Results

We've now reduced the execution time by almost a third again. Looking at the profiler output, we see that all of the time is simply spent inside the Cython function. Since all the operations are vectorized, we are unlikely to see anything but very incremental improvements. However, we've gone from nearly 3s down to around 50ms. Looking at the code, we've changed very little from the original Python version. Easily the most difficult part was rolling our own cross product, and even that was not really a necessary optimisation.

Now, just for our own piece of mind, we'll check that our optimised Cython version produces the same output as the original Python implementation.

In [29]:
cy_normal, cy_normalf = cy_compute_normal(points, tris)
py_normal, py_normalf = py_compute_normal(points, tris)

print np.allclose(cy_normal, py_normal)
print np.allclose(cy_normalf, py_normalf)
True
True

Numba

By request I have implemented the algorithm as best I could in Numba. I should note that:

  • I have no idea what I'm doing using Numba, so this is unlikely to be optimised
  • I had enormous trouble even getting Numba to run on Ubuntu 13.04. I had to build and install my own LLVM-3.2.
  • It took my about 2 hours just to get something to compile, and I had to resort to print statements because the output is so useless

I've tried to comment why I did certain unrollings, though I don't justify them because I don't really understand them. I assume that specifying the expected type will help the optimisation - but I honestly have no idea. Presumably the np.greater and np.less problem is a bug in Numba?

In [30]:
import numba
from numba.decorators import autojit, jit
from math import sqrt


# Had to unroll this otherwise it complained about some strange python object
# coercion error
@autojit
def numba_normalise(vec):
    # Avoid divisions by almost 0 numbers
    # np.spacing(1) is equivalent to Matlab's eps
    n = vec.shape[0]
    for i in range(n):
        d = sqrt(vec[i, 0] * vec[i, 0] +
                 vec[i, 1] * vec[i, 1] +
                 vec[i, 2] * vec[i, 2])
        if d < np.spacing(1):
            d = 1.0

        vec[i, 0] /= d
        vec[i, 1] /= d
        vec[i, 2] /= d


# If I didn't roll my own cross product then computing
# the normals actually takes LONGER than the pure Python implementation
@jit(argtypes=(numba.double[:, :], numba.double[:, :]))
def cross_numba(x, y):
    output = np.empty_like(x)
    n = x.shape[0]
    for i in range(n):
        output[i, 0] = x[i, 1] * y[i, 2] - x[i, 2] * y[i, 1]
        output[i, 1] = x[i, 2] * y[i, 0] - x[i, 0] * y[i, 2]
        output[i, 2] = x[i, 0] * y[i, 1] - x[i, 1] * y[i, 0]
    return output


@jit(argtypes=(numba.double[:, :], numba.int32[:, :]))
def numba_compute_normal(vertex, face):
    nface = face.shape[0]

    # Calculate the cross product (per-face normal)
    normalf = cross_numba(vertex[face[:, 1], :] - vertex[face[:, 0], :],
                          vertex[face[:, 2], :] - vertex[face[:, 0], :])
    numba_normalise(normalf)

    # Calculate per-vertex normal
    normal = np.zeros_like(vertex)
    for i in range(nface):
        f = face[i, :]
        for j in range(3):
            normal[f[j], :] += normalf[i, :]

    # Normalize
    numba_normalise(normal)

    # Enforce that the normal are outward
    v = vertex - np.mean(vertex)[..., None]
    s = np.sum(v * normal, axis=1)
    s_gt_sum = 0
    s_lt_sum = 0

    # Had to expand this loop otherwise numba complained:
    # 'only length-1 arrays can be converted to Python scalars'
    # On the calls to np.greater and np.less
    for i in range(s.shape[0]):
        if s[i] > 0:
            s_gt_sum += 1
        elif s[i] < 0:
            s_lt_sum += 1

    if s_gt_sum < s_lt_sum:
        # flip
        normal = -normal
        normalf = -normalf

    return normal, normalf

As for the results, we've gone down to around 800ms, which is definitely an improvement. Interestingly, this is on par with a Matlab implementation that I have (which is known to be jitted). To sanity check we will also check that the output is correct.

In [31]:
%timeit numba_compute_normal(points, tris)
1 loops, best of 3: 783 ms per loop
In [32]:
numba_normal, numba_normalf = numba_compute_normal(points, tris)
py_normal, py_normalf = py_compute_normal(points, tris)

print np.allclose(numba_normal, py_normal)
print np.allclose(numba_normalf, py_normalf)
True
True