def query_point(self, x_or_point, y=None, from_mask=0xffffffff): """Hit test at the point specified. :param x_or_point: x coordinate (float) or sequence of (x, y) floats. :param y: y coordinate (float) if x is not a sequence :param from_mask: Bit mask used to filter query results. This value is bit ANDed with candidate entities' ``collision.into_mask``. If the result is non-zero, then it is considered a hit. By default all entities colliding with the input point are returned. :return: A set of entities where the point is inside their collision radii as of the last time step. """ if y is None: point = Vec2d(x_or_point) else: point = Vec2d(x_or_point, y) hits = set() position = getattr(self.world.components, self.position_component) collision = getattr(self.world.components, self.collision_component) for entity in self.broad_phase.query_point(x_or_point, y, from_mask): separation = point - position[entity].position if separation.get_length_sqrd() <= collision[entity].radius**2: hits.add(entity) return hits
def test_generate_verts_with_angle(self): from grease.renderer import Vector from grease.geometry import Vec2d, Vec2dArray world = self.make_world() renderer = Vector() renderer.set_world(world) self.assertTrue(renderer.world is world) world.shapes = [ Data(closed=True, verts=Vec2dArray([(0,0), (0, 1), (1, 0)])), Data(closed=True, verts=Vec2dArray([(-2, -1), (2, -1), (2, 1), (-2, 1)])), Data(closed=False, verts=Vec2dArray([(1, -1), (-1, -1), (1, 1), (-1, 1)])), ] world.positions = [ Data(position=Vec2d(10, 10), angle=45), Data(position=Vec2d(4, 3), angle=90), Data(position=Vec2d(0, 0), angle=-45), ] v_array, i_size, i_array, i_count = renderer._generate_verts() self.assertEqual(i_count, 20) sin45 = math.sin(math.radians(45)) cos45 = math.cos(math.radians(45)) self.assertArrayEqual(self.get_verts(v_array[:3]), [(10, 10), (sin45+10, cos45+10), (cos45+10, -sin45+10)]) self.assertEqual(list(i_array[:6]), [0, 1, 1, 2, 2, 0]) self.assertArrayEqual(self.get_verts(v_array[3:7]), [(3, 5), (3, 1), (5, 1), (5, 5)]) self.assertEqual(list(i_array[6:14]), [3, 4, 4, 5, 5, 6, 6, 3]) self.assertArrayEqual(self.get_verts(v_array[7:11]), [(sqrt2, 0), (0, -sqrt2), (0, sqrt2), (-sqrt2, 0)]) self.assertEqual(list(i_array[14:20]), [7, 8, 8, 9, 9, 10]) self.assertEqual(self.get_rgba(v_array[:11]), [(255,255,255,255)] * 11)
def set(self, entity, position): from grease.geometry import Vec2d if entity in self: data = self[entity] else: data = self[entity] = Data() data.entity = entity data.position = Vec2d(position)
def test_cast(self): from grease.component.field import Field from grease.geometry import Vec2d f = Field(None, "string", str) self.assertEqual(f.cast(22), "22") f = Field(None, "int", int) self.assertEqual(f.cast("42"), 42) f = Field(None, "vec", Vec2d) self.assertEqual(f.cast((11, 12)), Vec2d(11, 12))
def make_world(self): from grease.geometry import Vec2d, Vec2dArray from grease.color import RGBA world = TestWorld() world.shapes = [ Data(closed=True, verts=Vec2dArray([(0,0), (0, 1), (0.5, 0.5)])), Data(closed=True, verts=Vec2dArray([(-1, -1), (1, -1), (1, 1), (-1, 1)])), Data(closed=False, verts=Vec2dArray([(1, -1), (-1, -1), (1, 1), (-1, 1)])), ] world.positions = [ Data(position=Vec2d(10, 10), angle=0), Data(position=Vec2d(4, 3), angle=0), Data(position=Vec2d(0, 0), angle=0), ] world.renderable = [ Data(color=RGBA(1,1,1,1)), Data(color=RGBA(1,1,1,1)), Data(color=RGBA(1,1,1,1)), ] return world
def _generate_verts(self): """Generate vertex and index arrays for rendering""" vert_count = sum( len(shape.verts) + 1 for shape, ignored, ignored in self.world.components.join( self.shape_component, self.position_component, self.renderable_component)) v_array = (CVertColor * vert_count)() if vert_count > 65536: i_array = (ctypes.c_uint * 2 * vert_count)() i_size = pyglet.gl.GL_UNSIGNED_INT else: i_array = (ctypes.c_ushort * (2 * vert_count))() i_size = pyglet.gl.GL_UNSIGNED_SHORT v_index = 0 i_index = 0 scale = self.scale rot_vec = Vec2d(0, 0) for shape, position, renderable in self.world.components.join( self.shape_component, self.position_component, self.renderable_component): shape_start = v_index angle = radians(-position.angle) rot_vec.x = cos(angle) rot_vec.y = sin(angle) r = int(renderable.color.r * 255) g = int(renderable.color.g * 255) b = int(renderable.color.b * 255) a = int(renderable.color.a * 255) for vert in shape.verts: vert = vert.cpvrotate(rot_vec) * scale + position.position v_array[v_index].vert.x = vert.x v_array[v_index].vert.y = vert.y v_array[v_index].color.r = r v_array[v_index].color.g = g v_array[v_index].color.b = b v_array[v_index].color.a = a if v_index > shape_start: i_array[i_index] = v_index - 1 i_index += 1 i_array[i_index] = v_index i_index += 1 v_index += 1 if shape.closed and v_index - shape_start > 2: i_array[i_index] = v_index - 1 i_index += 1 i_array[i_index] = shape_start i_index += 1 return v_array, i_size, i_array, i_index
def on_collide(self, other, point, normal): if isinstance(other, Asteroid): if self.collision.radius == 0: return if other.collision.radius == 0: return nx, ny = normal ppx, ppy = other.position.position px, py = self.position.position dx = ppx - px dy = ppy - py d2 = dx**2 + dy**2 d = math.sqrt(d2) d1 = d - self.collision.radius - other.collision.radius if d1 > 0: d1 += 1 else: d1 = d / (self.collision.radius + other.collision.radius) if d1 < 0.001: d1 = 0.001 if (self.world.time - self.states.created < 1.5 or self.world.time - other.states.created < 1.5): d1 += 0.1 d1 /= 5.0 self.position.position[0] += nx / d1 * 0.4 self.position.position[1] += ny / d1 * 0.4 other.movement.velocity[0] -= nx / d1 * 3.0 other.movement.velocity[1] -= ny / d1 * 3.0 self.movement.velocity *= 0.5 other.movement.velocity *= 0.5 self.movement.velocity *= 0.8 self.movement.velocity[ 0] += nx / d1 * 3.0 * other.collision.radius / self.collision.radius self.movement.velocity[ 1] += ny / d1 * 3.0 * other.collision.radius / self.collision.radius if self.world.time - self.states.created < 1: return if self.world.time - other.states.created < 1: return px1, py1 = self.position.position px2, py2 = other.position.position """ if d < 5: total_radius = math.sqrt(self.collision.radius**2 + other.collision.radius**2) if total_radius > 60: return px = (px1 + px2) / 2.0 py = (py1 + py2) / 2.0 dx1, dy1 = self.movement.velocity dx2, dy2 = other.movement.velocity dx = (dx1 + dx2) / 2.0 dy = (dy1 + dy2) / 2.0 if total_radius >= 4: Asteroid(self.world, total_radius, (px,py), (dx,dy), self.award.points / 2) self.collision.radius = 0 other.collision.radius = 0 self.delete() other.delete() """ return if isinstance(other, Shot): if other.collision.radius == 0: return if self.collision.radius == 0: return other.collision.radius = 0 if self.collision.radius > 6: total_area = 3.1415927 * (self.collision.radius**2) total_area -= 2 count = 0 for i in range(50): if total_area < 4: break min_area = 3.1415927 * 8 * 8 if min_area < total_area - 10: chunk_size = random.gauss(self.collision.radius / 8, self.collision.radius / 8) if chunk_size < 4: continue else: chunk_size = math.sqrt(total_area) / 3.1415927 - 0.5 if chunk_size < 4: break chunk_area = 3.1415927 * (chunk_size**2) if chunk_area > total_area: continue total_area -= chunk_area px, py = self.position.position angle = random.uniform(0, 360) offset = random.uniform(2 + chunk_size / 2, self.collision.radius / 2) dx, dy = math.cos(angle) * offset, math.sin(angle) * offset px += dx py += dy ppos = (px, py) Asteroid(self.world, chunk_size, ppos, self.movement.velocity + Vec2d(dx, dy) * 10, self.award.points * 2) count += 1 if random.gauss(0, total_area) > 8: count += 1 if random.gauss(0, total_area) > 8: count += 1 if random.gauss(0, total_area) > 8: count += 1 for i in range(random.randint(0, int(count / 4))): Collectable(self.world, None, self.position.position, self.movement.velocity) random.choice(self.HIT_SOUNDS).play() else: random.choice(self.HIT_SMALL_SOUNDS).play() self.collision.radius = 0 self.explode() self.delete()
############################################################################# __version__ = '$Id$' import operator from grease.geometry import Vec2d, Vec2dArray, Rect from grease import color # Allowed field types -> default values types = { int: lambda: 0, float: lambda: 0.0, bool: lambda: False, str: lambda: "", object: lambda: None, Vec2d: lambda: Vec2d(0, 0), Vec2dArray: lambda: Vec2dArray(), color.RGBA: lambda: color.RGBA(0.0, 0.0, 0.0, 0.0), Rect: lambda: Rect(0.0, 0.0, 0.0, 0.0) } class Schema(dict): """Field schema definition for custom components""" def __init__(self, **fields): for ftype in fields.values(): assert ftype in types, fname + " has an illegal field type" self.update(fields) class FieldAccessor(object):