Ver Fonte

Merge branch 'master' of https://github.com/libigl/libigl

Former-commit-id: dce20528caa9d5778d9608d6c0e9eabd1cbdfee4
Olga Diamanti há 9 anos atrás
pai
commit
fc13fa9986

+ 0 - 1
include/igl/AABB.cpp

@@ -10,7 +10,6 @@
 #include "barycenter.h"
 #include "barycentric_coordinates.h"
 #include "colon.h"
-#include "colon.h"
 #include "doublearea.h"
 #include "matlab_format.h"
 #include "point_simplex_squared_distance.h"

+ 29 - 0
include/igl/copyleft/cgal/assign_scalar.cpp

@@ -29,9 +29,38 @@ IGL_INLINE void igl::copyleft::cgal::assign_scalar(
   } while (d < interval.second);
 }
 
+IGL_INLINE void igl::copyleft::cgal::assign_scalar(
+  const typename CGAL::Epeck::FT & _cgal,
+  float& d)
+{
+  // FORCE evaluation of the exact type otherwise interval might be huge.
+  const typename CGAL::Epeck::FT cgal = _cgal.exact();
+  const auto interval = CGAL::to_interval(cgal);
+  d = interval.first;
+  do {
+      const float next = nextafter(d, float(interval.second));
+      if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break;
+      d = next;
+  } while (d < float(interval.second));
+}
+
 IGL_INLINE void igl::copyleft::cgal::assign_scalar(
   const double & c,
   double & d)
 {
   d = c;
 }
+
+IGL_INLINE void igl::copyleft::cgal::assign_scalar(
+  const float& c,
+  float& d)
+{
+  d = c;
+}
+
+IGL_INLINE void igl::copyleft::cgal::assign_scalar(
+  const float& c,
+  double& d)
+{
+  d = c;
+}

+ 9 - 0
include/igl/copyleft/cgal/assign_scalar.h

@@ -25,9 +25,18 @@ namespace igl
       IGL_INLINE void assign_scalar(
         const typename CGAL::Epeck::FT & cgal,
         double & d);
+      IGL_INLINE void assign_scalar(
+        const typename CGAL::Epeck::FT & cgal,
+        float& d);
       IGL_INLINE void assign_scalar(
         const double & c,
         double & d);
+      IGL_INLINE void assign_scalar(
+        const float& c,
+        float & d);
+      IGL_INLINE void assign_scalar(
+        const float& c,
+        double& d);
     }
   }
 }

+ 2 - 0
include/igl/edge_flaps.h

@@ -24,6 +24,8 @@ namespace igl
   //     F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) "
   //     e=(j->i)
   //   EI  #E by 2 list of edge flap corners (see above).
+  //
+  // TODO: This seems to be a duplicate of edge_topology.h
   IGL_INLINE void edge_flaps(
     const Eigen::MatrixXi & F,
     const Eigen::MatrixXi & E,

+ 3 - 1
include/igl/edge_topology.h

@@ -16,11 +16,13 @@
 namespace igl 
 {
   // Initialize Edges and their topological relations
-  
+  //
   // Output:
   // EV  : #Ex2, Stores the edge description as pair of indices to vertices
   // FE : #Fx3, Stores the Triangle-Edge relation
   // EF : #Ex2: Stores the Edge-Triangle relation
+  //
+  // TODO: This seems to be a duplicate of edge_flaps.h
 template <typename DerivedV, typename DerivedF>
   IGL_INLINE void edge_topology(
     const Eigen::PlainObjectBase<DerivedV>& V,

+ 4 - 18
include/igl/get_seconds.cpp

@@ -6,24 +6,10 @@
 // v. 2.0. If a copy of the MPL was not distributed with this file, You can 
 // obtain one at http://mozilla.org/MPL/2.0/.
 #include "get_seconds.h"
-// NULL for Linux
-#include <cstddef>
-
-#if _WIN32
-#  include <ctime>
+#include <chrono>
 IGL_INLINE double igl::get_seconds()
 {
-  // This does not work on mac os x with glut in the main loop
-  return double(clock())/CLOCKS_PER_SEC;
+  return 
+    std::chrono::duration<double>(
+      std::chrono::system_clock::now().time_since_epoch()).count();
 }
-#else
-#  include <sys/time.h>
-IGL_INLINE double igl::get_seconds()
-{
-  timeval time;
-  gettimeofday(&time, NULL);
-  return time.tv_sec + time.tv_usec / 1e6;
-  // This does not work on mac os x with glut in the main loop
-  //return double(clock())/CLOCKS_PER_SEC;
-}
-#endif

+ 47 - 36
include/igl/harmonic.cpp

@@ -6,13 +6,15 @@
 // v. 2.0. If a copy of the MPL was not distributed with this file, You can
 // obtain one at http://mozilla.org/MPL/2.0/.
 #include "harmonic.h"
-#include "cotmatrix.h"
-#include "massmatrix.h"
-#include "invert_diag.h"
 #include "adjacency_matrix.h"
-#include "sum.h"
+#include "cotmatrix.h"
 #include "diag.h"
+#include "invert_diag.h"
+#include "isdiag.h"
+#include "massmatrix.h"
 #include "min_quad_with_fixed.h"
+#include "speye.h"
+#include "sum.h"
 #include <Eigen/Sparse>
 
 template <
@@ -31,9 +33,7 @@ IGL_INLINE bool igl::harmonic(
 {
   using namespace Eigen;
   typedef typename DerivedV::Scalar Scalar;
-  typedef Matrix<Scalar,Dynamic,1> VectorXS;
-  SparseMatrix<Scalar> Q;
-  SparseMatrix<Scalar> L,M,Mi;
+  SparseMatrix<Scalar> L,M;
   cotmatrix(V,F,L);
   switch(F.cols())
   {
@@ -45,28 +45,7 @@ IGL_INLINE bool igl::harmonic(
       massmatrix(V,F,MASSMATRIX_TYPE_BARYCENTRIC,M);
     break;
   }
-  invert_diag(M,Mi);
-  Q = -L;
-  for(int p = 1;p<k;p++)
-  {
-    Q = (Q*Mi*-L).eval();
-  }
-
-  min_quad_with_fixed_data<Scalar> data;
-  min_quad_with_fixed_precompute(Q,b,SparseMatrix<Scalar>(),true,data);
-  W.resize(V.rows(),bc.cols());
-  const VectorXS B = VectorXS::Zero(V.rows(),1);
-  for(int w = 0;w<bc.cols();w++)
-  {
-    const VectorXS bcw = bc.col(w);
-    VectorXS Ww;
-    if(!min_quad_with_fixed_solve(data,B,bcw,VectorXS(),Ww))
-    {
-      return false;
-    }
-    W.col(w) = Ww;
-  }
-  return true;
+  return harmonic(L,M,b,bc,k,W);
 }
 
 template <
@@ -83,8 +62,6 @@ IGL_INLINE bool igl::harmonic(
 {
   using namespace Eigen;
   typedef typename Derivedbc::Scalar Scalar;
-  typedef Matrix<Scalar,Dynamic,1> VectorXS;
-  SparseMatrix<Scalar> Q;
   SparseMatrix<Scalar> A;
   adjacency_matrix(F,A);
   // sum each row
@@ -93,12 +70,46 @@ IGL_INLINE bool igl::harmonic(
   // Convert row sums into diagonal of sparse matrix
   SparseMatrix<Scalar> Adiag;
   diag(Asum,Adiag);
-  // Build uniform laplacian
-  Q = -A+Adiag;
+  SparseMatrix<Scalar> L = A-Adiag;
+  SparseMatrix<Scalar> M;
+  speye(L.rows(),M);
+  return harmonic(L,M,b,bc,k,W);
+}
+
+template <
+  typename DerivedL,
+  typename DerivedM,
+  typename Derivedb,
+  typename Derivedbc,
+  typename DerivedW>
+IGL_INLINE bool igl::harmonic(
+  const Eigen::SparseMatrix<DerivedL> & L,
+  const Eigen::SparseMatrix<DerivedM> & M,
+  const Eigen::PlainObjectBase<Derivedb> & b,
+  const Eigen::PlainObjectBase<Derivedbc> & bc,
+  const int k,
+  Eigen::PlainObjectBase<DerivedW> & W)
+{
+  const int n = L.rows();
+  assert(n == L.cols() && "L must be square");
+  assert(n == M.cols() && "M must be same size as L");
+  assert(n == M.rows() && "M must be square");
+  assert(igl::isdiag(M) && "Mass matrix should be diagonal");
+
+  Eigen::SparseMatrix<DerivedM> Mi;
+  invert_diag(M,Mi);
+  Eigen::SparseMatrix<DerivedL> Q;
+  Q = -L;
+  for(int p = 1;p<k;p++)
+  {
+    Q = (Q*Mi*-L).eval();
+  }
+  typedef DerivedL Scalar;
   min_quad_with_fixed_data<Scalar> data;
-  min_quad_with_fixed_precompute(Q,b,SparseMatrix<Scalar>(),true,data);
-  W.resize(A.rows(),bc.cols());
-  const VectorXS B = VectorXS::Zero(A.rows(),1);
+  min_quad_with_fixed_precompute(Q,b,Eigen::SparseMatrix<Scalar>(),true,data);
+  W.resize(n,bc.cols());
+  typedef Eigen::Matrix<Scalar,Eigen::Dynamic,1> VectorXS;
+  const VectorXS B = VectorXS::Zero(n,1);
   for(int w = 0;w<bc.cols();w++)
   {
     const VectorXS bcw = bc.col(w);

+ 46 - 24
include/igl/harmonic.h

@@ -9,6 +9,7 @@
 #define IGL_HARMONIC_H
 #include "igl_inline.h"
 #include <Eigen/Core>
+#include <Eigen/Sparse>
 namespace igl
 {
   // Compute k-harmonic weight functions "coordinates".
@@ -36,30 +37,51 @@ namespace igl
     const Eigen::PlainObjectBase<Derivedbc> & bc,
     const int k,
     Eigen::PlainObjectBase<DerivedW> & W);
-
- // Compute harmonic map using uniform laplacian operator
- //
- //
- // Inputs:
- //   F  #F by simplex-size list of element indices
- //   b  #b boundary indices into V
- //   bc #b by #W list of boundary values
- //   k  power of harmonic operation (1: harmonic, 2: biharmonic, etc)
- // Outputs:
- //   W  #V by #W list of weights
- //
- template <
-   typename DerivedF,
-   typename Derivedb,
-   typename Derivedbc,
-   typename DerivedW>
- IGL_INLINE bool harmonic(
-   const Eigen::PlainObjectBase<DerivedF> & F,
-   const Eigen::PlainObjectBase<Derivedb> & b,
-   const Eigen::PlainObjectBase<Derivedbc> & bc,
-   const int k,
-   Eigen::PlainObjectBase<DerivedW> & W);
- };
+  // Compute harmonic map using uniform laplacian operator
+  //
+  // Inputs:
+  //   F  #F by simplex-size list of element indices
+  //   b  #b boundary indices into V
+  //   bc #b by #W list of boundary values
+  //   k  power of harmonic operation (1: harmonic, 2: biharmonic, etc)
+  // Outputs:
+  //   W  #V by #W list of weights
+  //
+  template <
+    typename DerivedF,
+    typename Derivedb,
+    typename Derivedbc,
+    typename DerivedW>
+  IGL_INLINE bool harmonic(
+    const Eigen::PlainObjectBase<DerivedF> & F,
+    const Eigen::PlainObjectBase<Derivedb> & b,
+    const Eigen::PlainObjectBase<Derivedbc> & bc,
+    const int k,
+    Eigen::PlainObjectBase<DerivedW> & W);
+  // Compute a harmonic map using a given Laplacian and mass matrix
+  //
+  // Inputs:
+  //   L  #V by #V discrete (integrated) Laplacian  
+  //   M  #V by #V mass matrix
+  //   b  #b boundary indices into V
+  //   bc  #b by #W list of boundary values
+  //   k  power of harmonic operation (1: harmonic, 2: biharmonic, etc)
+  // Outputs:
+  //   W  #V by #V list of weights
+  template <
+    typename DerivedL,
+    typename DerivedM,
+    typename Derivedb,
+    typename Derivedbc,
+    typename DerivedW>
+  IGL_INLINE bool harmonic(
+    const Eigen::SparseMatrix<DerivedL> & L,
+    const Eigen::SparseMatrix<DerivedM> & M,
+    const Eigen::PlainObjectBase<Derivedb> & b,
+    const Eigen::PlainObjectBase<Derivedbc> & bc,
+    const int k,
+    Eigen::PlainObjectBase<DerivedW> & W);
+};
 
 #ifndef IGL_STATIC_LIBRARY
 #include "harmonic.cpp"

+ 32 - 0
include/igl/isdiag.cpp

@@ -0,0 +1,32 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+#include "isdiag.h"
+
+template <typename Scalar>
+IGL_INLINE bool igl::isdiag(const Eigen::SparseMatrix<Scalar> & A)
+{
+  // Iterate over outside of A
+  for(int k=0; k<A.outerSize(); ++k)
+  {
+    // Iterate over inside
+    for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
+    {
+      if(it.row() != it.col() && it.value()!=0)
+      {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template specialization
+template bool igl::isdiag<double>(class Eigen::SparseMatrix<double,0,int> const &);
+#endif

+ 26 - 0
include/igl/isdiag.h

@@ -0,0 +1,26 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+#ifndef IGL_ISDIAG_H
+#define IGL_ISDIAG_H
+#include "igl_inline.h"
+#include <Eigen/Sparse>
+namespace igl
+{
+  // Determine if a given matrix is diagonal: all non-zeros lie on the
+  // main diagonal.
+  //
+  // Inputs:
+  //   A  m by n sparse matrix
+  // Returns true iff and only if the matrix is diagonal.
+  template <typename Scalar>
+  IGL_INLINE bool isdiag(const Eigen::SparseMatrix<Scalar> & A);
+};
+#ifndef IGL_STATIC_LIBRARY
+#  include "isdiag.cpp"
+#endif
+#endif

+ 36 - 14
include/igl/sortrows.cpp

@@ -62,24 +62,46 @@ IGL_INLINE void igl::sortrows(
   using namespace std;
   using namespace Eigen;
   // Resize output
-  Y.resize(X.rows(),X.cols());
-  IX.resize(X.rows(),1);
-  for(int i = 0;i<X.rows();i++)
+  const size_t num_rows = X.rows();
+  const size_t num_cols = X.cols();
+  Y.resize(num_rows,num_cols);
+  IX.resize(num_rows,1);
+  for(int i = 0;i<num_rows;i++)
   {
     IX(i) = i;
   }
-  std::sort(
-    IX.data(),
-    IX.data()+IX.size(),
-    igl::IndexRowLessThan<const Eigen::PlainObjectBase<DerivedX> & >(X));
-  // if not ascending then reverse
-  if(!ascending)
-  {
-    std::reverse(IX.data(),IX.data()+IX.size());
+  if (ascending) {
+    auto index_less_than = [&X, num_cols](size_t i, size_t j) {
+      for (size_t c=0; c<num_cols; c++) {
+        if (X.coeff(i, c) < X.coeff(j, c)) return true;
+        else if (X.coeff(j,c) < X.coeff(i,c)) return false;
+      }
+      return false;
+    };
+      std::sort(
+        IX.data(),
+        IX.data()+IX.size(),
+        index_less_than
+        );
+  } else {
+    auto index_greater_than = [&X, num_cols](size_t i, size_t j) {
+      for (size_t c=0; c<num_cols; c++) {
+        if (X.coeff(i, c) > X.coeff(j, c)) return true;
+        else if (X.coeff(j,c) > X.coeff(i,c)) return false;
+      }
+      return false;
+    };
+      std::sort(
+        IX.data(),
+        IX.data()+IX.size(),
+        index_greater_than
+        );
   }
-  for(int i = 0;i<X.rows();i++)
-  {
-    Y.row(i) = X.row(IX(i));
+  for (size_t j=0; j<num_cols; j++) {
+      for(int i = 0;i<num_rows;i++)
+      {
+          Y(i,j) = X(IX(i), j);
+      }
   }
 }
 

+ 19 - 8
include/igl/unique.cpp

@@ -12,7 +12,6 @@
 #include "sortrows.h"
 #include "list_to_matrix.h"
 #include "matrix_to_list.h"
-#include "get_seconds.h"
 
 #include <algorithm>
 #include <iostream>
@@ -213,21 +212,32 @@ IGL_INLINE void igl::unique_rows(
   sortrows(A,true,sortA,IM);
 
 
-  vector<int> vIA(sortA.rows());
-  for(int i=0;i<(int)sortA.rows();i++)
+  const int num_rows = sortA.rows();
+  const int num_cols = sortA.cols();
+  vector<int> vIA(num_rows);
+  for(int i=0;i<num_rows;i++)
   {
     vIA[i] = i;
   }
+
+  auto index_equal = [&sortA, &num_cols](const size_t i, const size_t j) {
+    for (size_t c=0; c<num_cols; c++) {
+      if (sortA.coeff(i,c) != sortA.coeff(j,c))
+        return false;
+    }
+    return true;
+  };
   vIA.erase(
     std::unique(
     vIA.begin(),
     vIA.end(),
-    igl::IndexRowEquals<const Eigen::PlainObjectBase<DerivedA> &>(sortA)),vIA.end());
+    index_equal
+    ),vIA.end());
 
   IC.resize(A.rows(),1);
   {
     int j = 0;
-    for(int i = 0;i<(int)sortA.rows();i++)
+    for(int i = 0;i<num_rows;i++)
     {
       if(sortA.row(vIA[j]) != sortA.row(i))
       {
@@ -236,10 +246,11 @@ IGL_INLINE void igl::unique_rows(
       IC(IM(i,0),0) = j;
     }
   }
-  C.resize(vIA.size(),A.cols());
-  IA.resize(vIA.size(),1);
+  const int unique_rows = vIA.size();
+  C.resize(unique_rows,A.cols());
+  IA.resize(unique_rows,1);
   // Reindex IA according to IM
-  for(int i = 0;i<(int)vIA.size();i++)
+  for(int i = 0;i<unique_rows;i++)
   {
     IA(i,0) = IM(vIA[i],0);
     C.row(i) = A.row(IA(i,0));

+ 5 - 47
include/igl/viewer/Viewer.cpp

@@ -84,7 +84,7 @@ static int global_KMod = 0;
 
 static void glfw_mouse_press(GLFWwindow* window, int button, int action, int modifier)
 {
-  bool tw_used = 
+  bool tw_used =
 #ifdef IGL_VIEWER_WITH_NANOGUI
     __viewer->screen->mouseButtonCallbackEvent(button,action,modifier);
 #else
@@ -275,33 +275,6 @@ namespace viewer
 
   IGL_INLINE Viewer::Viewer()
   {
-    // This mess is to help debug problems arising when compiling
-    // libiglviewer.a with(without) IGL_STATIC_LIBRARY defined and including
-    // Viewer.h without(with) IGL_STATIC_LIBRARY defined.
-#ifdef IGL_STATIC_LIBRARY
-    std::cout<<"igl_with_nanogui_defined_consistently: "<<igl_with_nanogui_defined_consistently()<<std::endl;
-    std::cout<<"igl_with_nanogui_defined_at_compile: "<<  igl_with_nanogui_defined_at_compile()  <<std::endl;
-    std::cout<<"igl_with_nanogui_defined_at_include: "<<  igl_with_nanogui_defined_at_include()  <<std::endl;
-    // First try to first assert
-    assert(igl_with_nanogui_defined_consistently() && 
-      "Must compile and include with IGL_VIEWER_WITH_NANOGUI defined consistently");
-#ifdef NDEBUG
-    // Second print warning since it's hopeless that this will run if wrong.
-    if(!igl_with_nanogui_defined_consistently())
-    {
-      std::cerr<<
-        "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<<std::endl<<std::endl<<
-        "WARNING: Seems that IGL_WITH_NANOGUI " <<
-        (igl_with_nanogui_defined_at_compile() ? "was" : "was not") <<
-        " defined"<<std::endl<<"during compilation of Viewer.cpp and "<<
-        (igl_with_nanogui_defined_at_include() ? "was" : "was not") <<
-        " defined"<<std::endl<<"during inclusion of Viewer.h"<<std::endl <<
-        "You're about to get some nasty memory errors."<<std::endl<<std::endl<<
-        "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<<std::endl;
-    }
-#endif
-#endif
-
 #ifdef IGL_VIEWER_WITH_NANOGUI
     ngui = nullptr;
     screen = nullptr;
@@ -629,11 +602,11 @@ namespace viewer
       center = data.V.colwise().sum()/data.V.rows();
     }
 
-    Eigen::Vector3f coord = 
+    Eigen::Vector3f coord =
       igl::project(
-        Eigen::Vector3f(center(0),center(1),center(2)), 
-        (core.view * core.model).eval(), 
-        core.proj, 
+        Eigen::Vector3f(center(0),center(1),center(2)),
+        (core.view * core.model).eval(),
+        core.proj,
         core.viewport);
     down_mouse_z = coord[2];
     down_rotation = core.trackball_angle;
@@ -1044,20 +1017,5 @@ namespace viewer
     launch_shut();
     return EXIT_SUCCESS;
   }
-  IGL_INLINE bool Viewer::igl_with_nanogui_defined_at_compile()
-  {
-#ifdef IGL_VIEWER_WITH_NANOGUI
-    return true;
-#else
-    return false;
-#endif
-  }
-  IGL_INLINE bool Viewer::igl_with_nanogui_defined_consistently()
-  {
-    return 
-      igl_with_nanogui_defined_at_compile() == 
-      igl_with_nanogui_defined_at_include();
-  }
 } // end namespace
 }
-

+ 0 - 18
include/igl/viewer/Viewer.h

@@ -153,28 +153,10 @@ namespace viewer
     void* callback_key_down_data;
     void* callback_key_up_data;
 
-  public:
-
-    static IGL_INLINE bool igl_with_nanogui_defined_at_compile();
-    static IGL_INLINE bool igl_with_nanogui_defined_consistently();
-
   public:
       EIGEN_MAKE_ALIGNED_OPERATOR_NEW
   };
 
-  bool igl_with_nanogui_defined_at_include();
-#ifndef IGL_VIEWER_VIEWER_CPP
-  bool igl_with_nanogui_defined_at_include()
-  {
-    // this must be inlined here.
-#ifdef IGL_VIEWER_WITH_NANOGUI
-    return true;
-#else
-    return false;
-#endif
-  }
-#endif
-
 } // end namespace
 } // end namespace
 

+ 5 - 3
python/iglhelpers.py

@@ -4,6 +4,8 @@ import pyigl as igl
 
 def p2e(m):
     if isinstance(m, np.ndarray):
+        if not m.flags['C_CONTIGUOUS']:
+            raise TypeError("p2e only support C-contiguous order")
         if m.dtype.type == np.int32:
             return igl.eigen.MatrixXi(m)
         elif m.dtype.type == np.float64:
@@ -33,11 +35,11 @@ def p2e(m):
 
 def e2p(m):
     if isinstance(m, igl.eigen.MatrixXd):
-        return np.array(m, dtype='float64')
+        return np.array(m, dtype='float64', order='C')
     elif isinstance(m, igl.eigen.MatrixXi):
-        return np.array(m, dtype='int32')
+        return np.array(m, dtype='int32', order='C')
     elif isinstance(m, igl.eigen.MatrixXb):
-        return np.array(m, dtype='bool')
+        return np.array(m, dtype='bool', order='C')
     elif isinstance(m, igl.eigen.SparseMatrixd):
         coo = np.array(m.toCOO())
         I = coo[:, 0]

+ 1 - 1
tutorial/tutorial.html.REMOVED.git-id

@@ -1 +1 @@
-47cfcb9ea6dd7363e69106910fe3eaaf0aacc40b
+85350ebcb5f876161287c77568f3b6900d02b707

+ 1 - 1
tutorial/tutorial.md.REMOVED.git-id

@@ -1 +1 @@
-129cef4e745b13b5c567053630e25f92dfead7e1
+24049c9ad2cc3eff90b021f5f1d017c8c042e6da