surfaces/surf/geometry.py
2012-03-19 20:38:40 -06:00

287 lines
12 KiB
Python
Executable File

from __future__ import division
import pprint
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:
Face-vertex meshes: A simple list of vertices, and a set of polygons that vertex to the vertices it uses.
Winged-edge meshes, in which each edge vertices to two vertices, two faces, and the four (clockwise and counterclockwise) edges that touch it. Winged-edge meshes allow constant time traversal of the surface, but with higher storage requirements.
Half-edge meshes: Similar to winged-edge meshes except that only half the edge traversal information is used.
Quad-edge meshes, which store edges, half-edges, and vertices without any reference to polygons. The polygons are implicit in the representation, and may be found by traversing the structure. Memory requirements are similar to half-edge meshes.
Corner-tables, which store vertices in a predefined table, such that traversing the table implicitly defines polygons. This is in essence the "triangle fan" used in hardware graphics rendering. The representation is more compact, and more efficient to retrieve polygons, but operations to change polygons are slow. Furthermore, corner-tables do not represent meshes completely. Multiple corner-tables (triangle fans) are needed to represent most meshes.
Vertex-vertex meshes: A "VV" mesh represents only vertices, which vertex to other vertices. Both the edge and face information is implicit in the representation. However, the simplicity of the representation allows for many efficient operations to be performed on meshes.
Face-vertex meshes represent an object as a set of faces and a set of vertices. This is the most widely used mesh representation, being the input typically accepted by modern graphics hardware.
v4 v5
*-----e8-----*
| |
| |
e|4 f4 e|5
| |
v4 v|0 v|1 v5
*-----e4-----*-----e0-----*-----e5-----*
| | | |
| | | |
e11| f3 e|3 f0 e|1 f1 e|9
| | | |
| | | |
*-----e7-----*-----e2-----*-----e6-----*
v7 v|3 v|2 v6
| |
e|7 f2 e|6
| |
v|7 v|6
*-----e10----*
| |
| |
e|11 f5 e|9
| |
| |
*-----e8-----*
v4 v5
v0 - <0,1,0>
v1 - <1,1,0>
v2 - <1,0,0>
v3 - <0,0,0>
v4 - <0,1,1>
v5 - <1,1,1>
v6 - <1,0,1>
v7 - <0,0,1>
face list
f0 - e0, e1, e2, e3
f1 - e1, e5, e9, e6
f2 - e2, e6, e10, e7
f3 - e4, e3, e7, e11
f4 - e8, e5, e0, e4
f5 - e10, e9, e8, e11
winged edges ordered by face, then by vertex reference
edge list
e0 - v0, v1; f0, f4; e3, e1, e4, e5
e1 - v1, v2; f0, f1; e0, e2, e5, e6
e2 - v3, v2; f0, f2; e3, e7, e1, e6
e3 - v0, v3; f3, f0; e4, e7, e0, e2
e4 - v4, v0; f3, f4; e11, e3, e0 e8
e5 - v5, v1; f4, f1; e8, e0, e9, e1
e6 - v2, v6; f1, f2; e1, e9, e2, e10
e7 - v7, v3; f3, f2; e11, e3, e10, e2
e8 - v4, v5, f4, f5; e4, e5, e11, e9
e9 - v5, v6; f1, f5; e5, e6, e8, e10
e10 - v7, v6; f2, f5; e7, e6, e11, e9
e11 - v4, v7; f3, f5; e4, e7, e8, 10
vertex list
v0 - e0, e3, e4
v1 - e0, e5, e1
v2 - e1, e6, e2
v3 - e2, e7, e3
v4 - e4, e11, e8
v5 - e5, e9, e8
v6 - e2, e9, e10
v7 - e7, e10, e11
'''
class Vertex(object):
'''
A vertex is a position along with other information such as color, normal vector and texture coordinates.
'''
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
self.edges = []
def __repr__(self):
return "<%.2f, %.2f, %.2f>" % (self.x, self.y, self.z)
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)
def __radd__(self, other):
return self.__add__(other)
def __mul__(self, other):
# for now just assume type(other) = int or float
return Vertex(self.x * other, self.y * other, self.z * 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)
class Edge(object):
'''
'''
def __init__(self):
self.vertices = []
self.faces = []
self.edges = []
self.__edgeVertex = None
self.__subEdges = []
def neighborFace(self, neighborFace):
if neighborFace == self.faces[0]:
return self.faces[1]
else:
return self.faces[0]
def __getMidPoint(self):
return sum(self.vertices) / len(self.vertices)
midPoint = property(fget=__getMidPoint)
def __getSubEdges(self):
if not self.__subEdges:
self.__subEdges = [Edge(), Edge()]
self.__subEdges[0].vertices = [self.vertices[0], self.edgeVertex]
self.__subEdges[1].vertices = [self.edgeVertex, self.vertices[1]]
return self.__subEdges
subEdges = property(fget=__getSubEdges)
def __getEdgeVertex(self):
'''
Set each edge vertices to be the average of the two neighboring
face vertices and its two original end vertices.
'''
if not self.__edgeVertex:
# two neighboring face vertices:
neighboringFaceVertices = [face.centroid for face in self.faces]
neighboringFaceVertices.extend(self.vertices)
xs = [vertex.x for vertex in neighboringFaceVertices]
ys = [vertex.y for vertex in neighboringFaceVertices]
zs = [vertex.z for vertex in neighboringFaceVertices]
x = sum(xs)/len(xs)
y = sum(ys)/len(ys)
z = sum(zs)/len(zs)
self.__edgeVertex = Vertex(x, y, z)
self.__edgeVertex.edges.extend(self.__subEdges)
return self.__edgeVertex
edgeVertex = property(fget=__getEdgeVertex)
def __averageVertices(self, vertices):
return
class Face(object):
'''
A face is a closed set of edges, in which a triangle face has three edges, and a quad face has four edges.
'''
def __init__(self):
self.edges = []
self.__centroid = None
self.__interiorEdges = []
self.__subFaces = []
def __getCentroid(self):
if not self.__centroid:
# gather all face vertex coords
faceVertices = list(set([vertex for edge in self.edges for vertex in edge.vertices]))
xs = [vertex.x for vertex in faceVertices]
ys = [vertex.y for vertex in faceVertices]
zs = [vertex.z for vertex in faceVertices]
# average each vertex component
x = sum(xs) / len(xs)
y = sum(ys) / len(ys)
z = sum(zs) / len(zs)
self.__centroid = Vertex(x, y, z)
return self.__centroid
centroid = property(fget=__getCentroid)
def __getSubFaces(self):
self.__setupSubDivisions()
return self.__subFaces
subFaces = property(fget=__getSubFaces)
def __getInteriorEdges(self):
self.__setupSubDivisions()
return self.__interiorEdges
interiorEdges = property(fget=__getInteriorEdges )
def __setupSubDivisions(self):
'''
v0 ev0 v1
*------e0-----*
| | |
| | |
ev3 e|11----f5----e|1 ev1
| | |
| | |
*------e2-----*
v3 ev2 v2
'''
if not self.__subFaces:
# create empty subFaces that will be filled with edge references below
# these need to at least exist so the interior edges have something to reference
self.__subFaces = [Face() for edge in self.edges]
if not self.__interiorEdges:
# set up empty edge objects to be filled below
self.__interiorEdges = [Edge() for edge in self.edges]
# each interior edge connects the exterior edge vertex (mid-point) to the faceVertex (centroid)
for index in range(len(self.edges)):
prevIndex = (index - 1) % len(self.edges)
nextIndex = (index + 1) % len(self.edges)
# end vertices are face centroid and currEdge edgeVertex
self.__interiorEdges[index].vertices = [self.edges[index].edgeVertex,
self.centroid]
# wing edges are the current edge's subEdges (ordered same as vertex order) and the prev and next interior edges
self.__interiorEdges[index].edges = [self.edges[index].subEdges[0],
self.edges[index].subEdges[1],
self.__interiorEdges[prevIndex],
self.__interiorEdges[nextIndex]]
# edge faces are the new subFaces (current and next faces), the current will be define below
# and the next will be defined on the next iteration (or already defined on the last iteration)
self.__interiorEdges[index].faces = [self.__subFaces[index],
self.__subFaces[nextIndex]]
# now reference the current edge back into the faces,
# and the edge.subEdges, and the edge.edgeVertex
# current subFace (same index as current interior edge)
# set its edges to reference the same edges used to setup the interior edge
# order will be pretty important on these steps...
self.__subFaces[index].edges = [self.edges[index].subEdges[0],
self.__interiorEdges[index],
self.__interiorEdges[prevIndex],
self.edges[prevIndex].subEdges[1]]
# just set one of the vertex edges, the other belongs to another face and will get added when that face is run
self.edges[index].edgeVertex.edges.append(self.__interiorEdges[index])
self.edges[index].subEdges[0].faces.append(self.__subFaces[index])
self.edges[index].subEdges[0].faces.append(self.__subFaces[index])
class Polygon(object):
'''
Face splitting should happend on the polygon level(?). It doesn't make sense to split just one face since
it needs to average vertices with all adjoinging faces
'''
def __init__(self, v=None, e=None, f=None):
self.vertices = v or []
self.edges = e or []
self.faces = f or []
def __unicode__(self):
d = {
'vertices': self.vertices,
'edges': self.edges,
'faces': self.faces,
}
return pprint.pformat(d)
__str__ = __unicode__
__repr__ = __unicode__