5
5
import mcubes #pip install --upgrade PyMCubes
6
6
import json
7
7
import argparse
8
+ from matplotlib import cm
8
9
9
10
# -- Begin primitive SDFs from
10
11
# https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
@@ -81,6 +82,7 @@ def compute_curvature_directions_taubin(mesh):
81
82
eig_min = np .zeros ((n ,), dtype = float )
82
83
confidence = np .zeros ((n ,), dtype = float )
83
84
85
+ eps = 1e-8
84
86
edges = [[] for i in range (n )]
85
87
86
88
for i in range (m ):
@@ -94,26 +96,36 @@ def compute_curvature_directions_taubin(mesh):
94
96
95
97
for i in range (n ):
96
98
total_area = 0
97
- nv = normals [i ][:,None ]
99
+ nv = - normals [i ][:,None ]
98
100
inn = np .identity (3 ) - nv @ nv .T
99
101
if len (edges [i ]) == 0 :
100
102
continue
101
103
for u , tri in edges [i ]:
102
104
uv = (vertices [u ] - vertices [i ])[:,None ]
103
- if areas [tri ] < 1e-8 :
105
+ if areas [tri ] < eps or np . linalg . norm ( uv ) < eps :
104
106
#print(areas[tri], np.inner(uv[:,0], uv[:,0]))
105
107
continue
106
108
innuv = inn @ uv
107
- t = (innuv ) / np .linalg .norm (innuv )
109
+ fail_count = 0
110
+ while np .linalg .norm (innuv ) < eps and fail_count < 10 :
111
+ uv += np .random .normal (0 , 0.001 * np .linalg .norm (uv ), uv .shape )
112
+ innuv = inn @ uv
113
+ fail_count += 1
114
+ if fail_count >= 5 :
115
+ continue
116
+
117
+ t = innuv / np .linalg .norm (innuv )
108
118
kappa = 2 * np .inner (nv [:,0 ], uv [:,0 ]) / np .inner (uv [:,0 ], uv [:,0 ])
109
119
110
120
total_area += areas [tri ]
111
121
matrices [i ,:,:] += areas [tri ] * kappa * (t @ t .T )
112
122
113
123
#not necessary for finding eigenvectors
114
- if total_area < 1e-8 :
115
- total_area = 1
116
- matrices [i ,:,:] /= total_area
124
+ if total_area > 0 :
125
+ matrices [i ,:,:] /= total_area
126
+
127
+ #print(i)
128
+ #print(matrices[i,:,:])
117
129
118
130
eigvals , eigvecs = eig (matrices [i ])
119
131
eigvals = eigvals .real
@@ -236,7 +248,9 @@ def compute_curvature_directions_rusinkiewicz(mesh):
236
248
LMN_local = np .array ([[x [0 ], x [1 ]], [x [1 ], x [2 ]]], dtype = float )
237
249
238
250
rot_axis = normalize (np .cross (ay , tay ))
239
- rot_theta = np .arccos (np .dot (ay , tay ))
251
+ if abs (np .dot (ay , tay )) > 1 :
252
+ print (ay , tay , np .dot (ay , tay ), "???" )
253
+ rot_theta = np .arccos (np .clip (np .dot (ay , tay ), - 1 , 1 ))
240
254
rot = Rotation .from_rotvec (rot_theta * rot_axis ).as_matrix ()
241
255
242
256
rax = rot @ ax
@@ -249,6 +263,8 @@ def compute_curvature_directions_rusinkiewicz(mesh):
249
263
matrices [idxA ,:,:] += np .array ([[L , M ], [M , N ]]) * area
250
264
251
265
for i in range (n ):
266
+ if vertex_areas [i ] > 0 :
267
+ matrices [i ] /= vertex_areas [i ]
252
268
eigvals , eigvecs = eig (matrices [i ])
253
269
eigvals = eigvals .real
254
270
eigvecs = eigvecs .real
@@ -274,15 +290,15 @@ def get_lineset(vertices, vectors, color, l=0.01):
274
290
line_set .colors = o3d .utility .Vector3dVector (colors )
275
291
return line_set
276
292
277
- def visualize_curvature_directions (mesh , l = 0.01 , show_normals = False , taubin = False ):
293
+ def visualize_curvature_directions (mesh , l = 0.01 , show_normals = False , show_curvature = True , taubin = False ):
278
294
mesh .compute_vertex_normals ()
279
295
vertices = np .array (mesh .vertices )
280
296
normals = np .array (mesh .vertex_normals )
281
297
n = vertices .shape [0 ]
282
298
if taubin :
283
- curvature_min , curvature_max , _ , _ , _ = compute_curvature_directions_taubin (mesh )
299
+ curvature_min , curvature_max , eig_min , eig_max , _ = compute_curvature_directions_taubin (mesh )
284
300
else :
285
- curvature_min , curvature_max , _ , _ , _ = compute_curvature_directions_rusinkiewicz (mesh )
301
+ curvature_min , curvature_max , eig_min , eig_max , _ = compute_curvature_directions_rusinkiewicz (mesh )
286
302
287
303
288
304
line_set_max = get_lineset (vertices , curvature_max , [1 , 0 , 0 ])
@@ -293,6 +309,20 @@ def visualize_curvature_directions(mesh, l=0.01, show_normals=False, taubin=Fals
293
309
o3d .visualization .draw_geometries ([mesh , line_set_min , line_set_max , line_set_normal ])
294
310
else :
295
311
o3d .visualization .draw_geometries ([mesh , line_set_min , line_set_max ])
312
+
313
+ if show_curvature :
314
+ old_colors = np .array (mesh .vertex_colors )
315
+ paint = cm .get_cmap ("seismic" )
316
+ gaussian_curvature = eig_min * eig_max
317
+ print ("Gaussian curvature:\t {:.4f}\t {:.4f}" .format (gaussian_curvature .min (), gaussian_curvature .max ()))
318
+ mesh .vertex_colors = o3d .utility .Vector3dVector (paint (gaussian_curvature / 5 + 0.5 )[:,:3 ])
319
+ o3d .visualization .draw_geometries ([mesh ])
320
+ paint = cm .get_cmap ("PiYG" )
321
+ mean_curvature = 1 / 2 * (eig_min + eig_max )
322
+ print ("Mean curvature:\t \t {:.4f}\t {:.4f}" .format (mean_curvature .min (), mean_curvature .max ()))
323
+ mesh .vertex_colors = o3d .utility .Vector3dVector (paint (mean_curvature / 5 + 0.5 )[:,:3 ])
324
+ o3d .visualization .draw_geometries ([mesh ])
325
+ mesh .vertex_colors = o3d .utility .Vector3dVector (old_colors )
296
326
297
327
def center_mesh (mesh ):
298
328
vertices = np .array (mesh .vertices )
@@ -354,13 +384,63 @@ def compare_taubin_rusinkiewicz(mesh):
354
384
visualize_curvature_directions (mesh , taubin = True )
355
385
visualize_curvature_directions (mesh , taubin = False )
356
386
387
+ def torus_experiment (taubin = False ):
388
+ torus_radius = 1.0
389
+ tube_radius = 0.5
390
+ mesh = o3d .geometry .TriangleMesh .create_torus (torus_radius = torus_radius , tube_radius = tube_radius , radial_resolution = 90 , tubular_resolution = 60 )
391
+ mesh .compute_vertex_normals ()
392
+ vertices = np .array (mesh .vertices )
393
+ normals = np .array (mesh .vertex_normals )
394
+ n = vertices .shape [0 ]
395
+ if taubin :
396
+ curvature_min , curvature_max , eig_min , eig_max , _ = compute_curvature_directions_taubin (mesh )
397
+ else :
398
+ curvature_min , curvature_max , eig_min , eig_max , _ = compute_curvature_directions_rusinkiewicz (mesh )
399
+
400
+
401
+ line_set_max = get_lineset (vertices , curvature_max , [1 , 0 , 0 ])
402
+ line_set_min = get_lineset (vertices , curvature_min , [0 , 1 , 0 ])
403
+ line_set_normal = get_lineset (vertices , normals , [0 , 0 , 1 ])
404
+
405
+ o3d .visualization .draw_geometries ([mesh , line_set_min , line_set_max ])
406
+
407
+ old_colors = np .array (mesh .vertex_colors )
408
+ paint = cm .get_cmap ("seismic" )
409
+ gaussian_curvature = eig_min * eig_max
410
+ print ("Gaussian curvature:\t {:.4f}\t {:.4f}" .format (gaussian_curvature .min (), gaussian_curvature .max ()))
411
+ mesh .vertex_colors = o3d .utility .Vector3dVector (paint (gaussian_curvature / 5 + 0.5 )[:,:3 ])
412
+ o3d .visualization .draw_geometries ([mesh ])
413
+ paint = cm .get_cmap ("PiYG" )
414
+ mean_curvature = 1 / 2 * (eig_min + eig_max )
415
+ print ("Mean curvature:\t \t {:.4f}\t {:.4f}" .format (mean_curvature .min (), mean_curvature .max ()))
416
+ mesh .vertex_colors = o3d .utility .Vector3dVector (paint (mean_curvature / 5 + 0.5 )[:,:3 ])
417
+ o3d .visualization .draw_geometries ([mesh ])
418
+
419
+ cosv = torus_radius - np .linalg .norm (vertices [:,:2 ], axis = 1 )
420
+ print (cosv .shape , "cosv" )
421
+ true_gaussian_curvature = cosv / (tube_radius * (torus_radius + tube_radius * cosv ))
422
+ true_mean_curvature = (torus_radius + 2 * tube_radius * cosv ) / (2 * tube_radius * (torus_radius + tube_radius * cosv ))
423
+ paint = cm .get_cmap ("seismic" )
424
+ print ("True Gaussian curvature:\t {:.4f}\t {:.4f}" .format (true_gaussian_curvature .min (), true_gaussian_curvature .max ()))
425
+ mesh .vertex_colors = o3d .utility .Vector3dVector (paint (true_gaussian_curvature / 5 + 0.5 )[:,:3 ])
426
+ o3d .visualization .draw_geometries ([mesh ])
427
+ paint = cm .get_cmap ("PiYG" )
428
+ print ("True mean curvature:\t \t {:.4f}\t {:.4f}" .format (true_mean_curvature .min (), true_mean_curvature .max ()))
429
+ mesh .vertex_colors = o3d .utility .Vector3dVector (paint (true_mean_curvature / 5 + 0.5 )[:,:3 ])
430
+ o3d .visualization .draw_geometries ([mesh ])
431
+
432
+ mesh .vertex_colors = o3d .utility .Vector3dVector (old_colors )
433
+
357
434
if __name__ == "__main__" :
358
- # # mesh = o3d.io.read_triangle_mesh("../models/bunny/reconstruction/bun_zipper.ply")
435
+ mesh = o3d .io .read_triangle_mesh ("../models/bunny/reconstruction/bun_zipper.ply" )
359
436
# #mesh = o3d.io.read_triangle_mesh("../models/csg.ply")
360
437
# mesh = mesh_from_sdf(1, 160)
361
- # mesh = center_mesh(mesh)
362
- # compare_taubin_rusinkiewicz(mesh)
363
- # exit()
438
+ mesh = center_mesh (mesh )
439
+ #mesh = o3d.geometry.TriangleMesh.create_torus(torus_radius=1.0, tube_radius=0.5, radial_resolution=90, tubular_resolution=60)
440
+ #mesh = o3d.geometry.TriangleMesh.create_sphere(radius=1.0, resolution=20)
441
+
442
+ compare_taubin_rusinkiewicz (mesh )
443
+ exit ()
364
444
parser = argparse .ArgumentParser ()
365
445
parser .add_argument (
366
446
"--source" ,
0 commit comments