205_Laplacian.py 2.6 KB

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