from collections import defaultdict from surf.geometry import PolygonMesh, centroid def refine(mesh): new_vertices = list(mesh.vertices) f_vert_offset = len(new_vertices) edge_vids_for_face = defaultdict(list) face_ids_for_v = defaultdict(list) # TODO: not completely populated yet: new_edges = [] # TODO: must be populated: new_faces = [] # For each face, add a face point for cur_face_id, cur_face in enumerate(mesh.faces): # Set each face point to be the centroid of all original points for the # respective face. # here we have already created a list that is a copy of the original # verts. we append the new face points to that same list, and simply # keep track of offsets into the new vert list ... see e_vert_offset # below new_face_point = mesh.centroid(cur_face) new_vertices.append(new_face_point) for i in cur_face: face_ids_for_v[i].append(cur_face_id) e_vert_offset = len(new_vertices) # For each edge, add an edge point. for old_edge_id, old_edge in enumerate(mesh.edges): # make mapping from edge -> new_face_vert for later old_face_ids_for_old_edge = mesh.faces_for_edge[old_edge_id] for fid in old_face_ids_for_old_edge: edge_vids_for_face[fid].append(old_edge_id) tmp_verts = [] # Set each edge point to be the average of the two neighbouring, (very # recently calculated) face points ... tmp_verts.extend([new_vertices[f + f_vert_offset] for f in old_face_ids_for_old_edge]) # ... and its two original endpoints. tmp_verts.extend([mesh.vertices[vid] for vid in old_edge]) # centroid == average new_vertices.append(centroid(tmp_verts)) # For each face point, add an edge connecting the new face point to each # new edge point new_face_vert_ids = new_vertices[f_vert_offset:e_vert_offset] for trunc_vid in xrange(len(new_face_vert_ids)): overall_vid = f_vert_offset + trunc_vid for edge_vid in edge_vids_for_face[trunc_vid]: new_edges.append([edge_vid, overall_vid]) # For each original point P, move the original point to a new location # Here we mimic the F, R, and P spelling in the wiki article for old_vid in xrange(f_vert_offset): # take the average F of all n face points for faces touching P ... F_verts = [] for old_fid in mesh.faces_for_vert[old_vid]: F_verts.append(new_vertices[f_vert_offset + old_fid]) F = centroid(F_verts) # and take the average R of all n edge midpoints for edges touching P # wiki is wrong ... it should be the edge points, not midpoints ... edges = [mesh.edges[eid] for eid in mesh.edges_for_vert[old_vid]] e_verts = [] for edge in edges: e_verts.extend([mesh.vertices[vid] for vid in edge]) R = centroid(e_verts) # where each edge midpoint is the average of its two endpoint vertices. # **Move** each original point to the point (or add it to new_verts) new_vertex_point = (F + 2 * R + (len(edges) - 3) * new_vertices[old_vid]) / len(edges) new_vertices[old_vid] = new_vertex_point # Connect each new Vertex point to the new edge points of all original # edges incident on the original vertex. for eid in mesh.edges_for_vert[old_vid]: new_edges.append([old_vid, eid]) return PolygonMesh(vertices=new_vertices, edges=new_edges, faces=new_faces) if __name__ == '__main__': import sys import json from surf.subd.cc import refine input_file_name = sys.argv[1] cube = json.load(open(input_file_name, 'r')) p = PolygonMesh(**cube) q = refine(p) print q