Source code for arcade.draw_commands

"""
This module contains commands for basic graphics drawing commands.
(Drawing primitives.)

Many of these commands are slow, because they load everything to the
graphics card each time a shape is drawn. For faster drawing, see the
Buffered Draw Commands.
"""
from __future__ import annotations

import array
import math
from typing import Optional, Tuple, List

import PIL.Image
import PIL.ImageOps
import PIL.ImageDraw

import pyglet.gl as gl

from arcade.color import WHITE
from arcade.types import AsFloat, Color, RGBA255, PointList, Point, Point2List
from arcade.earclip import earclip
from arcade.types.rect import Rect, LBWH, LRBT, XYWH
from .math import rotate_point
from arcade import (
    get_points_for_thick_line,
    Texture,
    get_window,
)

__all__ = [
    "draw_arc_filled",
    "draw_arc_outline",
    "draw_parabola_filled",
    "draw_parabola_outline",
    "draw_circle_filled",
    "draw_circle_outline",
    "draw_ellipse_filled",
    "draw_ellipse_outline",
    "draw_line_strip",
    "draw_line",
    "draw_lines",
    "draw_point",
    "draw_points",
    "draw_polygon_filled",
    "draw_polygon_outline",
    "draw_triangle_filled",
    "draw_triangle_outline",
    "draw_lrbt_rectangle_outline",
    "draw_lbwh_rectangle_outline",
    "draw_rect_outline",
    "draw_lrbt_rectangle_filled",
    "draw_lbwh_rectangle_filled",
    "draw_rect_filled",
    "draw_rect_outline_kwargs",
    "draw_rect_filled_kwargs",
    "draw_scaled_texture_rectangle",
    "draw_texture_rectangle",
    "draw_lbwh_rectangle_textured",
    "get_pixel",
    "get_image"
]

# --- BEGIN ARC FUNCTIONS # # #


[docs] def draw_arc_filled(center_x: float, center_y: float, width: float, height: float, color: RGBA255, start_angle: float, end_angle: float, tilt_angle: float = 0, num_segments: int = 128): """ Draw a filled in arc. Useful for drawing pie-wedges, or Pac-Man. :param center_x: x position that is the center of the arc. :param center_y: y position that is the center of the arc. :param width: width of the arc. :param height: height of the arc. :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param start_angle: start angle of the arc in degrees. :param end_angle: end angle of the arc in degrees. :param tilt_angle: angle the arc is tilted (clockwise). :param num_segments: Number of line segments used to draw arc. """ unrotated_point_list = [(0.0, 0.0)] start_segment = int(start_angle / 360 * num_segments) end_segment = int(end_angle / 360 * num_segments) for segment in range(start_segment, end_segment + 1): theta = 2.0 * 3.1415926 * segment / num_segments x = width * math.cos(theta) / 2 y = height * math.sin(theta) / 2 unrotated_point_list.append((x, y)) if tilt_angle == 0: uncentered_point_list = unrotated_point_list else: uncentered_point_list = [rotate_point(point[0], point[1], 0, 0, tilt_angle) for point in unrotated_point_list] point_list = [(point[0] + center_x, point[1] + center_y) for point in uncentered_point_list] _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLE_FAN)
[docs] def draw_arc_outline(center_x: float, center_y: float, width: float, height: float, color: RGBA255, start_angle: float, end_angle: float, border_width: float = 1, tilt_angle: float = 0, num_segments: int = 128): """ Draw the outside edge of an arc. Useful for drawing curved lines. :param center_x: x position that is the center of the arc. :param center_y: y position that is the center of the arc. :param width: width of the arc. :param height: height of the arc. :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param start_angle: start angle of the arc in degrees. :param end_angle: end angle of the arc in degrees. :param border_width: width of line in pixels. :param tilt_angle: angle the arc is tilted (clockwise). :param num_segments: float of triangle segments that make up this circle. Higher is better quality, but slower render time. """ unrotated_point_list = [] start_segment = int(start_angle / 360 * num_segments) end_segment = int(end_angle / 360 * num_segments) inside_width = (width - border_width / 2) / 2 outside_width = (width + border_width / 2) / 2 inside_height = (height - border_width / 2) / 2 outside_height = (height + border_width / 2) / 2 for segment in range(start_segment, end_segment + 1): theta = 2.0 * math.pi * segment / num_segments x1 = inside_width * math.cos(theta) y1 = inside_height * math.sin(theta) x2 = outside_width * math.cos(theta) y2 = outside_height * math.sin(theta) unrotated_point_list.append((x1, y1)) unrotated_point_list.append((x2, y2)) if tilt_angle == 0: uncentered_point_list = unrotated_point_list else: uncentered_point_list = [rotate_point(point[0], point[1], 0, 0, tilt_angle) for point in unrotated_point_list] point_list = [(point[0] + center_x, point[1] + center_y) for point in uncentered_point_list] _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLE_STRIP)
# --- END ARC FUNCTIONS # # # # --- BEGIN PARABOLA FUNCTIONS # # #
[docs] def draw_parabola_filled(start_x: float, start_y: float, end_x: float, height: float, color: RGBA255, tilt_angle: float = 0): """ Draws a filled in parabola. :param start_x: The starting x position of the parabola :param start_y: The starting y position of the parabola :param end_x: The ending x position of the parabola :param height: The height of the parabola :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param tilt_angle: The angle of the tilt of the parabola (clockwise) """ center_x = (start_x + end_x) / 2 center_y = start_y + height start_angle = 0 end_angle = 180 width = start_x - end_x draw_arc_filled(center_x, center_y, width, height, color, start_angle, end_angle, tilt_angle)
[docs] def draw_parabola_outline(start_x: float, start_y: float, end_x: float, height: float, color: RGBA255, border_width: float = 1, tilt_angle: float = 0): """ Draws the outline of a parabola. :param start_x: The starting x position of the parabola :param start_y: The starting y position of the parabola :param end_x: The ending x position of the parabola :param height: The height of the parabola :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param border_width: The width of the parabola :param tilt_angle: The angle of the tilt of the parabola (clockwise) """ center_x = (start_x + end_x) / 2 center_y = start_y + height start_angle = 0 end_angle = 180 width = start_x - end_x draw_arc_outline(center_x, center_y, width, height, color, start_angle, end_angle, border_width, tilt_angle)
# --- END PARABOLA FUNCTIONS # # # # --- BEGIN CIRCLE FUNCTIONS # # #
[docs] def draw_circle_filled(center_x: float, center_y: float, radius: float, color: RGBA255, tilt_angle: float = 0, num_segments: int = -1): """ Draw a filled-in circle. :param center_x: x position that is the center of the circle. :param center_y: y position that is the center of the circle. :param radius: width of the circle. :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param tilt_angle: Angle in degrees to tilt the circle. Useful for low segment count circles :param num_segments: Number of triangle segments that make up this circle. Higher is better quality, but slower render time. The default value of -1 means arcade will try to calculate a reasonable amount of segments based on the size of the circle. """ draw_ellipse_filled(center_x, center_y, radius * 2, radius * 2, color, tilt_angle=tilt_angle, num_segments=num_segments)
[docs] def draw_circle_outline(center_x: float, center_y: float, radius: float, color: RGBA255, border_width: float = 1, tilt_angle: float = 0, num_segments: int = -1): """ Draw the outline of a circle. :param center_x: x position that is the center of the circle. :param center_y: y position that is the center of the circle. :param radius: width of the circle. :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param border_width: Width of the circle outline in pixels. :param tilt_angle: Angle in degrees to tilt the circle (clockwise). Useful for low segment count circles :param num_segments: Number of triangle segments that make up this circle. Higher is better quality, but slower render time. The default value of -1 means arcade will try to calculate a reasonable amount of segments based on the size of the circle. """ draw_ellipse_outline(center_x=center_x, center_y=center_y, width=radius * 2, height=radius * 2, color=color, border_width=border_width, tilt_angle=tilt_angle, num_segments=num_segments)
# --- END CIRCLE FUNCTIONS # # # # --- BEGIN ELLIPSE FUNCTIONS # # #
[docs] def draw_ellipse_filled(center_x: float, center_y: float, width: float, height: float, color: RGBA255, tilt_angle: float = 0, num_segments: int = -1): """ Draw a filled in ellipse. :param center_x: x position that is the center of the circle. :param center_y: y position that is the center of the circle. :param width: width of the ellipse. :param height: height of the ellipse. :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param color: Either a :py:class:`~arcade.types.Color` instance or an RGBA :py:class:`tuple` of 4 byte values (0 to 255). :param tilt_angle: Angle in degrees to tilt the ellipse (clockwise). Useful when drawing a circle with a low segment count, to make an octagon for example. :param num_segments: Number of triangle segments that make up this circle. Higher is better quality, but slower render time. The default value of -1 means arcade will try to calculate a reasonable amount of segments based on the size of the circle. """ # Fail immediately if we have no window or context window = get_window() ctx = window.ctx program = ctx.shape_ellipse_filled_unbuffered_program geometry = ctx.shape_ellipse_unbuffered_geometry buffer = ctx.shape_ellipse_unbuffered_buffer # Normalize the color because this shader takes a float uniform color_normalized = Color.from_iterable(color).normalized # Pass data to the shader program['color'] = color_normalized program['shape'] = width / 2, height / 2, tilt_angle program['segments'] = num_segments buffer.orphan() buffer.write(data=array.array('f', (center_x, center_y))) geometry.render(program, mode=gl.GL_POINTS, vertices=1)
[docs] def draw_ellipse_outline(center_x: float, center_y: float, width: float, height: float, color: RGBA255, border_width: float = 1, tilt_angle: float = 0, num_segments: int = -1): """ Draw the outline of an ellipse. :param center_x: x position that is the center of the circle. :param center_y: y position that is the center of the circle. :param width: width of the ellipse. :param height: height of the ellipse. :param color: A 3 or 4 length tuple of 0-255 channel values or a :py:class:`~arcade.types.Color` instance. :param border_width: Width of the circle outline in pixels. :param tilt_angle: Angle in degrees to tilt the ellipse (clockwise). Useful when drawing a circle with a low segment count, to make an octagon for example. :param num_segments: Number of triangle segments that make up this circle. Higher is better quality, but slower render time. The default value of -1 means arcade will try to calculate a reasonable amount of segments based on the size of the circle. """ # Fail immediately if we have no window or context window = get_window() ctx = window.ctx program = ctx.shape_ellipse_outline_unbuffered_program geometry = ctx.shape_ellipse_outline_unbuffered_geometry buffer = ctx.shape_ellipse_outline_unbuffered_buffer # Normalize the color because this shader takes a float uniform color_normalized = Color.from_iterable(color).normalized # Pass data to shader program['color'] = color_normalized program['shape'] = width / 2, height / 2, tilt_angle, border_width program['segments'] = num_segments buffer.orphan() buffer.write(data=array.array('f', (center_x, center_y))) geometry.render(program, mode=gl.GL_POINTS, vertices=1)
# --- END ELLIPSE FUNCTIONS # # # # --- BEGIN LINE FUNCTIONS # # # def _generic_draw_line_strip(point_list: PointList, color: RGBA255, mode: int = gl.GL_LINE_STRIP): """ Draw a line strip. A line strip is a set of continuously connected line segments. :param point_list: List of points making up the line. Each point is in a list. So it is a list of lists. :param color: A color, specified as an RGBA tuple or a :py:class:`~arcade.types.Color` instance. """ # Fail if we don't have a window, context, or right GL abstractions window = get_window() ctx = window.ctx geometry = ctx.generic_draw_line_strip_geometry vertex_buffer = ctx.generic_draw_line_strip_vbo color_buffer = ctx.generic_draw_line_strip_color program = ctx.line_vertex_shader # Validate and alpha-pad color, then expand to multi-vertex form since # this shader normalizes internally as if made to draw multicolor lines. rgba = Color.from_iterable(color) num_vertices = len(point_list) # Fail if it isn't a sized / sequence object # Translate Python objects into types arcade's Buffer objects accept color_array = array.array('B', rgba * num_vertices) vertex_array = array.array('f', tuple(item for sublist in point_list for item in sublist)) geometry.num_vertices = num_vertices # Double buffer sizes until they can hold all our data goal_vertex_buffer_size = len(vertex_array) * 4 while goal_vertex_buffer_size > vertex_buffer.size: vertex_buffer.orphan(color_buffer.size * 2) color_buffer.orphan(color_buffer.size * 2) else: vertex_buffer.orphan() color_buffer.orphan() # Write data & render vertex_buffer.write(vertex_array) color_buffer.write(color_array) geometry.render(program, mode=mode)
[docs] def draw_line_strip(point_list: PointList, color: RGBA255, line_width: float = 1): """ Draw a multi-point line. :param point_list: List of x, y points that make up this strip :param color: A color, specified as an RGBA tuple or a :py:class:`~arcade.types.Color` instance. :param line_width: Width of the line """ if line_width == 1: _generic_draw_line_strip(point_list, color, gl.GL_LINE_STRIP) else: triangle_point_list: List[Point] = [] # This needs a lot of improvement last_point = None for point in point_list: if last_point is not None: points = get_points_for_thick_line(last_point[0], last_point[1], point[0], point[1], line_width) reordered_points = points[1], points[0], points[2], points[3] triangle_point_list.extend(reordered_points) last_point = point _generic_draw_line_strip(triangle_point_list, color, gl.GL_TRIANGLE_STRIP)
[docs] def draw_line(start_x: float, start_y: float, end_x: float, end_y: float, color: RGBA255, line_width: float = 1): """ Draw a line. :param start_x: x position of line starting point. :param start_y: y position of line starting point. :param end_x: x position of line ending point. :param end_y: y position of line ending point. :param color: A color, specified as an RGBA tuple or a :py:class:`~arcade.types.Color` instance. :param line_width: Width of the line in pixels. """ # Fail if we don't have a window, context, or right GL abstractions window = get_window() ctx = window.ctx program = ctx.shape_line_program geometry = ctx.shape_line_geometry line_pos_buffer = ctx.shape_line_buffer_pos # Validate & normalize to a pass the shader an RGBA float uniform color_normalized = Color.from_iterable(color).normalized # Pass data to the shader program['color'] = color_normalized program['line_width'] = line_width line_pos_buffer.orphan() # Allocate new buffer internally line_pos_buffer.write( data=array.array('f', (start_x, start_y, end_x, end_y))) geometry.render(program, mode=gl.GL_LINES, vertices=2)
[docs] def draw_lines(point_list: PointList, color: RGBA255, line_width: float = 1): """ Draw a set of lines. Draw a line between each pair of points specified. :param point_list: List of points making up the lines. Each point is in a list. So it is a list of lists. :param color: A color, specified as an RGBA tuple or a :py:class:`~arcade.types.Color` instance. :param line_width: Width of the line in pixels. """ # Fail if we don't have a window, context, or right GL abstractions window = get_window() ctx = window.ctx program = ctx.shape_line_program geometry = ctx.shape_line_geometry line_buffer_pos = ctx.shape_line_buffer_pos # Validate & normalize to a pass the shader an RGBA float uniform color_normalized = Color.from_iterable(color).normalized line_pos_array = array.array('f', (v for point in point_list for v in point)) num_points = len(point_list) # Grow buffer until large enough to hold all our data goal_buffer_size = num_points * 3 * 4 while goal_buffer_size > line_buffer_pos.size: ctx.shape_line_buffer_pos.orphan(line_buffer_pos.size * 2) else: ctx.shape_line_buffer_pos.orphan() # Pass data to shader program['line_width'] = line_width program['color'] = color_normalized line_buffer_pos.write(data=line_pos_array) geometry.render(program, mode=gl.GL_LINES, vertices=num_points)
# --- BEGIN POINT FUNCTIONS # # #
[docs] def draw_point(x: float, y: float, color: RGBA255, size: float): """ Draw a point. :param x: x position of point. :param y: y position of point. :param color: A color, specified as an RGBA tuple or a :py:class:`~arcade.types.Color` instance. :param size: Size of the point in pixels. """ draw_rect_filled(XYWH(x, y, size, size), color)
[docs] def draw_points(point_list: PointList, color: RGBA255, size: float = 1): """ Draw a set of points. :param point_list: List of points Each point is in a list. So it is a list of lists. :param color: A color, specified as an RGBA tuple or a :py:class:`~arcade.types.Color` instance. :param size: Size of the point in pixels. """ # Fails immediately if we don't have a window or context window = get_window() ctx = window.ctx program = ctx.shape_rectangle_filled_unbuffered_program geometry = ctx.shape_rectangle_filled_unbuffered_geometry buffer = ctx.shape_rectangle_filled_unbuffered_buffer # Validate & normalize to a pass the shader an RGBA float uniform color_normalized = Color.from_iterable(color).normalized # Get # of points and translate Python tuples to a C-style array num_points = len(point_list) point_array = array.array('f', (v for point in point_list for v in point)) # Resize buffer data_size = num_points * 8 # if data_size > buffer.size: buffer.orphan(size=data_size) # Pass data to shader program['color'] = color_normalized program['shape'] = size, size, 0 buffer.write(data=point_array) # Only render the # of points we have complete data for geometry.render(program, mode=ctx.POINTS, vertices=data_size // 8)
# --- END POINT FUNCTIONS # # # # --- BEGIN POLYGON FUNCTIONS # # #
[docs] def draw_polygon_filled(point_list: Point2List, color: RGBA255): """ Draw a polygon that is filled in. :param point_list: List of points making up the lines. Each point is in a list. So it is a list of lists. :param color: The color, specified in RGB or RGBA format. """ triangle_points = earclip(point_list) flattened_list = tuple(i for g in triangle_points for i in g) _generic_draw_line_strip(flattened_list, color, gl.GL_TRIANGLES)
[docs] def draw_polygon_outline(point_list: Point2List, color: RGBA255, line_width: float = 1): """ Draw a polygon outline. Also known as a "line loop." :param point_list: List of points making up the lines. Each point is in a list. So it is a list of lists. :param color: The color of the outline as an RGBA :py:class:`tuple` or :py:class:`~arcade.types.Color` instance. :param line_width: Width of the line in pixels. """ # Convert to modifiable list & close the loop new_point_list = list(point_list) new_point_list.append(point_list[0]) # Create a place to store the triangles we'll use to thicken the line triangle_point_list = [] # This needs a lot of improvement last_point = None for point in new_point_list: if last_point is not None: # Calculate triangles, then re-order to link up the quad? points = get_points_for_thick_line(*last_point, *point, line_width) reordered_points = points[1], points[0], points[2], points[3] triangle_point_list.extend(reordered_points) last_point = point # Use first two points of new list to close the loop new_start, new_next = new_point_list[:2] s_x, s_y = new_start n_x, n_y = new_next points = get_points_for_thick_line(s_x, s_y, n_x, n_y, line_width) triangle_point_list.append(points[1]) _generic_draw_line_strip(triangle_point_list, color, gl.GL_TRIANGLE_STRIP)
[docs] def draw_triangle_filled(x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, color: RGBA255): """ Draw a filled in triangle. :param x1: x value of first coordinate. :param y1: y value of first coordinate. :param x2: x value of second coordinate. :param y2: y value of second coordinate. :param x3: x value of third coordinate. :param y3: y value of third coordinate. :param color: Color of the triangle as an RGBA :py:class:`tuple` or :py:class:`~arcade.types.Color` instance. """ point_list = ( (x1, y1), (x2, y2), (x3, y3), ) _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLES)
[docs] def draw_triangle_outline(x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, color: RGBA255, border_width: float = 1): """ Draw a the outline of a triangle. :param x1: x value of first coordinate. :param y1: y value of first coordinate. :param x2: x value of second coordinate. :param y2: y value of second coordinate. :param x3: x value of third coordinate. :param y3: y value of third coordinate. :param color: RGBA255 of triangle as an RGBA :py:class:`tuple` or :py:class`~arcade.types.Color` instance. :param border_width: Width of the border in pixels. Defaults to 1. """ point_list = ( (x1, y1), (x2, y2), (x3, y3), ) draw_polygon_outline(point_list, color, border_width)
# --- END POLYGON FUNCTIONS # # # # --- BEGIN RECTANGLE FUNCTIONS # # #
[docs] def draw_lrbt_rectangle_outline(left: float, right: float, bottom: float, top: float, color: RGBA255, border_width: float = 1): """ Draw a rectangle by specifying left, right, bottom and top edges. :param left: The x coordinate of the left edge of the rectangle. :param right: The x coordinate of the right edge of the rectangle. :param bottom: The y coordinate of the rectangle bottom. :param top: The y coordinate of the top of the rectangle. :param color: The color of the rectangle. :param border_width: The width of the border in pixels. Defaults to one. :Raises ValueError: Raised if left > right or top < bottom. """ if left > right: raise ValueError("Left coordinate must be less than or equal to " "the right coordinate") if bottom > top: raise ValueError("Bottom coordinate must be less than or equal to " "the top coordinate") draw_rect_outline(LRBT(left, right, bottom, top), color, border_width)
[docs] def draw_lbwh_rectangle_outline(left: float, bottom: float, width: float, height: float, color: RGBA255, border_width: float = 1): """ Draw a rectangle extending from bottom left to top right :param bottom_left_x: The x coordinate of the left edge of the rectangle. :param bottom_left_y: The y coordinate of the bottom of the rectangle. :param width: The width of the rectangle. :param height: The height of the rectangle. :param color: The color of the rectangle as an RGBA :py:class:`tuple` or :py:class`~arcade.types.Color` instance. :param border_width: The width of the border in pixels. Defaults to one. """ draw_rect_outline(LBWH(left, bottom, width, height), color, border_width)
[docs] def draw_lrbt_rectangle_filled(left: float, right: float, bottom: float, top: float, color: RGBA255): """ Draw a rectangle by specifying left, right, bottom and top edges. :param left: The x coordinate of the left edge of the rectangle. :param right: The x coordinate of the right edge of the rectangle. :param bottom: The y coordinate of the rectangle bottom. :param top: The y coordinate of the top of the rectangle. :param color: The color of the rectangle. :Raises ValueError: Raised if left > right or top < bottom. """ if left > right: raise ValueError(f"Left coordinate {left} must be less than or equal to the right coordinate {right}") if bottom > top: raise ValueError(f"Bottom coordinate {bottom} must be less than or equal to the top coordinate {top}") draw_rect_filled(LRBT(left, right, bottom, top), color)
[docs] def draw_lbwh_rectangle_filled(left: float, bottom: float, width: float, height: float, color: RGBA255): """ Draw a filled rectangle extending from bottom left to top right :param left: The x coordinate of the left edge of the rectangle. :param bottom: The y coordinate of the bottom of the rectangle. :param width: The width of the rectangle. :param height: The height of the rectangle. :param color: The color of the rectangles an RGBA :py:class:`tuple` or :py:class`~arcade.types.Color` instance. """ draw_rect_filled(LBWH(left, bottom, width, height), color)
[docs] def draw_scaled_texture_rectangle(center_x: float, center_y: float, texture: Texture, scale: float = 1.0, angle: float = 0, alpha: int = 255): """ Draw a textured rectangle on-screen. .. warning:: This method can be slow! Most users should consider using :py:class:`arcade.Sprite` with :py:class:`arcade.SpriteList` instead of this function. OpenGL accelerates drawing by using batches to draw multiple things at once. This method doesn't do that. If you need finer control or less overhead than arcade allows, consider `pyglet's batching features <https://pyglet.readthedocs.io/en/master/modules/graphics/index.html#batches-and-groups>`_. :param center_x: x coordinate of rectangle center. :param center_y: y coordinate of rectangle center. :param texture: identifier of texture returned from load_texture() call :param scale: scale of texture :param angle: rotation of the rectangle (clockwise). Defaults to zero. :param alpha: Transparency of image. 0 is fully transparent, 255 (default) is fully visible """ texture.draw_scaled(center_x, center_y, scale, angle, alpha)
[docs] def draw_texture_rectangle(center_x: float, center_y: float, width: float, height: float, texture: Texture, angle: float = 0, alpha: int = 255): """ Draw a textured rectangle on-screen. :param center_x: x coordinate of rectangle center. :param center_y: y coordinate of rectangle center. :param width: width of texture :param height: height of texture :param texture: identifier of texture returned from load_texture() call :param angle: rotation of the rectangle. Defaults to zero (clockwise). :param alpha: Transparency of image. 0 is fully transparent, 255 (default) is visible """ texture.draw_sized(center_x, center_y, width, height, angle, alpha)
[docs] def draw_lbwh_rectangle_textured(left: float, bottom: float, width: float, height: float, texture: Texture, angle: float = 0, alpha: int = 255): """ Draw a texture extending from bottom left to top right. :param left: The x coordinate of the left edge of the rectangle. :param bottom: The y coordinate of the bottom of the rectangle. :param width: The width of the rectangle. :param height: The height of the rectangle. :param texture: identifier of texture returned from load_texture() call :param angle: rotation of the rectangle. Defaults to zero (clockwise). :param alpha: Transparency of image. 0 is fully transparent, 255 (default) is visible """ center_x = left + (width / 2) center_y = bottom + (height / 2) texture.draw_sized(center_x, center_y, width, height, angle=angle, alpha=alpha)
# Reference implementations: drawing of new Rect
[docs] def draw_rect_outline(rect: Rect, color: RGBA255, border_width: float = 1, tilt_angle: float = 0): """ Draw a rectangle outline. :param rect: The rectangle to draw. a :py:class`~arcade.types.Rect` instance. :param color: The color of the rectangle. :py:class:`tuple` or :py:class`~arcade.types.Color` instance. :param border_width: width of the lines, in pixels. :param tilt_angle: rotation of the rectangle. Defaults to zero (clockwise). """ HALF_BORDER = border_width / 2 i_lb = rect.bottom_left.x + HALF_BORDER, rect.bottom_left.y + HALF_BORDER i_rb = rect.bottom_right.x - HALF_BORDER, rect.bottom_right.y + HALF_BORDER i_rt = rect.top_right.x - HALF_BORDER, rect.top_right.y - HALF_BORDER i_lt = rect.top_left.x + HALF_BORDER, rect.top_left.y - HALF_BORDER o_lb = rect.bottom_left.x - HALF_BORDER, rect.bottom_left.y - HALF_BORDER o_rb = rect.bottom_right.x + HALF_BORDER, rect.bottom_right.y - HALF_BORDER o_rt = rect.top_right.x + HALF_BORDER, rect.top_right.y + HALF_BORDER o_lt = rect.top_left.x - HALF_BORDER, rect.top_right.y + HALF_BORDER point_list: PointList = (o_lt, i_lt, o_rt, i_rt, o_rb, i_rb, o_lb, i_lb, o_lt, i_lt) if tilt_angle != 0: point_list_2 = [] for point in point_list: new_point = rotate_point(point[0], point[1], rect.x, rect.y, tilt_angle) point_list_2.append(new_point) point_list = point_list_2 _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLE_STRIP)
[docs] def draw_rect_filled(rect: Rect, color: RGBA255, tilt_angle: float = 0): """ Draw a filled-in rectangle. :param rect: The rectangle to draw. a :py:class`~arcade.types.Rect` instance. :param color: The color of the rectangle as an RGBA :py:class:`tuple` or :py:class`~arcade.types.Color` instance. :param tilt_angle: rotation of the rectangle (clockwise). Defaults to zero. """ # Fail if we don't have a window, context, or right GL abstractions window = get_window() ctx = window.ctx program = ctx.shape_rectangle_filled_unbuffered_program geometry = ctx.shape_rectangle_filled_unbuffered_geometry buffer = ctx.shape_rectangle_filled_unbuffered_buffer # Validate & normalize to a pass the shader an RGBA float uniform color_normalized = Color.from_iterable(color).normalized # Pass data to the shader program['color'] = color_normalized program['shape'] = rect.width, rect.height, tilt_angle buffer.orphan() buffer.write(data=array.array('f', (rect.x, rect.y))) geometry.render(program, mode=ctx.POINTS, vertices=1)
[docs] def draw_rect_outline_kwargs(color: RGBA255 = WHITE, border_width: int = 1, tilt_angle: float = 0, **kwargs: AsFloat): rect = Rect.from_kwargs(**kwargs) draw_rect_outline(rect, color, border_width, tilt_angle)
[docs] def draw_rect_filled_kwargs(color: RGBA255 = WHITE, tilt_angle: float = 0, **kwargs: AsFloat): rect = Rect.from_kwargs(**kwargs) draw_rect_filled(rect, color, tilt_angle)
# Get_ functions
[docs] def get_pixel(x: int, y: int, components: int = 3) -> Tuple[int, ...]: """ Given an x, y, will return a color value of that point. :param x: x location :param y: y location :param components: Number of components to fetch. By default we fetch 3 3 components (RGB). 4 components would be RGBA. """ # noinspection PyCallingNonCallable,PyTypeChecker # The window may be 'scaled' on hi-res displays. Particularly Macs. OpenGL # won't account for this, so we need to. window = get_window() pixel_ratio = window.get_pixel_ratio() x = int(pixel_ratio * x) y = int(pixel_ratio * y) a = (gl.GLubyte * 4)(0) gl.glReadPixels(x, y, 1, 1, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, a) return tuple(int(i) for i in a[:components])
[docs] def get_image(x: int = 0, y: int = 0, width: Optional[int] = None, height: Optional[int] = None) -> PIL.Image.Image: """ Get an image from the screen. Example:: image = get_image() image.save('screenshot.png', 'PNG') :param x: Start (left) x location :param y: Start (top) y location :param width: Width of image. Leave blank for grabbing the 'rest' of the image :param height: Height of image. Leave blank for grabbing the 'rest' of the image :returns: A Pillow Image """ window = get_window() pixel_ratio = window.get_pixel_ratio() x = int(pixel_ratio * x) y = int(pixel_ratio * y) if width is None: width = window.width - x if height is None: height = window.height - y width = int(pixel_ratio * width) height = int(pixel_ratio * height) # Create an image buffer # noinspection PyTypeChecker image_buffer = (gl.GLubyte * (4 * width * height))(0) gl.glReadPixels(x, y, width, height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, image_buffer) image = PIL.Image.frombytes("RGBA", (width, height), image_buffer) image = PIL.ImageOps.flip(image) return image