def _create_rects(rect_list: Iterable[Sprite]) -> List[float]: """ Create a vertex buffer for a set of rectangles. """ v2f = [] for shape in rect_list: x1 = -shape.width / 2 + shape.center_x x2 = shape.width / 2 + shape.center_x y1 = -shape.height / 2 + shape.center_y y2 = shape.height / 2 + shape.center_y p1 = [x1, y1] p2 = [x2, y1] p3 = [x2, y2] p4 = [x1, y2] if shape.angle: p1 = rotate_point(p1[0], p1[1], shape.center_x, shape.center_y, shape.angle) p2 = rotate_point(p2[0], p2[1], shape.center_x, shape.center_y, shape.angle) p3 = rotate_point(p3[0], p3[1], shape.center_x, shape.center_y, shape.angle) p4 = rotate_point(p4[0], p4[1], shape.center_x, shape.center_y, shape.angle) v2f.extend([p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], p4[0], p4[1]]) return v2f
def get_adjusted_hit_box(self) -> List[List[float]]: """ Get the points that make up the hit box for the rect that makes up the sprite, including rotation and scaling. """ # If we've already calculated the adjusted hit box, use the cached version if self._point_list_cache is not None: return self._point_list_cache # If there is no hitbox, use the width/height to get one if self._points is None and self._texture: self._points = self._texture.hit_box_points if self._points is None and self._width: x1, y1 = -self._width / 2, -self._height / 2 x2, y2 = +self._width / 2, -self._height / 2 x3, y3 = +self._width / 2, +self._height / 2 x4, y4 = -self._width / 2, +self._height / 2 self._points = [[x1, y1], [x2, y2], [x3, y3], [x4, y4]] if self._points is None and self.texture is not None: self._points = self.texture.hit_box_points if self._points is None: raise ValueError( "Error trying to get the hit box of a sprite, when no hit box is set.\nPlease make sure the " "Sprite.texture is set to a texture before trying to draw or do collision testing.\n" "Alternatively, manually call Sprite.set_hit_box with points for your hitbox." ) # Adjust the hitbox point_list = [] for point in self._points: # Get a copy of the point point = [point[0], point[1]] # Scale the point if self.scale != 1: point[0] *= self.scale point[1] *= self.scale # Rotate the point if self.angle: point = rotate_point(point[0], point[1], 0, 0, self.angle) # Offset the point point = [point[0] + self.center_x, point[1] + self.center_y] point_list.append(point) # Cache the results self._point_list_cache = point_list # if self.texture: # print(self.texture.name, self._point_list_cache) return self._point_list_cache
def get_rectangle_points(center_x: float, center_y: float, width: float, height: float, tilt_angle: float = 0): """ Utility function that will return all four coordinate points of a rectangle given the center_x, center_y, width, height, and rotation. Args: center_x: center_y: width: height: tilt_angle: Returns: """ x1 = -width / 2 + center_x y1 = -height / 2 + center_y x2 = -width / 2 + center_x y2 = height / 2 + center_y x3 = width / 2 + center_x y3 = height / 2 + center_y x4 = width / 2 + center_x y4 = -height / 2 + center_y if tilt_angle: x1, y1 = rotate_point(x1, y1, center_x, center_y, tilt_angle) x2, y2 = rotate_point(x2, y2, center_x, center_y, tilt_angle) x3, y3 = rotate_point(x3, y3, center_x, center_y, tilt_angle) x4, y4 = rotate_point(x4, y4, center_x, center_y, tilt_angle) data = [(x1, y1), (x2, y2), (x3, y3), (x4, y4)] return data
def draw_ellipse(center_x: float, center_y: float, width: float, height: float, color: Color, border_width: float = 1, tilt_angle: float = 0, num_segments: int = 32, filled=True): """ Draw an ellipse. Note: This can't be unit tested on Appveyor because its support for OpenGL is poor. """ # Create an array with the vertex point_list point_list = [] for segment in range(num_segments): theta = 2.0 * 3.1415926 * segment / num_segments x = width * math.cos(theta) + center_x y = height * math.sin(theta) + center_y if tilt_angle: x, y = rotate_point(x, y, center_x, center_y, tilt_angle) point_list.append((x, y)) if filled: id = f"ellipse-filled-{center_x}-{center_y}-{width}-{height}-{color}-{border_width}-{tilt_angle}-{num_segments}" if id not in buffered_shapes.keys(): half = len(point_list) // 2 interleaved = itertools.chain.from_iterable( itertools.zip_longest(point_list[:half], reversed(point_list[half:]))) point_list = [p for p in interleaved if p is not None] shape_mode = gl.GL_TRIANGLE_STRIP shape = _create_line_generic(point_list, color, shape_mode, border_width) buffered_shapes[id] = shape buffered_shapes[id].draw() else: id = f"ellipse-empty-{center_x}-{center_y}-{width}-{height}-{color}-{border_width}-{tilt_angle}-{num_segments}" if id not in buffered_shapes.keys(): point_list.append(point_list[0]) shape_mode = gl.GL_LINE_STRIP shape = _create_line_generic(point_list, color, shape_mode, border_width) buffered_shapes[id] = shape buffered_shapes[id].draw()
def draw_arc_filled(center_x: float, center_y: float, width: float, height: float, color: Color, 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 float center_x: x position that is the center of the arc. :param float center_y: y position that is the center of the arc. :param float width: width of the arc. :param float height: height of the arc. :param Color color: color, specified in a list of 3 or 4 bytes in RGB or RGBA format. :param float start_angle: start angle of the arc in degrees. :param float end_angle: end angle of the arc in degrees. :param float tilt_angle: angle the arc is tilted. :param float 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 = [] for point in unrotated_point_list: uncentered_point_list.append( rotate_point(point[0], point[1], 0, 0, tilt_angle)) point_list = [] for point in uncentered_point_list: point_list.append((point[0] + center_x, point[1] + center_y)) _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLE_FAN)
def draw_ellipse_filled_with_colors(center_x: float, center_y: float, width: float, height: float, outside_color: Color, inside_color: Color, tilt_angle: float = 0, num_segments: int = 32): """ Draw an ellipse, and specify inside/outside color. Used for doing gradients. :param float center_x: :param float center_y: :param float width: :param float height: :param Color outside_color: :param float inside_color: :param float tilt_angle: :param int num_segments: :Returns Shape: """ # Create an array with the vertex data # Create an array with the vertex point_list point_list = [(center_x, center_y)] for segment in range(num_segments): theta = 2.0 * 3.1415926 * segment / num_segments x = width * math.cos(theta) + center_x y = height * math.sin(theta) + center_y if tilt_angle: x, y = rotate_point(x, y, center_x, center_y, tilt_angle) point_list.append((x, y)) point_list.append(point_list[1]) color_list = [inside_color] + [outside_color] * (num_segments + 1) id = f"ellipse-filled-with-colors-{point_list}-{color_list}" if id not in buffered_shapes.keys(): shape = _create_line_generic_with_colors(point_list, color_list, gl.GL_TRIANGLE_FAN) buffered_shapes[id] = shape buffered_shapes[id].draw()
def draw_rectangle(center_x: float, center_y: float, width: float, height: float, color: Color, border_width: float = 1, tilt_angle: float = 0, filled=True): """ Draw a rectangle. Args: center_x: center_y: width: height: color: border_width: tilt_angle: filled: Returns: """ data: List[Point] = cast( List[Point], get_rectangle_points(center_x, center_y, width, height, tilt_angle)) if filled: shape_mode = gl.GL_TRIANGLE_STRIP data[-2:] = reversed(data[-2:]) id = f"rect-filled-{data}-{color}-{border_width}" else: i_lb = center_x - width / 2 + border_width / 2, center_y - height / 2 + border_width / 2 i_rb = center_x + width / 2 - border_width / 2, center_y - height / 2 + border_width / 2 i_rt = center_x + width / 2 - border_width / 2, center_y + height / 2 - border_width / 2 i_lt = center_x - width / 2 + border_width / 2, center_y + height / 2 - border_width / 2 o_lb = center_x - width / 2 - border_width / 2, center_y - height / 2 - border_width / 2 o_rb = center_x + width / 2 + border_width / 2, center_y - height / 2 - border_width / 2 o_rt = center_x + width / 2 + border_width / 2, center_y + height / 2 + border_width / 2 o_lt = center_x - width / 2 - border_width / 2, center_y + height / 2 + border_width / 2 data = [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: List[Point] = [] for point in data: new_point = rotate_point(point[0], point[1], center_x, center_y, tilt_angle) point_list_2.append(new_point) data = point_list_2 border_width = 1 shape_mode = gl.GL_TRIANGLE_STRIP # _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLE_STRIP) # shape_mode = gl.GL_LINE_STRIP # data.append(data[0])s id = f"rect-empty-{data}-{color}-{border_width}" if id not in buffered_shapes.keys(): shape = _create_line_generic(data, color, shape_mode, border_width) buffered_shapes[id] = shape buffered_shapes[id].draw()
def draw_arc_outline(center_x: float, center_y: float, width: float, height: float, color: Color, 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 float center_x: x position that is the center of the arc. :param float center_y: y position that is the center of the arc. :param float width: width of the arc. :param float height: height of the arc. :param Color color: color, specified in a list of 3 or 4 bytes in RGB or RGBA format. :param float start_angle: start angle of the arc in degrees. :param float end_angle: end angle of the arc in degrees. :param float border_width: width of line in pixels. :param float tilt_angle: angle the arc is tilted. :param int 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 = [] for point in unrotated_point_list: uncentered_point_list.append( rotate_point(point[0], point[1], 0, 0, tilt_angle)) point_list = [] for point in uncentered_point_list: point_list.append((point[0] + center_x, point[1] + center_y)) _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLE_STRIP)