Source code for gbvision.utils.shapes.base_shape

import abc
from typing import Type, List

import numpy as np

from gbvision.constants.types import Shape, Number, Rect, Point, Contour, Frame, Color


[docs]class BaseShape(abc.ABC): def __init__(self): raise Exception("Can't create an instance of a base shape class")
[docs] @staticmethod @abc.abstractmethod def to_bounding_rect(shape: Shape) -> Rect: """ Finds the bounding rect of this shape :param shape: The shape :return: The bounding rect of this shape, as a gbvision.Rect """
[docs] @staticmethod @abc.abstractmethod def from_bounding_rect(bounding_rect: Rect) -> Shape: """ Converts a rect to the closest possible object of this shape that is contained in it :param bounding_rect: The rect to convert :return: A shape of this type that is as close as possible to this rect """
[docs] @staticmethod @abc.abstractmethod def from_contour(cnt: Contour) -> Shape: """ Converts a single contour to this shape :param cnt: The contour :return: A shape representing this contour """
[docs] @classmethod def from_contours(cls, cnts: List[Contour]) -> List[Shape]: """ Converts the given list of contours to a list of this shape :param cnts: The contours to convert :return: A list of this shape, the same length as the contours list """ return list(map(cls.from_contour, cnts))
[docs] @staticmethod @abc.abstractmethod def collision(shape1: Shape, shape2: Shape) -> bool: """ checks if the two shapes are colliding :param shape1: the first shape :param shape2: the second shape :return: True if the shapes are colliding, False otherwise """
[docs] @staticmethod @abc.abstractmethod def area(shape: Shape) -> Number: """ calculates the area of the shape :param shape: the shape :return: the area of the shape """
[docs] @staticmethod @abc.abstractmethod def center(shape: Shape) -> Point: """ calculates the center-of-mass of the shape :param shape: the shape :return: the center of the shape """
[docs] @classmethod def root_area(cls, shape: Shape) -> Number: """ calculates the square root of the area of the shape default is the square root of cls.shape_area, but it can be overridden in case there is a simpler way\ (for example for circles) :param shape: the shape :return: the square root of the area of the shape """ return np.sqrt(cls.area(shape))
[docs] @classmethod def sort(cls, shapes: List[Shape]) -> List[Shape]: """ sorts the list of shapes by area, should be overridden to use area root in case it's better doesn't modify the given list, returns a new list :param shapes: the list of shapes to sort :return: a sorted copy of the list of shapes """ return sorted(shapes, key=cls.area, reverse=True)
[docs] @classmethod def filter_inners(cls, shapes: List[Shape]) -> List[Shape]: """ filters out all shapes that are colliding with a shape with a smaller index in the given list returns a list of all shapes that aren't colliding :param shapes: the list of shapes :return: the filtered list of shapes """ filtered_shapes = [] for i, shape in enumerate(shapes): shape_invalid = False for j in range(i): shape_invalid = cls.collision(shape, shapes[j]) if shape_invalid: break if not shape_invalid: filtered_shapes.append(shape) return filtered_shapes
[docs] @classmethod def from_contours_sorted(cls, cnts: List[Contour]) -> List[Shape]: """ Converts the given list of contours to a list of this shape, and sorts them by size :param cnts: The contours to convert :return: A list of this shape, the same length as the contours list """ return cls.sort(cls.from_contours(cnts))
[docs] @classmethod def distance_squared(cls, shape1: Shape, shape2: Shape) -> Number: """ Finds the square of the distance between the centers of the shapes :param shape1: The first shape :param shape2: The second shape :return: The square of distance between the two centers """ center1 = cls.center(shape1) center2 = cls.center(shape2) return (center1[0] - center2[0]) ** 2 + (center1[1] - center2[1]) ** 2
[docs] @classmethod def distance(cls, shape1: Shape, shape2: Shape) -> Number: """ Finds the square of the distance between the centers of the shapes :param shape1: The first shape :param shape2: The second shape :return: The square of distance between the two centers """ return np.sqrt(cls.distance_squared(shape1, shape2))
@staticmethod @abc.abstractmethod def _unsafe_draw(frame: Frame, shape: Shape, color: Color, *args, **kwargs) -> None: """ Unsafely draws this shape on the frame This method should modify the given frame and not return anything :param frame: The frame to draw on :param shape: The shape to draw :param color: The color to draw with :param args: Optional arguments to pass to the drawing function :param kwargs: Optional keyword arguments to pass to the drawing function """ @classmethod def _unsafe_draw_multiple(cls, frame: Frame, shapes: List[Shape], color: Color, *args, **kwargs) -> None: """ Unsafely draws this object on the frame This method should modify the given frame and not return anything :param frame: The frame to draw on :param shapes: The list of shapes to draw :param color: The color to draw with :param args: Optional arguments to pass to the drawing function :param kwargs: Optional keyword arguments to pass to the drawing function """ for shape in shapes: cls._unsafe_draw(frame, shape, color, *args, **kwargs)
[docs] @classmethod def draw(cls, frame: Frame, shape: Shape, color: Color, *args, **kwargs) -> Frame: """ Draws the given shape on the frame This method does not change to original frame, and instead creates a copy of it and returns the copy :param frame: The frame to draw on :param shape: The shape to draw :param color: The color to use :param args: Optional arguments to pass to the drawing function :param kwargs: Optional keyword arguments to pass to the drawing function :return: A copy of the frame with the shape drawn on it """ frame = frame.copy() cls._unsafe_draw(frame, shape, color, *args, **kwargs) return frame
[docs] @staticmethod @abc.abstractmethod def set_center(shape: Shape, new_center: Point) -> Shape: """ Returns an identical copy that has been moved to a new location Only the center should be different :param shape: The shape :param new_center: The new center, calling BaseShape.center on the return value should result in this :return: A copy of this shape, with the new center """
[docs] @classmethod def draw_multiple(cls, frame: Frame, shapes: List[Shape], color: Color, *args, **kwargs) -> Frame: """ Draws the given shapes on the frame This method does not change to original frame, and instead creates a copy of it and returns the copy :param frame: The frame to draw on :param shapes: The list of shapes to draw :param color: The color to use :param args: Optional arguments to pass to the drawing function :param kwargs: Optional keyword arguments to pass to the drawing function :return: A copy of the frame with the shape drawn on it """ frame = frame.copy() cls._unsafe_draw_multiple(frame, shapes, color, *args, **kwargs) return frame
[docs] @classmethod def sort_and_filter_inners(cls, shapes: List[Shape]): """ Sorts the given list of shapes and filters out any inner shapes :param shapes: The list of shapes :return: The list of shapes sorted, with all inner shapes filtered out """ return cls.filter_inners(cls.sort(shapes))
BaseShapeType = Type[BaseShape]