Spaces:
Sleeping
Sleeping
import matplotlib.pyplot as plt | |
import numpy as np | |
NORM = np.linalg.norm | |
def get_neibour(id,f): | |
neibour_list = [] | |
for i in id: | |
neibour = set(f[np.where(f==i)[0]].flatten()) | |
if i in neibour: | |
neibour.remove(i) | |
neibour = list(neibour) | |
neibour_list.extend(neibour) | |
return neibour_list | |
def get_adj(v,f,level): | |
adj = [] | |
for i,vt in enumerate(v): | |
neibour_list = [i] | |
for j in range(level): | |
neibour_list.extend( get_neibour(neibour_list,f) ) | |
neibour_list.remove(i) | |
adj.append(neibour_list) | |
return adj | |
def get_edges(v,f): | |
edges = [] | |
adj = [] | |
for i,vt in enumerate(v): | |
neibour = get_neibour([i],f) | |
adj.append(neibour) | |
edges.append( np.sort(np.vstack(( np.repeat(i,len(neibour)),neibour)).T,axis=1) ) | |
return edges,adj | |
def find_clockwise_nearest(vector_a,vector_b_arr,id_list): | |
""" | |
This function find the smallest clockwise angle between vector_a and vector in vector_b_arr | |
Args: | |
1. vector_a | |
2. vector_b_arr , array of vectors | |
3. id_list: id of verts in vert_b_arr | |
Return: | |
1 . find the vector b in vector b array that has the smallest angle between vector a | |
and return the id of the point that consist vector b | |
""" | |
ang = np.arctan2(vector_a[0]*vector_b_arr[:,1]-vector_a[1]*vector_b_arr[:,0],vector_a[0]*vector_b_arr[:,0]+vector_a[1]*vector_b_arr[:,1]) | |
# id_list = vector_b_arr[:,2] | |
positive_id = np.where(ang > 1e-12)[0] # when ang == 0, means vector_a find it self. using 1e-12 to aviod float precision error, | |
if positive_id.shape[0] > 0 : | |
# e.g angle [-20,20,30] we wanna get 20 degree, rather than -20 degree, | |
# because -20 degree means the vector has neg direction compare to vector_a | |
clockwise_nearest = positive_id[np.argmin(ang[positive_id])] | |
else: | |
negative_id = np.where(ang < 0)[0] | |
clockwise_nearest = negative_id[np.argmin(ang[negative_id])] | |
next_pt_id = int(id_list[clockwise_nearest]) | |
return next_pt_id | |
def find_inters(pv,rv,qv,sv): | |
# find intersections https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect | |
# p is one vector | |
# q is a set of vectors | |
cross = rv[0]*sv[:,1]-rv[1]*sv[:,0] | |
cross [cross == 0] = 1 | |
qv_minus_pv = qv - pv | |
t = (qv_minus_pv[:,0]*sv[:,1]-qv_minus_pv[:,1]*sv[:,0]) / cross | |
u = (qv_minus_pv[:,0]*rv[1]-qv_minus_pv[:,1]*rv[0]) / cross | |
line_has_inters = np.where( ( (t < 1-1e-6) & (t>1e-6)) & ( (u<1-1e-6) & (u>1e-6)) & (cross !=0) )[0] | |
if line_has_inters.shape[0] !=0: | |
intersections = pv + t[line_has_inters].reshape(-1,1) * rv | |
return intersections,line_has_inters | |
else: | |
return None,None | |
def tracing_outline_robust(verts,faces): | |
""" | |
this is the version not require tree building, it calculates all intersections from all edges | |
args: | |
1.N X 2 verts | |
2.M X 3 faces | |
Return: | |
outline points coordinates | |
""" | |
start_id = np.argmin(verts[:,0]) | |
center_pt = verts[start_id] | |
pre_pt = center_pt.copy() | |
pre_pt[0] = pre_pt[0] - 1 #start from left | |
next_id = start_id | |
break_c = verts.shape[0] | |
i = 0 | |
edges_list,adj = get_edges(verts,faces.astype('int')) | |
edge_arr = np.vstack(edges_list) | |
edge_arr = edge_arr.astype('int') | |
out_points = [] | |
connect_id = [] | |
out_id = [] | |
out_id.append(next_id) | |
out_points.append(verts[next_id]) | |
while True and i < break_c: | |
i += 1 | |
if len(connect_id) == 0: | |
connect_id = adj[next_id] | |
vector_a = pre_pt - center_pt | |
vector_b_arr = verts[connect_id] - center_pt | |
next_id = find_clockwise_nearest(vector_a,vector_b_arr,connect_id) | |
if next_id == start_id: | |
break | |
pre_pt = center_pt | |
center_pt = verts[next_id] | |
arr_q = verts[edge_arr[:,0]] | |
arr_r = verts[edge_arr[:,1]] - arr_q | |
inters,inter_edge_id = find_inters(center_pt,pre_pt-center_pt,arr_q,arr_r) | |
if inters is not None: | |
nearest = np.argmin(NORM(inters - pre_pt,axis=1)) | |
center_pt = inters[nearest] | |
connect_id = np.ndarray.tolist(edge_arr[inter_edge_id[nearest]]) | |
connect_id.append(next_id) | |
inters = None | |
else: | |
connect_id = [] | |
inters = None | |
out_id.append(next_id) | |
out_points.append(center_pt) | |
return np.asarray(out_points),out_id | |
if __name__ == '__main__': | |
import trimesh # for reading mesh only | |
mesh_path = 'bunny.ply' | |
mesh = trimesh.load_mesh(mesh_path, process=False) | |
v,f = mesh.vertices,mesh.faces | |
v_2d = v[:,:2] | |
points,ids = tracing_outline_robust(v_2d,f) # not doing any projection,just simply take the verts's x and y . | |
# visualizing points tracing offline | |
fig= plt.figure(figsize=(9,9)) | |
plt.triplot(v_2d[:, 0], v_2d[:, 1], f) | |
plt.plot(points[:,0],points[:,1]) | |
plt.plot(points[:,0],points[:,1],'r.') | |
plt.show() |