205_Laplacian.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. from __future__ import print_function
  2. import igl
  3. import math
  4. global V
  5. global U
  6. global F
  7. global L
  8. V = igl.eigen.MatrixXd()
  9. U = igl.eigen.MatrixXd()
  10. F = igl.eigen.MatrixXi()
  11. L = igl.eigen.SparseMatrixd()
  12. viewer = igl.viewer.Viewer()
  13. # Load a mesh in OFF format
  14. igl.readOFF("../tutorial/shared/cow.off", V, F)
  15. # Compute Laplace-Beltrami operator: #V by #V
  16. igl.cotmatrix(V,F,L)
  17. # Alternative construction of same Laplacian
  18. G = igl.eigen.SparseMatrixd()
  19. K = igl.eigen.SparseMatrixd()
  20. # Gradient/Divergence
  21. igl.grad(V,F,G);
  22. # Diagonal per-triangle "mass matrix"
  23. dblA = igl.eigen.MatrixXd()
  24. igl.doublearea(V,F,dblA)
  25. # Place areas along diagonal #dim times
  26. T = (dblA.replicate(3,1)*0.5).asDiagonal() * 1
  27. # Laplacian K built as discrete divergence of gradient or equivalently
  28. # discrete Dirichelet energy Hessian
  29. temp = -G.transpose()
  30. K = -G.transpose() * T * G
  31. print("|K-L|: ",(K-L).norm())
  32. def key_pressed(viewer, key, modifier):
  33. global V
  34. global U
  35. global F
  36. global L
  37. if key == ord('r') or key == ord('R'):
  38. U = V;
  39. elif key == ord(' '):
  40. # Recompute just mass matrix on each step
  41. M = igl.eigen.SparseMatrixd()
  42. igl.massmatrix(U,F,igl.MASSMATRIX_TYPE_BARYCENTRIC,M);
  43. # Solve (M-delta*L) U = M*U
  44. S = (M - 0.001*L)
  45. solver = igl.eigen.SimplicialLLTsparse(S)
  46. U = solver.solve(M*U)
  47. # Compute centroid and subtract (also important for numerics)
  48. dblA = igl.eigen.MatrixXd()
  49. igl.doublearea(U,F,dblA)
  50. print(dblA.sum())
  51. area = 0.5*dblA.sum()
  52. BC = igl.eigen.MatrixXd()
  53. igl.barycenter(U,F,BC)
  54. centroid = igl.eigen.MatrixXd([[0.0,0.0,0.0]])
  55. for i in range(0,BC.rows()):
  56. centroid += 0.5*dblA[i,0]/area*BC.row(i)
  57. U -= centroid.replicate(U.rows(),1)
  58. # Normalize to unit surface area (important for numerics)
  59. U = U / math.sqrt(area)
  60. else:
  61. return False
  62. # Send new positions, update normals, recenter
  63. viewer.data.set_vertices(U)
  64. viewer.data.compute_normals()
  65. viewer.core.align_camera_center(U,F)
  66. return True
  67. # Use original normals as pseudo-colors
  68. N = igl.eigen.MatrixXd()
  69. igl.per_vertex_normals(V,F,N)
  70. C = N.rowwiseNormalized()*0.5+0.5;
  71. # Initialize smoothing with base mesh
  72. U = V
  73. viewer.data.set_mesh(U, F)
  74. viewer.data.set_colors(C)
  75. viewer.callback_key_pressed = key_pressed
  76. print("Press [space] to smooth.")
  77. print("Press [r] to reset.")
  78. viewer.launch()