def get_support(self, shape1, shape2): """Gets a support point based on two points in the shapes that may be colliding. """ opposite = [-x for x in self.search] a = max(shape1.pts, key=lambda p: dot(minus(p, shape1.center), self.search)) b = max(shape2.pts, key=lambda p: dot(minus(p, shape2.center), opposite)) return minus(a, b)
def check_tetrahedron(self): """Determines whether the origin is enclosed within a 4-simplex. Changes self.hit to True if there's a hit. Otherwise, drops a point from the simplex and sets the search to its normal vector. """ # we know triangle DCB is aligned correctly; # we need to check the other 3 normals for same_direction d, c, b, a = self.simplex triangles = [ [c, b, a], [d, b, a], [d, c, a] ] # saving the order of points in the triangles so # we know which cross product order creates an # inward-facing normal when rolled out in reverse # order (c, b, a = triangle, not a, b, c = triangle) good_tris = [] for tri in triangles: cc, bb, aa = tri ab_vec = minus(bb, aa) ac_vec = minus(cc, aa) # get the point to test the direction of the norm dd = [pt for pt in self.simplex if pt not in tri][0] norm = pt_cross(ab_vec, ac_vec) if dot(dd, norm) > 0: if self.same_direction(norm): good_tris.append(tri) else: new_norm = [-x for x in norm] if self.same_direction(new_norm): good_tris.append([bb, cc, aa]) self.hit = len(good_tris) == 3 if not self.hit: # i can admit, this is really just sort of flailing. # but it isn't entirely stupid; i'm just throwing out the oldest point # and starting over with a triangle and a new search direction. self.simplex.pop(0) c, b, a = self.simplex ab = minus(b, a) ac = minus(c, a) ab_x = pt_cross(ab, ac) if self.same_direction(ab_x): self.simplex = [c, b, a] self.search = ab_x else: self.simplex = [b, c, a] self.search = [-x for x in ab_x]
def __init__(self, x, y, z, d): pts = coords([x, y, z], [ [-1, -1, -1], [-1, 1, -1], [1, -1, -1], [1, 1, -1], [-1, -1, 1], [-1, 1, 1], [1, -1, 1], [1, 1, 1], ], d / 2.0) newlines = [] for a in pts: for b in pts: vector = minus(a, b) if dot(vector, vector) == d**2: newlines.append((a, b)) super(ZCube, self).__init__(pts) pt_groups = ((pts[0], pts[1], pts[2], pts[3]), (pts[4], pts[5], pts[6], pts[7]), (pts[0], pts[1], pts[4], pts[5]), (pts[2], pts[3], pts[6], pts[7]), (pts[0], pts[2], pts[4], pts[6]), (pts[1], pts[3], pts[5], pts[7])) for group in pt_groups: lines = [(a, b) for a in group for b in group if (a, b) in newlines or (b, a) in newlines] self.set_face(group, lines)
def sort_face_pts(self, pts): pt_list = pts[:] pt = pt_list[0] yield pt while pt_list: pt = min(pt_list, key=lambda p: sum(x * x for x in minus(pt, p))) pt_list.remove(pt) yield pt
def set_norm(self, wrong_way): """Determines the correct order for calculating the surface normal. Ideally all points in this surface are coplanar so just three points are enough. The 'wrong way' point is needed to set the direction (line self.center -> wrong_way) we do NOT want the normal to point. """ wrong_vector = minus(wrong_way, self.center) if not dot(wrong_vector, self.get_norm()) < 0: self.order.append(self.order.pop(1))
def cull_draw(self, qs): all_objs = [] for q in qs: for s in q: all_objs.append(s) self.screen.lock() for zshape in sorted(all_objs, key=lambda x: x.center[-1], reverse=True): if not zshape.faces: for line in zshape.lines: self.draw_zline(line[0], line[1], zshape.color, 1) else: for face in zshape.faces: if dot(minus(face.order[0], self.camera), face.get_norm()) < 0: self.draw_face(face) self.screen.unlock()
def check_line(self): """Determines which part of a 2-simplex is closest to the origin. Sets search direction in accordance. May reduce the simplex to a single point if self.simplex[-1] is the closest aspect. """ ab = minus(self.simplex[0], self.simplex[1]) if self.same_direction(ab): # set next search vector self.search = pt_cross(pt_cross(ab, self.ao), ab) else: # the closest aspect # is the last point in the simplex. # so the simplex is pt A # and the search direction is AO self.simplex.pop(0) self.search = self.ao
def __init__(self, x, y, z, d, right=True): pts = coords([x, y, z], [[0, sqrt(3) - (1.0 / sqrt(3)), 0], [-1, -1.0 / sqrt(3), 0], [1, -1.0 / sqrt(3), 0], [0, 0, 2.0 * sqrt(2 / 3.0)]], d) e = .000001 lines = [] for a in pts: for b in pts: vector = minus(a, b) if d - e <= (dot(vector, vector) / (d * 4)) <= d + e: lines.append((a, b)) super(ZTetra, self).__init__(pts) if right: self.rotate(-(2 * pi) / 4, "x") pt_group = ((pts[0], pts[1], pts[2]), (pts[0], pts[2], pts[3]), (pts[0], pts[1], pts[3]), (pts[1], pts[2], pts[3])) for group in pt_group: line_group = ((group[0], group[1]), (group[1], group[2]), (group[0], group[2])) self.set_face(group, line_group)
def __init__(self, x, y, z, d): pts = coords([x, y, z], [[1, 0, 0], [0, 1, 0], [0, 0, 1], [-1, 0, 0], [0, -1, 0], [0, 0, -1]], d) e = .000001 lines = [] for a in pts: for b in pts: vector = minus(a, b) if d - e <= (dot(vector, vector) / (d * 2)) <= d + e: lines.append((a, b)) super(ZOcta, self).__init__(pts) pt_groups = ((pts[0], pts[1], pts[2]), (pts[0], pts[1], pts[5]), (pts[3], pts[1], pts[2]), (pts[3], pts[1], pts[5]), (pts[0], pts[4], pts[2]), (pts[0], pts[4], pts[5]), (pts[3], pts[4], pts[2]), (pts[3], pts[4], pts[5])) for group in pt_groups: newlines = [(a, b) for a in group for b in group if (a, b) in lines or (b, a) in lines] self.set_face(group, newlines)
def find_collision(self, shape1, shape2): self.hit = False self.search = minus(shape1.center, shape2.center) self.simplex = [self.get_support(shape1, shape2)] self.search = [-x for x in self.search] # keeps it from working on the same # two shapes ad infinitum for _ in xrange(len(shape1.pts) * len(shape2.pts)): next_pt = self.get_support(shape1, shape2) # if the dot product of line ON (origin -> next_pt) # and line OS (origin -> search) is positive, # then we evolve the simplex and continue. # # if not, we cannot have a hit - return False. Loop over! if dot(next_pt, self.search) > 0: self.simplex.append(next_pt) # if the origin is enclosed # by the tetrahedron, self.hit flips to True self.get_simplex() else: return False if self.hit: return True
def flat_shading(self, face): n_norm = normalize_vector(face.get_norm()) d_norm = normalize_vector(minus(self.light_source, face.order[0])) shade = abs(dot(n_norm, d_norm)) return [int(c * shade) for c in face.color]
def check_triangle(self): """Determines which part of a 3-simplex is closest to the origin. Sets search direction in accordance. May reduce the simplex to either a 2-simplex or a single point, depending on the results. """ # note that at this point, # self.simplex is triangle # CBA, not ABC! we want to keep looking # towards line AO (simplex[-1] to origin) # so unroll the list accordingly c, b, a = self.simplex # these come up a lot here ab_vec = minus(b, a) ac_vec = minus(c, a) # get triangle normal abc_norm = pt_cross(ab_vec, ac_vec) # then get normal from the normal! this should # always point outward (away from triangle center) abc_x_ac = pt_cross(abc_norm, ac_vec) fall_thru = False # if a line orthogonal from the triangle # AND line AC is the same as AO... if self.same_direction(abc_x_ac): # ...do another test because origin could # still be outside the triangle if self.same_direction(ac_vec): # simplex becomes line AC # search is the normal from AC x AO x AC self.simplex = [c, a] self.search = pt_cross(pt_cross(ac_vec, self.ao), ac_vec) else: fall_thru = True else: # otherwise check the other normal from the other side ab_x_abc = pt_cross(ab_vec, abc_norm) if not self.same_direction(ab_x_abc): # it's a NOT check because if ab_x_abc # dots positively to self.ao, it # means that line AB is the closest, so # that becomes the new simplex. # otherwise, the triangle is the closest aspect # now we just need to find the right normal from # the triangle to search in if self.same_direction(abc_norm): # just change the search, triangle is fine self.search = abc_norm else: # otherwise, flip the normal and the # b & c points in the triangle! self.simplex = [b, c, a] self.search = [-x for x in abc_norm] else: fall_thru = True # if we're here it means that AB was closest # and so we pop simplex[0] if fall_thru: if self.same_direction(ab_vec): self.simplex = [b, a] self.search = pt_cross(pt_cross(ab_vec, self.ao), ab_vec) else: # must be that the triangle itself was pointed at the origin # and point A was closest, so pop again and set search to self.ao self.simplex = [a] self.search = self.ao