You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('') and can be up to 35 characters long.
240 lines
7.2 KiB
240 lines
7.2 KiB
from __future__ import division


import json




'''


http://en.wikipedia.org/wiki/Polygon_mesh




Polygon meshes may be represented in a variety of ways, using different methods


to store the vertex, edge and face data. These include:


 Facevertex


 Wingededge


 Halfedge


 Quadedge


 Cornertables


 Vertexvertex


 Facevertex




We have chosen to use a wingededge style mesh for our purpopses.




'''




__all__ = [


'Vertex',


'PolygonMesh',


]






def cross(a, b):


i = a.y * b.z  a.z * b.y


j = a.z * b.x  a.x * b.z


k = a.x * b.y  a.y * b.x


return Vertex(i, j, k)






def centroid(verts):


xs = [v.x for v in verts]


ys = [v.y for v in verts]


zs = [v.z for v in verts]




# average each vertex component


x = sum(xs) / len(xs)


y = sum(ys) / len(ys)


z = sum(zs) / len(zs)




return Vertex(x, y, z)






class Vertex(list):


'''


A vertex is a position along with other information such as color, normal


vector and texture coordinates.




For the sake of our algorithms, we will only worry about the (x, y, z)


float positions. Eventually we will also keep track of weights.


'''




def __init__(self, *args, **kwargs):


""" The constructor supports the following formats:


>>> Vertex([3, 1, 4])


V[3, 1, 4]


>>> Vertex(2, 7, 2)


V[2, 7, 2]


>>> Vertex()


V[0, 0, 0]


"""


if len(args) == 0:


a = [[0, 0, 0]]


a.extend(args)


super(Vertex, self).__init__(*a, **kwargs)


elif len(args) == 1:


if len(args[0]) != 3:


raise TypeError("Only support 3D at the moment")


super(Vertex, self).__init__(*args, **kwargs)


elif len(args) == 3:


super(Vertex, self).__init__(args, **kwargs)




@property


def x(self):


return self[0]




@property


def y(self):


return self[1]




@property


def z(self):


return self[2]




def __eq__(self, other):


if(self.x == other.x and self.y == other.y and self.z == other.z):


return True


else:


return False




def __add__(self, other):


# for now just assume type(other) = Vertex... bad, I know


return Vertex(self.x + other.x, self.y + other.y, self.z + other.z)




__iadd__ = __add__




def __radd__(self, other):


return other + self




def __mul__(self, other):


if isinstance(other, Vertex):


return cross(self, other)


elif isinstance(other, (float, int)):


return Vertex(self.x * other, self.y * other, self.z * other)


else:


raise TypeError("{0} has an unexpected type: {1}".format(


other, type(other)))




def __rmul__(self, other):


return self.__mul__(other)




def __div__(self, other):


# same assumption as __mult__


return Vertex(self.x / other, self.y / other, self.z / other)


__truediv__ = __div__




def __neg__(self):


return Vertex(self.x, self.y, self.z)




def __unicode__(self):


return 'V{0}'.format([self.x, self.y, self.z])




__str__ = __unicode__


__repr__ = __unicode__






class PolygonMesh(object):


'''


A polygon object is a collection of the following lists:




 a list containing the 3space vertex information


 a list containing the edge indices ([0, 1] means first and second


elements of the vertices list)


 a list of the faces (indices of the vertices in a given face)




 connectivity information (eventually will be calculated given the verts,


edges, and faces)


'''




def __init__(self, vertices, faces, edges=None, **kwargs):


self.vertices = [Vertex(*v) for v in vertices]


self.faces = faces


self.edges = edges




# the strategy for the following members involves lazyinstantiating


# them if they weren't passing them in:


self._faces_for_edge = kwargs.get('faces for edge', None)


self._edges_for_face = kwargs.get('edges for face', None)


self._edges_for_vert = kwargs.get('edges for vert', None)


self._faces_for_vert = kwargs.get('faces for vert', None)




self._edge_map = None




@property


def faces_for_edge(self):


"""returns a list of face indices for a given edge index.




Intended to be used in the following way:




>>> # vs, es, fs, ffe, eff, efv, ffv from blender or similar


>>> mesh = Polygon(vs, es, fs, ffe, eff, efv, ffv)


>>> mesh.faces_for_edge[0]


[0, 1]


>>> [self.face[i] for i in mesh.faces_for_edge[1]]


[[...], [...], ...]




where 0 and 1 are indices into the face list


"""


if self._faces_for_edge is None:


self._set_up_face_edge_connectivity()


return self._faces_for_edge




@property


def edges_for_face(self):


"""returns a list of edge indices for a given face index."""


if self._edges_for_face is None:


self._set_up_face_edge_connectivity()


return self._edges_for_face




@property


def edges_for_vert(self):


"""returns a list of edge indices for a given vertex index."""


if self._edges_for_vert is None:


self._edges_for_vert = [[] for i in range(len(self.vertices))]


for i, edge in enumerate(self.edges):


for vid in edge:


self._edges_for_vert[vid].append(i)


return self._edges_for_vert




@property


def faces_for_vert(self):


"""returns a list of face indices for a given vert index."""


if self._faces_for_vert is None:


self._faces_for_vert = [[] for i in range(len(self.vertices))]


for i, face in enumerate(self.faces):


for vid in face:


self._faces_for_vert[vid].append(i)


return self._faces_for_vert




def _make_edge_map(self):


self._edge_map = {}


for i, edge in enumerate(self.edges):


self._edge_map[tuple(sorted(edge))] = i




def _set_up_face_edge_connectivity(self):


if self._edge_map is None:


self._make_edge_map()


self._edges_for_face = [[] for i in range(len(self.faces))]


self._faces_for_edge = [[] for i in range(len(self.edges))]


for face_id, face in enumerate(self.faces):


for i in range(len(face) 1):


cur_edge = tuple(sorted([face[i], face[i+1]]))


edge_id = self._edge_map[cur_edge]


self._edges_for_face[face_id].append(edge_id)


self._faces_for_edge[edge_id].append(face_id)


cur_edge = tuple(sorted([face[1], face[0]]))


edge_id = self._edge_map[cur_edge]


self._edges_for_face[face_id].append(edge_id)


self._faces_for_edge[edge_id].append(face_id)




def __unicode__(self):


# TODO: perhaps also add connectivity here?


d = {


'vertices': self.vertices,


'edges': self.edges,


'faces': self.faces,


'faces_for_vert': self.faces_for_vert,


'edges_for_vert': self.edges_for_vert,


}


return json.dumps(d)




__str__ = __unicode__


__repr__ = __unicode__




def centroid(self, vert_ids):


verts = [self.vertices[vid] for vid in vert_ids]


return centroid(verts)


