def test_does_overlap(): aabb5 = AABB([(-3, 3), (-3, 3)]) aabb6 = AABB([(0, 1), (5, 6)]) aabb7 = AABB([(6.5, 6.5), (5.5, 5.5)]) for aabb in (aabb5, aabb6, aabb7): assert not AABBTree().does_overlap(aabb) aabbs = standard_aabbs() for indices in itertools.permutations(range(4)): tree = AABBTree() for i in indices: tree.add(aabbs[i]) assert tree.does_overlap(aabb5) assert not tree.does_overlap(aabb6) assert not tree.does_overlap(aabb7)
def test_does_overlap(): aabb5 = AABB([(-3, 3), (-3, 3)]) aabb6 = AABB([(0, 1), (5, 6)]) aabb7 = AABB([(6.5, 6.5), (5.5, 5.5)]) not_tree = AABBTree() not_tree.add(aabb6) not_tree.add(aabb7) for aabb in (aabb5, aabb6, aabb7): for m in ('DFS', 'BFS'): assert not AABBTree().does_overlap(aabb, method=m) aabbs = standard_aabbs() for indices in itertools.permutations(range(4)): tree = AABBTree() alt_tree = AABBTree() for i_ind, i in enumerate(indices): tree.add(aabbs[i]) alt_tree.add(aabbs[i_ind]) for m in ('DFS', 'BFS'): assert tree.does_overlap(tree, method=m) assert alt_tree.does_overlap(tree, method=m) assert tree.does_overlap(alt_tree, method=m) assert tree.does_overlap(aabb5, method=m) assert not tree.does_overlap(aabb6, method=m) assert not tree.does_overlap(aabb7, method=m) assert not tree.does_overlap(not_tree, method=m) assert not not_tree.does_overlap(tree, method=m)
class Collision: def __init__(self, objects=None): self.tree = AABBTree() if objects: for item in objects: if item["type"] == "CUBE": self.add_object(item["pos"], (item["scale"][0] / 2, item["scale"][1] / 2, item["scale"][2] / 2), item["goal"]) elif item["type"] == "SPHERE": self.add_object(item["pos"], item["scale"], item["goal"]) # Returns AABB object with coorect values based on a point and offset def get_aabb(self, point, bound): return AABB([(point.x - bound[0], point.x + bound[0]), (point.y - bound[1], point.y + bound[1]), (point.z - bound[2], point.z + bound[2])]) # Add object with position and scale to the tree # Sets the position as the value returned in case of collision def add_object(self, position, scale, goal): self.tree.add(self.get_aabb(position, scale), { "pos": position, "scale": scale, "goal": goal }) # Makes checking for collision on point easier def point_collision(self, point, bound): return self.tree.does_overlap(self.get_aabb(point, bound)) # Returns the point object of collided objects def collision_objects(self, point, bound): return self.tree.overlap_values(self.get_aabb(point, bound)) # checks if player is between bounds of object on an axis with respect to size def is_between(self, player, obj, axis): return obj["pos"][axis] - obj["scale"][axis] - player["scale"][ axis] < player["pos"][axis] < obj["pos"][axis] + obj["scale"][ axis] + player["scale"][axis] # Finds which side of a cube the player is touching def get_colliding_face(self, player, obj): # Zeroes the axis of the plane on the object the player is touching return (1 if self.is_between(player, obj, 0) else 0, 1 if self.is_between(player, obj, 1) else 0, 1 if self.is_between(player, obj, 2) else 0) # Returns surface vector based on player direction def get_surface_vector(self, player, obj): directions = self.get_colliding_face(player, obj) # Returns a normalized vector that represents the direction of the surface # Direction on the non zeroed axes based on player movement return Vector(player["direction"].x * directions[0], player["direction"].y * directions[1], player["direction"].z * directions[2]).normalize() # Returns slide vector for player on collided object def get_slide_vector(self, player, obj): surface_vector = self.get_surface_vector(player, obj) return surface_vector * player["direction"].dot(surface_vector) # Returns motion vector of player def move(self, player): # collision member set to advoid key error player["collision"] = [] # If no collision return player directly if not self.point_collision(player["newpos"], player["scale"]): return player else: # If collision, get slide vector for each object collided with for item in self.collision_objects(player["newpos"], player["scale"]): player["direction"] = self.get_slide_vector(player, item) player["collision"].append( self.get_colliding_face(player, item)) if item["goal"]: player["collision"].append((0, 0, 0)) return player