# Add the igl library to the modules search path import sys, os sys.path.insert(0, os.getcwd() + "/../") import pyigl as igl from math import pi V = igl.eigen.MatrixXd() F = igl.eigen.MatrixXi() # Face barycenters B = igl.eigen.MatrixXd() # Scale for visualizing the fields global_scale = 1 extend_arrows = False; # Cross field X1 = igl.eigen.MatrixXd() X2 = igl.eigen.MatrixXd() # Bisector field BIS1 = igl.eigen.MatrixXd() BIS2 = igl.eigen.MatrixXd() # Combed bisector BIS1_combed = igl.eigen.MatrixXd() BIS2_combed = igl.eigen.MatrixXd() # Per-corner, integer mismatches MMatch = igl.eigen.MatrixXi() # Field singularities isSingularity = igl.eigen.MatrixXi() singularityIndex = igl.eigen.MatrixXi() # Per corner seams Seams = igl.eigen.MatrixXi() # Combed field X1_combed = igl.eigen.MatrixXd() X2_combed = igl.eigen.MatrixXd() # Global parametrization (with seams) UV_seams = igl.eigen.MatrixXd() FUV_seams = igl.eigen.MatrixXi() # Global parametrization UV = igl.eigen.MatrixXd() FUV = igl.eigen.MatrixXi() # Texture texture_R = igl.eigen.MatrixXuc() texture_G = igl.eigen.MatrixXuc() texture_B = igl.eigen.MatrixXuc() # Create a texture that hides the integer translation in the parametrization def line_texture(): size = 128 size2 = int(size/2) lineWidth = 3 texture_R.setConstant(size, size, 255) for i in range(0,size): for j in range(size2-lineWidth,size2+lineWidth+1): texture_R[i,j] = 0 for i in range(size2-lineWidth,size2+lineWidth+1): for j in range(0,size): texture_R[i,j] = 0 texture_G = texture_R.copy() texture_B = texture_R.copy() return (texture_R, texture_G, texture_B) def key_down(viewer, key, modifier): global extend_arrows, texture_R, texture_G, texture_B if key == ord('E'): extend_arrows = not extend_arrows if key < ord('1') or key > ord('8'): return False; viewer.data.clear() viewer.core.show_lines = False viewer.core.show_texture = False if key == ord('1'): # Cross field viewer.data.set_mesh(V, F) viewer.data.add_edges(B - global_scale*X1 if extend_arrows else B, B + global_scale*X1 , igl.eigen.MatrixXd([[1,0,0]])) viewer.data.add_edges(B - global_scale*X2 if extend_arrows else B, B + global_scale*X2 , igl.eigen.MatrixXd([[0,0,1]])) if key == ord('2'): # Bisector field viewer.data.set_mesh(V, F) viewer.data.add_edges(B - global_scale*BIS1 if extend_arrows else B, B + global_scale*BIS1 , igl.eigen.MatrixXd([[1,0,0]])) viewer.data.add_edges(B - global_scale*BIS2 if extend_arrows else B, B + global_scale*BIS2 , igl.eigen.MatrixXd([[0,0,1]])) if key == ord('3'): # Bisector field combed viewer.data.set_mesh(V, F) viewer.data.add_edges(B - global_scale*BIS1_combed if extend_arrows else B, B + global_scale*BIS1_combed , igl.eigen.MatrixXd([[1,0,0]])) viewer.data.add_edges(B - global_scale*BIS2_combed if extend_arrows else B, B + global_scale*BIS2_combed , igl.eigen.MatrixXd([[0,0,1]])) if key == ord('4'): # Singularities and cuts viewer.data.set_mesh(V, F) # Plot cuts l_count = Seams.sum() P1 = igl.eigen.MatrixXd(l_count,3) P2 = igl.eigen.MatrixXd(l_count,3) for i in range(0,Seams.rows()): for j in range(0,Seams.cols()): if Seams[i,j] != 0: P1.setRow(l_count-1, V.row(F[i,j])) P2.setRow(l_count-1, V.row(F[i,(j+1)%3])) l_count = l_count - 1 viewer.data.add_edges(P1, P2, igl.eigen.MatrixXd([[1, 0, 0]])) # Plot the singularities as colored dots (red for negative, blue for positive) for i in range(0,singularityIndex.size()): if singularityIndex[i] < 2 and singularityIndex[i] > 0: viewer.data.add_points(V.row(i),igl.eigen.MatrixXd([[1,0,0]])) elif singularityIndex[i] > 2: viewer.data.add_points(V.row(i),igl.eigen.MatrixXd([[1,0,0]])) if key == ord('5'): # Singularities and cuts, original field # Singularities and cuts viewer.data.set_mesh(V, F) viewer.data.add_edges(B - global_scale*X1_combed if extend_arrows else B, B + global_scale*X1_combed ,igl.eigen.MatrixXd([[1,0,0]])) viewer.data.add_edges(B - global_scale*X2_combed if extend_arrows else B, B + global_scale*X2_combed ,igl.eigen.MatrixXd([[0,0,1]])) # Plot cuts l_count = Seams.sum() P1 = igl.eigen.MatrixXd(l_count,3) P2 = igl.eigen.MatrixXd(l_count,3) for i in range(0, Seams.rows()): for j in range(0, Seams.cols()): if Seams[i,j] != 0: P1.setRow(l_count-1,V.row(F[i,j])) P2.setRow(l_count-1,V.row(F[i,(j+1)%3])) l_count = l_count - 1 viewer.data.add_edges(P1, P2, igl.eigen.MatrixXd([[1, 0, 0]])) # Plot the singularities as colored dots (red for negative, blue for positive) for i in range(0,singularityIndex.size()): if singularityIndex[i] < 2 and singularityIndex[i] > 0: viewer.data.add_points(V.row(i),igl.eigen.MatrixXd([[1,0,0]])) elif singularityIndex[i] > 2: viewer.data.add_points(V.row(i),igl.eigen.MatrixXd([[0,1,0]])) if key == ord('6'): # Global parametrization UV viewer.data.set_mesh(UV, FUV) viewer.data.set_uv(UV) viewer.core.show_lines = True if key == ord('7'): # Global parametrization in 3D viewer.data.set_mesh(V, F) viewer.data.set_uv(UV,FUV) viewer.core.show_texture = True if key == ord('8'): # Global parametrization in 3D with seams viewer.data.set_mesh(V, F) viewer.data.set_uv(UV_seams,FUV_seams) viewer.core.show_texture = True viewer.data.set_colors(igl.eigen.MatrixXd([[1,1,1]])) viewer.data.set_texture(texture_R, texture_B, texture_G) viewer.core.align_camera_center(viewer.data.V,viewer.data.F) return False # Load a mesh in OFF format igl.readOFF("../../tutorial/shared/3holes.off", V, F) # Compute face barycenters igl.barycenter(V, F, B) # Compute scale for visualizing fields global_scale = .5*igl.avg_edge_length(V, F) # Contrain one face b = igl.eigen.MatrixXi([[0]]) bc = igl.eigen.MatrixXd([[1,0,0]]) # Create a smooth 4-RoSy field S = igl.eigen.MatrixXd() igl.comiso.nrosy(V,F,b,bc,igl.eigen.MatrixXi(),igl.eigen.MatrixXd(),igl.eigen.MatrixXd(),4,0.5,X1,S) # Find the the orthogonal vector B1 = igl.eigen.MatrixXd() B2 = igl.eigen.MatrixXd() B3 = igl.eigen.MatrixXd() igl.local_basis(V,F,B1,B2,B3) X2 = igl.rotate_vectors(X1, igl.eigen.MatrixXd.Constant(1,1,pi/2), B1, B2) gradient_size = 50 iterations = 0 stiffness = 5.0 direct_round = False # Always work on the bisectors, it is more general igl.compute_frame_field_bisectors(V, F, X1, X2, BIS1, BIS2) # Comb the field, implicitly defining the seams igl.comb_cross_field(V, F, BIS1, BIS2, BIS1_combed, BIS2_combed) # Find the integer mismatches igl.cross_field_missmatch(V, F, BIS1_combed, BIS2_combed, True, MMatch) # Find the singularities igl.find_cross_field_singularities(V, F, MMatch, isSingularity, singularityIndex); # Cut the mesh, duplicating all vertices on the seams igl.cut_mesh_from_singularities(V, F, MMatch, Seams) # Comb the frame-field accordingly igl.comb_frame_field(V, F, X1, X2, BIS1_combed, BIS2_combed, X1_combed, X2_combed) # Global parametrization igl.comiso.miq(V, F, X1_combed, X2_combed, MMatch, isSingularity, Seams, UV, FUV, gradient_size, stiffness, direct_round, iterations, 5, True, True); # Global parametrization (with seams, only for demonstration) igl.comiso.miq(V, F, X1_combed, X2_combed, MMatch, isSingularity, Seams, UV_seams, FUV_seams, gradient_size, stiffness, direct_round, iterations, 5, False); # Plot the mesh viewer = igl.viewer.Viewer() # Replace the standard texture with an integer shift invariant texture (texture_R, texture_G, texture_B) = line_texture() # Plot the original mesh with a texture parametrization key_down(viewer,ord('7'),0) # Launch the viewer viewer.callback_key_down = key_down viewer.launch()