surfaces/surf/geometry.py

266 lines
8.5 KiB
Python
Raw Normal View History

import pprint
'''
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
- Winged-edge
- Half-edge
- Quad-edge
- Corner-tables
- Vertex-vertex
- Face-vertex
We have chosen to use a winged-edge style mesh for our purpopses.
'''
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)
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 __eq__(self, other):
if(self.x == other.x and self.y == other.y and self.z == other.z):
return True
else:
return False
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 other + self
# return self.__add__(other)
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__
other = float(other)
return Vertex(self.x / other, self.y / other, self.z / other)
def __neg__(self):
return Vertex(-self.x, -self.y, -self.z)
class Edge(object):
'''
'''
def __init__(self):
self.vertices = []
self.faces = []
self.edges = []
self.__edge_vertex = None
self.__sub_edges = []
def neighborFace(self, neighborFace):
if neighborFace == self.faces[0]:
return self.faces[1]
else:
return self.faces[0]
@property
def mid_point(self):
return sum(self.vertices, Vertex()) / len(self.vertices)
@property
def sub_edges(self):
if not self.__sub_edges:
self.__sub_edges = [Edge(), Edge()]
self.__sub_edges[0].vertices = [self.vertices[0], self.edge_vertex]
self.__sub_edges[1].vertices = [self.edge_vertex, self.vertices[1]]
return self.__sub_edges
@property
def edge_vertex(self):
'''
Set each edge vertices to be the average of the two neighboring
face vertices and its two original end vertices.
'''
if not self.__edge_vertex:
# 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.__edge_vertex = Vertex(x, y, z)
self.__edge_vertex.edges.extend(self.__sub_edges)
return self.__edge_vertex
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.__interior_edges = []
self.__sub_faces = []
@property
def centroid(self):
if not self.__centroid:
# gather all face vertex coords
face_vertices = list(set([vertex
for edge in self.edges for vertex in edge.vertices]))
xs = [vertex.x for vertex in face_vertices]
ys = [vertex.y for vertex in face_vertices]
zs = [vertex.z for vertex in face_vertices]
# 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
@property
def sub_faces(self):
self.__setupSubDivisions()
return self.__sub_faces
@property
def interior_edges(self):
self.__setupSubDivisions()
return self.__interior_edges
def __setupSubDivisions(self):
'''
v0 ev0 v1
*------e0-----*
| | |
| | |
ev3 e|11----f5----e|1 ev1
| | |
| | |
*------e2-----*
v3 ev2 v2
'''
if not self.__sub_faces:
# create empty sub_faces that will be filled with edge references
# below
# these need to at least exist so the interior edges have
# something to reference
self.__sub_faces = [Face() for edge in self.edges]
if not self.__interior_edges:
# set up empty edge objects to be filled below
self.__interior_edges = [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 edge_vertex
self.__interior_edges[index].vertices = [
self.edges[index].edge_vertex, self.centroid
]
# wing edges are the current edge's sub_edges (ordered same as
# vertex order) and the prev and next interior edges
self.__interior_edges[index].edges = [
self.edges[index].sub_edges[0],
self.edges[index].sub_edges[1],
self.__interior_edges[prevIndex],
self.__interior_edges[nextIndex]
]
# edge faces are the new sub_faces (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.__interior_edges[index].faces = [
self.__sub_faces[index],
self.__sub_faces[nextIndex]
]
# now reference the current edge back into the faces,
# and the edge.sub_edges, and the edge.edge_vertex
# 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.__sub_faces[index].edges = [
self.edges[index].sub_edges[0],
self.__interior_edges[index],
self.__interior_edges[prevIndex],
self.edges[prevIndex].sub_edges[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].edge_vertex.edges.append(
self.__interior_edges[index])
self.edges[index].sub_edges[0].faces.append(
self.__sub_faces[index])
self.edges[index].sub_edges[0].faces.append(
self.__sub_faces[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__