Skip to content

Instantly share code, notes, and snippets.

@yuancu
Last active February 1, 2021 13:15
Show Gist options
  • Save yuancu/d1b08bf3384e28dfb72988ba670f869c to your computer and use it in GitHub Desktop.
Save yuancu/d1b08bf3384e28dfb72988ba670f869c to your computer and use it in GitHub Desktop.
Implementation of some helper functions
# Return a list of vertices
def get_vertices(context):
selected_object = context.active_object
if selected_object is None:
raise Exception('No object selected: please select the object to deform')
return selected_object.data.vertices
# Returns a list of triangles of vertex indices (you need to perform simple triangulation)
def get_faces(context):
selected_object = context.active_object
if selected_object is None:
raise Exception('No object selected: please select the object to deform')
return selected_object.data.polygons
# EFFICIENTLY returns the 1-ring (a list of vertex indices) for a vertex index
def neighbor_indices(vertex_index, vertices, faces):
neighbors = []
obj = bpy.context.active_object
me = obj.data
bm = bmesh.new()
bm.from_mesh(me)
# make bm.verts indexable
if hasattr(bm.verts, "ensure_lookup_table"):
bm.verts.ensure_lookup_table()
v = bm.verts[vertex_index]
for e in v.link_edges:
neighbors.append(e.other_vert(v).index)
bm.free()
return neighbors
# Return the sparse diagonal weight matrix W
def weights(vertices, faces):
"""
Compute weights by iterating through faces.
for every edge in every face:
if weights[edge] isn't assigned:
assign cot of respective angle as its weight (i.e, cot(A) for edge a)
else, this means its weight is assgined in another face as cot:
assign weights[edge] * 0.5 + new cot as its weight
"""
vert_num = len(vertices)
w = dict() # use a dict to store weights in {(i, j): w}
for face in faces:
for i, j in face.edge_keys:
other = (set(face.vertices) ^ set((i, j))).pop()
# make i always be the smaller index
i, j = i, j if i < j else j, i
if w.get((i, j), 0) == 0:
w[(i, j)] = compute_cot(vertices[i].co - vertices[other].co,
vertices[j].co -vertices[other].co )
else:
w[(i, j)] = w[(i, j)] * 0.5 + compute_cot(vertices[i].co - vertices[other].co,
vertices[j].co - vertices[other].co) * 0.5
# convert dict to list
w_list = []
for k,v in w:
w_list.append((k[0], k[1], v))
# force an ascending edge order
w_list = sorted(w_list, key = lambda w_list: (w_list[0], w_list[1]))
return ddm.Sparse_Matrix(w_list, vert_num, vert_num)
def compute_cot(v1, v2):
dotprod = v1 * v2
cos_v1v2 = dotprod / (v1.length * v2.length)
cot_v1v2 = cos_v1v2 / np.sqrt(1 - cos_v1v2**2)
return cot_v1v2
# Returns g matrix of shape |E| × 3
# Note: E here represents all edges
# Order of edges is: acend, first by vertex index, second by neighbor index
def compute_g(vertices, R_list):
g = []
for v_i, R_i in zip(vertices, R_list):
neighbors = neighbor_indices(v_i.index, None, None)
# force an order of edges connected to a vertex
neighbors.sort()
for n in neighbors:
# force an order where only records g_ij that i < j
if v_i.index >= n:
continue
v_j = vertices[n]
R_j = R_list[n]
g_ij = (v_i.co - v_j.co) * R_i
g_ji = (v_j.co - v_i.co) * R_j
g.append((g_ij - g_ji) / 2)
if bpy.context.active_object is not None:
assert len(g) == len(bpy.context.active_object.data.edges), 'length of g should be equal to number of edges'
return Matrix(np.array(g))
# Returns a sparse diagonal matrix of type scipy.sparse.coo_matrix where indices in index list is set to one
# if flip is set, it's a diagonal matrix where indices not set in index_list are one
def convert_to_mask(index_list, length, flip=False):
if flip:
mask1D = np.zeros(length, dtype=np.int)
mask1D[index_list] = 1
index_list = np.where(mask1D == 0)[0].tolist()
len_index = len(index_list)
mask = sp.coo_matrix((np.ones(len_index), (index_list, index_list)), shape=(length, length))
return mask
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment