Source code for gloopy.shapes.shape

from itertools import repeat

from ..geom.vector import Vector
from ..color import Color



def add_vertex(vertices, new_vert):
[docs] ''' Modifies `vertices` in-place by appending the given `new_vert`. Returns the index number of the new vertex. Loads of Shape-modifying algorithms seem to need this function. Can't make it a method because they often haven't constructed the shape instance yet. ''' vertices.append(new_vert) return len(vertices) - 1 class Face(object):
[docs] ''' A single flat face that forms part of a Shape. Attributes are the params to the constructor below, plus: `normal`: A Vector, perpendicular to the face .. function:: __init__(indices, color, shape, source='unknown') `indices`: a list of int indices into the parent shape's vertex list `color`: an instance of Color `shape`: a reference to the parent Shape `source`: a descriptive string. These can be used when writing algorithms that modify shapes, to select certain faces to operate on. .. function:: __getitem__(n) Return the nth index, as an integer. .. function:: __iter__() Iterate through `indices` .. function:: __len__() Return the length of `indices` ''' def __init__(self, indices, color, shape, source='unknown'):
[docs] self.indices = indices self.color = color self.shape = shape self.source = source self.normal = self.get_normal() def __getitem__(self, index):
[docs] return self.indices[index % len(self.indices)] def __iter__(self):
[docs] return self.indices.__iter__() def __len__(self):
[docs] return len(self.indices) def get_normal(self):
[docs] ''' Return the unit normal vector at right angles to this face. Note that the direction of the normal will be reversed if the face's winding is reversed. ''' v0 = self.shape.vertices[self.indices[0]] v1 = self.shape.vertices[self.indices[1]] v2 = self.shape.vertices[self.indices[2]] a = v0 - v1 b = v2 - v1 normal = b.cross(a).normalized() return normal @property
def centroid(self):
[docs] ''' Warning: Not an accurate centroid, just the mean vertex position ''' return sum( [self.shape.vertices[i] for i in self], Vector.Origin ) / len(self.indices) class Shape(object):
[docs] ''' Defines a polyhedron, a 3D shape with flat faces and straight edges. .. function:: __init__(vertices, faces, colors, name='unknown') `vertices`: a list of Vector points in 3D space, relative to the shape's center point. `faces`: a list of faces, where each face is a list of integer indices into the vertices list. The referenced vertices of a single face must form a coplanar ring defining the face's edges. Duplicate indices do not have to be given at the start and end of each face, the closed loop is implied. `colors`: a single Color which is applied to every face, or a sequence of colors, one for each face. `name`: the 'source' attribute to be applied to each face. See the source for factory functions like :func:`~gloopy.shapes.cube.Cube` for examples of constructing Shapes. ''' def __init__(self, vertices, faces, colors, name='unknown'):
[docs] # sanity checks len_verts = len(vertices) for face in faces: assert len(face) >= 3 for index in face: assert 0 <= index < len_verts # convert vertices from tuple to Vector if required if len(vertices) > 0 and not isinstance(vertices[0], Vector): vertices = [Vector(*v) for v in vertices] # if color is a single color, then convert it to a sequence of # identical colors, one for each face if isinstance(colors, Color): colors = repeat(colors) self.vertices = vertices self.faces = [ Face(face, color, self, source=name) for face, color in zip(faces, colors) ] def __repr__(self):
return '<Shape %d verts, %d faces>' % ( len(self.vertices), len(self.faces), ) def get_edges(self):
[docs] ''' Return a set of pairs, each pair represents indices that start and end an edge. Contents of each pair is sorted. e.g Tetrahedron: { (0, 1), (1, 2), (0, 2), (0, 3), (1, 3), (2, 3), } ''' edges = set() for face in self.faces: for i in xrange(len(face)): edges.add( tuple(sorted((face[i], face[i+1]))) ) return edges def replace_face(self, index, new_faces):
[docs] ''' Replace the face at position 'index' in self.faces with the list of Face instances in 'new_faces' ''' self.faces[index] = new_faces.pop() while new_faces: self.faces.append( new_faces.pop() )