Module moog.shapes
Shapes and shape-fetching functions for common use across tasks.
Expand source code
"""Shapes and shape-fetching functions for common use across tasks."""
import numpy as np
from moog import sprite
from spriteworld import shapes
# A selection of simple shapes. Elements in SHAPES can be looked up from their
# string keys in sprite.Sprite, i.e. you can give a string key as the `shape`
# argument to sprite.Sprite and it will fetch the vertices if that key is in
# this dictionary.
SHAPES = {
'triangle': shapes.polygon(num_sides=3, theta_0=np.pi/2),
'square': shapes.polygon(num_sides=4, theta_0=np.pi/4),
'pentagon': shapes.polygon(num_sides=5, theta_0=np.pi/2),
'hexagon': shapes.polygon(num_sides=6),
'octagon': shapes.polygon(num_sides=8),
'circle': shapes.polygon(num_sides=30),
'star_4': shapes.star(num_sides=4, theta_0=np.pi/4),
'star_5': shapes.star(num_sides=5, theta_0=np.pi + np.pi/10),
'star_6': shapes.star(num_sides=6),
'spoke_4': shapes.spokes(num_sides=4, theta_0=np.pi/4),
'spoke_5': shapes.spokes(num_sides=5, theta_0=np.pi + np.pi/10),
'spoke_6': shapes.spokes(num_sides=6),
}
def border_walls(visible_thickness=0.05,
total_thickness=0.5,
c0=0,
c1=0,
c2=0,
opacity=255):
"""Get four sprites forming a border around the [0, 1] x [0, 1] frame.
This can be used to (i) create the visual effect of a border at the edges of
the screen, and/or (ii) create walls around the border that can be used to
contain sprites inside the interior of the screen.
Args:
visible_thickness: Float. How thick the borders within the frame should
be.
total_thickness: Float. How thick the border wall is in total. Depending
on visible_thickness, much of this may lie outside of the frame. As
long as total_thickness is greater than visible_thickness, it is not
important. However, if visible_thickness is very small then it can
be good to have total_thickness non-negligibly greater than zero,
otherwise the wall sprites are extremely narrow and collisions can
be a little unstable since their vertices and centers of mass are
nearly collinear.
c0: Scalar. First coordinate of color of wall sprites.
c1: Scalar. Second coordinate of color of wall sprites.
c2: Scalar. Third coordinate of color of wall sprites.
opacity: Integer in [0, 255]. Opacity of wall sprites.
Returns:
walls: List of four sprites, the walls.
"""
boundary_wall_shape_0 = np.array([
[0., visible_thickness],
[1., visible_thickness],
[1., visible_thickness - total_thickness],
[0., visible_thickness - total_thickness],
])
distance_across_frame = 1 + total_thickness - 2 * visible_thickness
wall_shapes = [
boundary_wall_shape_0,
boundary_wall_shape_0 + np.array([[0., distance_across_frame]]),
np.flip(boundary_wall_shape_0, axis=1),
np.flip(boundary_wall_shape_0, axis=1) + np.array(
[[distance_across_frame, 0.]]),
]
sprite_factors = dict(x=0., y=0., c0=c0, c1=c1, c2=c2, opacity=opacity)
walls = [
sprite.Sprite(shape=wall_shape, **sprite_factors)
for wall_shape in wall_shapes
]
return walls
def grid_lines(grid_x=0.4,
grid_y=0.4,
line_thickness=0.01,
buffer_border=0.,
c0=0,
c1=0,
c2=0,
opacity=255):
"""Get grid of lines.
Returns a list of thin rectangular sprites forming grid lines.
This is sometimes used to put a grid in the background, particularly when
using a first-person renderer for which this grid tells the player how the
agent is moving.
Args:
grid_x: Float. Width of each grid cell.
grid_y: Float. Height of each grid cell.
line_thickness: Float. How thick the grid lines should be.
buffer_border: Float. How far around the frame in every direction to
create the grid. This is useful for first-person rendering, when the
field of view sometimes extends outside [0, 1] x [0, 1].
c0: Scalar. First coordinate of color of background grid sprites.
c1: Scalar. Second coordinate of color of background grid sprites.
c2: Scalar. Third coordinate of color of background grid sprites.
opacity: Integer in [0, 255]. Opacity of background grid sprites.
Returns:
grid_lines: List of sprites, the grid lines.
"""
half_num_lines_across = int(np.floor((0.5 + buffer_border) / grid_x))
half_num_lines_up = int(np.floor((0.5 + buffer_border) / grid_y))
x_vertices = np.linspace(
start=0.5 - half_num_lines_across * grid_x,
stop=0.5 + half_num_lines_across * grid_x,
num=1 + 2 * half_num_lines_across,
)
y_vertices = np.linspace(
start=0.5 - half_num_lines_up * grid_y,
stop=0.5 + half_num_lines_up * grid_y,
num=1 + 2 * half_num_lines_up,
)
sprite_factors = dict(x=0., y=0., c0=c0, c1=c1, c2=c2, opacity=opacity)
grid_sprites = []
def _add_sprite(min_x, max_x, min_y, max_y):
shape = np.array([
[min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y]
])
grid_sprites.append(sprite.Sprite(shape=shape, **sprite_factors))
for x in x_vertices:
min_x = x - 0.5 * line_thickness
max_x = x + 0.5 * line_thickness
min_y = -1 * buffer_border
max_y = 1. + buffer_border
_add_sprite(min_x, max_x, min_y, max_y)
for y in y_vertices:
min_x = -1 * buffer_border
max_x = 1. + buffer_border
min_y = y - 0.5 * line_thickness
max_y = y + 0.5 * line_thickness
_add_sprite(min_x, max_x, min_y, max_y)
return grid_sprites
def circle_vertices(radius, num_sides=50):
"""Get vertices for a circle, centered about the origin.
Args:
radius: Scalar. Radius of the circle.
num_sides: Int. Number of sides in the circle. The circle is really just
a many-sided polygon with this many sides
Returns:
circle: Numpy array of shape [num_sides, 2] containing the vertices of
the circle.
"""
min_theta = 2 * np.pi / num_sides
thetas = np.linspace(min_theta, 2 * np.pi, num_sides)
circle = np.stack([np.sin(thetas), np.cos(thetas)], axis=1)
circle *= radius
return circle
def annulus_vertices(inner_radius, outer_radius, num_sides=50):
"""Get vertices for an annulus, centered about the origin.
Args:
inner_radius: Float. Radius of inner circle
outer_radius: Float. Radius of outer circle.
num_sides: Int. Number of sides in each circle. Each circle is really a
many-sided polygon.
Returns:
annulus: Numpy array of shape [num_sides, 2] containing the vertices of
the annulus.
"""
inner_circle = circle_vertices(inner_radius, num_sides=num_sides)
inner_circle = np.concatenate((inner_circle, [inner_circle[0]]), axis=0)
outer_circle = circle_vertices(outer_radius, num_sides=num_sides)
outer_circle = np.concatenate((outer_circle, [outer_circle[0]]), axis=0)
annulus = np.concatenate((inner_circle, outer_circle[::-1]), axis=0)
return annulus
Functions
def annulus_vertices(inner_radius, outer_radius, num_sides=50)
-
Get vertices for an annulus, centered about the origin.
Args
inner_radius
- Float. Radius of inner circle
outer_radius
- Float. Radius of outer circle.
num_sides
- Int. Number of sides in each circle. Each circle is really a many-sided polygon.
Returns
annulus
- Numpy array of shape [num_sides, 2] containing the vertices of the annulus.
Expand source code
def annulus_vertices(inner_radius, outer_radius, num_sides=50): """Get vertices for an annulus, centered about the origin. Args: inner_radius: Float. Radius of inner circle outer_radius: Float. Radius of outer circle. num_sides: Int. Number of sides in each circle. Each circle is really a many-sided polygon. Returns: annulus: Numpy array of shape [num_sides, 2] containing the vertices of the annulus. """ inner_circle = circle_vertices(inner_radius, num_sides=num_sides) inner_circle = np.concatenate((inner_circle, [inner_circle[0]]), axis=0) outer_circle = circle_vertices(outer_radius, num_sides=num_sides) outer_circle = np.concatenate((outer_circle, [outer_circle[0]]), axis=0) annulus = np.concatenate((inner_circle, outer_circle[::-1]), axis=0) return annulus
def border_walls(visible_thickness=0.05, total_thickness=0.5, c0=0, c1=0, c2=0, opacity=255)
-
Get four sprites forming a border around the [0, 1] x [0, 1] frame.
This can be used to (i) create the visual effect of a border at the edges of the screen, and/or (ii) create walls around the border that can be used to contain sprites inside the interior of the screen.
Args
visible_thickness
- Float. How thick the borders within the frame should be.
total_thickness
- Float. How thick the border wall is in total. Depending on visible_thickness, much of this may lie outside of the frame. As long as total_thickness is greater than visible_thickness, it is not important. However, if visible_thickness is very small then it can be good to have total_thickness non-negligibly greater than zero, otherwise the wall sprites are extremely narrow and collisions can be a little unstable since their vertices and centers of mass are nearly collinear.
c0
- Scalar. First coordinate of color of wall sprites.
c1
- Scalar. Second coordinate of color of wall sprites.
c2
- Scalar. Third coordinate of color of wall sprites.
opacity
- Integer in [0, 255]. Opacity of wall sprites.
Returns
walls
- List of four sprites, the walls.
Expand source code
def border_walls(visible_thickness=0.05, total_thickness=0.5, c0=0, c1=0, c2=0, opacity=255): """Get four sprites forming a border around the [0, 1] x [0, 1] frame. This can be used to (i) create the visual effect of a border at the edges of the screen, and/or (ii) create walls around the border that can be used to contain sprites inside the interior of the screen. Args: visible_thickness: Float. How thick the borders within the frame should be. total_thickness: Float. How thick the border wall is in total. Depending on visible_thickness, much of this may lie outside of the frame. As long as total_thickness is greater than visible_thickness, it is not important. However, if visible_thickness is very small then it can be good to have total_thickness non-negligibly greater than zero, otherwise the wall sprites are extremely narrow and collisions can be a little unstable since their vertices and centers of mass are nearly collinear. c0: Scalar. First coordinate of color of wall sprites. c1: Scalar. Second coordinate of color of wall sprites. c2: Scalar. Third coordinate of color of wall sprites. opacity: Integer in [0, 255]. Opacity of wall sprites. Returns: walls: List of four sprites, the walls. """ boundary_wall_shape_0 = np.array([ [0., visible_thickness], [1., visible_thickness], [1., visible_thickness - total_thickness], [0., visible_thickness - total_thickness], ]) distance_across_frame = 1 + total_thickness - 2 * visible_thickness wall_shapes = [ boundary_wall_shape_0, boundary_wall_shape_0 + np.array([[0., distance_across_frame]]), np.flip(boundary_wall_shape_0, axis=1), np.flip(boundary_wall_shape_0, axis=1) + np.array( [[distance_across_frame, 0.]]), ] sprite_factors = dict(x=0., y=0., c0=c0, c1=c1, c2=c2, opacity=opacity) walls = [ sprite.Sprite(shape=wall_shape, **sprite_factors) for wall_shape in wall_shapes ] return walls
def circle_vertices(radius, num_sides=50)
-
Get vertices for a circle, centered about the origin.
Args
radius
- Scalar. Radius of the circle.
num_sides
- Int. Number of sides in the circle. The circle is really just a many-sided polygon with this many sides
Returns
circle
- Numpy array of shape [num_sides, 2] containing the vertices of the circle.
Expand source code
def circle_vertices(radius, num_sides=50): """Get vertices for a circle, centered about the origin. Args: radius: Scalar. Radius of the circle. num_sides: Int. Number of sides in the circle. The circle is really just a many-sided polygon with this many sides Returns: circle: Numpy array of shape [num_sides, 2] containing the vertices of the circle. """ min_theta = 2 * np.pi / num_sides thetas = np.linspace(min_theta, 2 * np.pi, num_sides) circle = np.stack([np.sin(thetas), np.cos(thetas)], axis=1) circle *= radius return circle
def grid_lines(grid_x=0.4, grid_y=0.4, line_thickness=0.01, buffer_border=0.0, c0=0, c1=0, c2=0, opacity=255)
-
Get grid of lines.
Returns a list of thin rectangular sprites forming grid lines.
This is sometimes used to put a grid in the background, particularly when using a first-person renderer for which this grid tells the player how the agent is moving.
Args
grid_x
- Float. Width of each grid cell.
grid_y
- Float. Height of each grid cell.
line_thickness
- Float. How thick the grid lines should be.
buffer_border
- Float. How far around the frame in every direction to create the grid. This is useful for first-person rendering, when the field of view sometimes extends outside [0, 1] x [0, 1].
c0
- Scalar. First coordinate of color of background grid sprites.
c1
- Scalar. Second coordinate of color of background grid sprites.
c2
- Scalar. Third coordinate of color of background grid sprites.
opacity
- Integer in [0, 255]. Opacity of background grid sprites.
Returns
grid_lines()
- List of sprites, the grid lines.
Expand source code
def grid_lines(grid_x=0.4, grid_y=0.4, line_thickness=0.01, buffer_border=0., c0=0, c1=0, c2=0, opacity=255): """Get grid of lines. Returns a list of thin rectangular sprites forming grid lines. This is sometimes used to put a grid in the background, particularly when using a first-person renderer for which this grid tells the player how the agent is moving. Args: grid_x: Float. Width of each grid cell. grid_y: Float. Height of each grid cell. line_thickness: Float. How thick the grid lines should be. buffer_border: Float. How far around the frame in every direction to create the grid. This is useful for first-person rendering, when the field of view sometimes extends outside [0, 1] x [0, 1]. c0: Scalar. First coordinate of color of background grid sprites. c1: Scalar. Second coordinate of color of background grid sprites. c2: Scalar. Third coordinate of color of background grid sprites. opacity: Integer in [0, 255]. Opacity of background grid sprites. Returns: grid_lines: List of sprites, the grid lines. """ half_num_lines_across = int(np.floor((0.5 + buffer_border) / grid_x)) half_num_lines_up = int(np.floor((0.5 + buffer_border) / grid_y)) x_vertices = np.linspace( start=0.5 - half_num_lines_across * grid_x, stop=0.5 + half_num_lines_across * grid_x, num=1 + 2 * half_num_lines_across, ) y_vertices = np.linspace( start=0.5 - half_num_lines_up * grid_y, stop=0.5 + half_num_lines_up * grid_y, num=1 + 2 * half_num_lines_up, ) sprite_factors = dict(x=0., y=0., c0=c0, c1=c1, c2=c2, opacity=opacity) grid_sprites = [] def _add_sprite(min_x, max_x, min_y, max_y): shape = np.array([ [min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y] ]) grid_sprites.append(sprite.Sprite(shape=shape, **sprite_factors)) for x in x_vertices: min_x = x - 0.5 * line_thickness max_x = x + 0.5 * line_thickness min_y = -1 * buffer_border max_y = 1. + buffer_border _add_sprite(min_x, max_x, min_y, max_y) for y in y_vertices: min_x = -1 * buffer_border max_x = 1. + buffer_border min_y = y - 0.5 * line_thickness max_y = y + 0.5 * line_thickness _add_sprite(min_x, max_x, min_y, max_y) return grid_sprites