Skip to content

Axis Aligned Rectangle

Axis Aligned Rectangle class

AxisAlignedRectangle

Bases: Rectangle

Axis Aligned Rectangle class that inherits from Rectangle. It defines a rectangle that is axis-aligned, meaning:

  • its sides are parallel to the X and Y axes
  • the first point is the top-left point (considering the y-axis pointing downwards)
  • the points are ordered clockwise
Source code in otary/geometry/discrete/shape/axis_aligned_rectangle.py
class AxisAlignedRectangle(Rectangle):
    """
    Axis Aligned Rectangle class that inherits from Rectangle.
    It defines a rectangle that is axis-aligned, meaning:

     - its sides are parallel to the X and Y axes
     - the first point is the top-left point (considering the y-axis pointing downwards)
     - the points are ordered clockwise
    """

    def __init__(self, points: NDArray | list, is_cast_int: bool = False) -> None:
        super().__init__(points=points, is_cast_int=is_cast_int)

        if not self.is_axis_aligned:
            raise ValueError(
                "Trying to create an AxisAlignedRectangle from a non-axis-aligned "
                "rectangle. Please check the input points."
            )

    @classmethod
    def from_rectangle(cls, rectangle: Rectangle) -> AxisAlignedRectangle:
        """Create an AxisAlignedRectangle from an ordinary Rectangle.
        Only works if the input Rectangle forms an AxisAlignedRectangle with its points.

        If you want to create an AxisAlignedRectangle from any Rectangle, use the
        from_polygon method instead.

        Args:
            rectangle (Rectangle): Rectangle object

        Returns:
            AxisAlignedRectangle: AxisAlignedRectangle object
        """
        return cls(points=rectangle.points)

    @classmethod
    def from_polygon(cls, polygon: Polygon | NDArray) -> AxisAlignedRectangle:
        """Create an AxisAlignedRectangle from any Polygon by computing its
        Axis Aligned Bounding Box (AABB).
        The resulting AxisAlignedRectangle will be the smallest axis-aligned rectangle
        that fully contains the input polygon.

        Args:
            polygon (Polygon | NDArray): Polygon object or array of points

        Returns:
            AxisAlignedRectangle: AxisAlignedRectangle object
        """
        if isinstance(polygon, Polygon):
            polygon_arr = polygon.asarray
        elif isinstance(polygon, np.ndarray):
            polygon_arr = polygon
        else:
            raise TypeError(
                "Input must be a Polygon object or a numpy ndarray of points."
            )

        if len(polygon_arr) <= 2:
            raise ValueError(
                "Cannot create an AxisAlignedRectangle from a Polygon with less than "
                "3 points."
            )

        if len(polygon_arr.shape) != 2 or polygon_arr.shape[1] != 2:
            raise ValueError(
                "Cannot create an AxisAlignedRectangle from a Polygon with more than "
                "2 dimensions."
            )

        polygon_arr = polygon_arr.astype(np.float32)

        xmin = np.min(polygon_arr[:, 0])
        xmax = np.max(polygon_arr[:, 0])
        ymin = np.min(polygon_arr[:, 1])
        ymax = np.max(polygon_arr[:, 1])

        return cls(points=[[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax]])

    @classmethod
    def from_center(
        cls,
        center: NDArray,
        width: float,
        height: float,
        is_cast_int: bool = False,
    ) -> AxisAlignedRectangle:
        """Create an AxisAlignedRectangle from a center point

        Args:
            center (NDArray): center point of the rectangle
            width (float): width of the rectangle
            height (float): height of the rectangle
            is_cast_int (bool, optional): cast the points coordinates to int

        Returns:
            AxisAlignedRectangle: new AxisAlignedRectangle object
        """
        return cls.from_rectangle(
            super().from_center(center, width, height, is_cast_int=is_cast_int)
        )

    @property
    def height(self) -> float:
        """Get the height of the AxisAlignedRectangle

        Returns:
            float: height of the rectangle
        """
        return self.ymax - self.ymin

    @property
    def width(self) -> float:
        """Get the width of the AxisAlignedRectangle

        Returns:
            float: width of the rectangle
        """
        return self.xmax - self.xmin

    @property
    def area(self) -> float:
        """Get the area of the AxisAlignedRectangle

        Returns:
            float: area of the rectangle
        """
        return self.width * self.height

    @property
    def perimeter(self) -> float:
        """Get the perimeter of the AxisAlignedRectangle

        Returns:
            float: perimeter of the rectangle
        """
        return 2 * (self.width + self.height)

    @property
    def as_pymupdf_rect(self) -> pymupdf.Rect:
        """Get the pymupdf representation of the given Rectangle.
        Beware a pymupdf can only be straight or axis-aligned.

        See: https://pymupdf.readthedocs.io/en/latest/rect.html

        Returns:
            pymupdf.Rect: pymupdf axis-aligned Rect object
        """
        return pymupdf.Rect(x0=self.xmin, y0=self.ymin, x1=self.xmax, y1=self.ymax)

    @property
    def rotated90(self) -> AxisAlignedRectangle:
        """Get the unique other related AxisAlignedRectangle that is the same one
        but rotated 90 degrees around its center.

        Returns:
            AxisAlignedRectangle: new AxisAlignedRectangle object
        """
        return AxisAlignedRectangle.from_center(
            center=self.centroid, width=self.height, height=self.width
        )

    def rotate(
        self,
        angle: float,
        is_degree: bool = False,
        is_clockwise: bool = True,
        pivot: Optional[NDArray] = None,
    ):
        """The in-place rotate method is not supported for AxisAlignedRectangle.
        Use the rotate_transform method instead.
        """
        raise TypeError("AxisAlignedRectangle does not support arbitrary rotation.")

    def rotate_transform(
        self,
        angle: float,
        is_degree: bool = False,
        is_clockwise: bool = True,
        pivot: Optional[NDArray] = None,
    ) -> Rectangle:
        """Rotate the AxisAlignedRectangle and transform it into a Rectangle.
        The result is a Rectangle object since the result is not
        guaranteed to be axis-aligned anymore.

        Args:
            angle (float): rotation angle
            is_degree (bool, optional): whether the angle is in degree or radian.
                Defaults to False which means radians.
            is_clockwise (bool, optional): whether the rotation is clockwise or
                counter-clockwise. Defaults to True.
            pivot (NDArray, optional): pivot point.
                Defaults to None which means that by default the centroid point of
                the shape is taken as the pivot point.

        Returns:
            Rectangle: object resulting from the rotation

        Example:
            The rotate method does not modify the original AxisAlignedRectangle object:
            ```python
            >>> rect = ot.AxisAlignedRectangle(points=[[0, 0], [4, 0], [4, 3], [0, 3]])
            >>> rot_rect = rect.rotate(angle=45, is_degree=True)
            >>> rot_rect.asarray
            array([[ 1.6464466 , -0.9748737 ],
                   [ 4.4748735 ,  1.8535534 ],
                   [ 2.3535533 ,  3.9748738 ],
                   [-0.47487372,  1.1464466 ]], dtype=float32)
            >>> rect
            array([[0, 0],
                   [4, 0],
                   [4, 3],
                   [0, 3]], dtype=int32)
            ```
        """
        # pylint: disable=R0801
        if pivot is None:
            pivot = self.centroid

        rot_points = rotate_2d_points(
            points=self.points,
            angle=angle,
            pivot=pivot,
            is_degree=is_degree,
            is_clockwise=is_clockwise,
        )
        return Rectangle(points=rot_points)

    def copy(self) -> AxisAlignedRectangle:
        """Create a copy of the AxisAlignedRectangle object.

        Returns:
            AxisAlignedRectangle: copy of the object
        """
        return AxisAlignedRectangle(
            points=self.asarray.copy(), is_cast_int=self.is_cast_int
        )

area property

Get the area of the AxisAlignedRectangle

Returns:

Name Type Description
float float

area of the rectangle

as_pymupdf_rect property

Get the pymupdf representation of the given Rectangle. Beware a pymupdf can only be straight or axis-aligned.

See: https://pymupdf.readthedocs.io/en/latest/rect.html

Returns:

Type Description
Rect

pymupdf.Rect: pymupdf axis-aligned Rect object

height property

Get the height of the AxisAlignedRectangle

Returns:

Name Type Description
float float

height of the rectangle

perimeter property

Get the perimeter of the AxisAlignedRectangle

Returns:

Name Type Description
float float

perimeter of the rectangle

rotated90 property

Get the unique other related AxisAlignedRectangle that is the same one but rotated 90 degrees around its center.

Returns:

Name Type Description
AxisAlignedRectangle AxisAlignedRectangle

new AxisAlignedRectangle object

width property

Get the width of the AxisAlignedRectangle

Returns:

Name Type Description
float float

width of the rectangle

copy()

Create a copy of the AxisAlignedRectangle object.

Returns:

Name Type Description
AxisAlignedRectangle AxisAlignedRectangle

copy of the object

Source code in otary/geometry/discrete/shape/axis_aligned_rectangle.py
def copy(self) -> AxisAlignedRectangle:
    """Create a copy of the AxisAlignedRectangle object.

    Returns:
        AxisAlignedRectangle: copy of the object
    """
    return AxisAlignedRectangle(
        points=self.asarray.copy(), is_cast_int=self.is_cast_int
    )

from_center(center, width, height, is_cast_int=False) classmethod

Create an AxisAlignedRectangle from a center point

Parameters:

Name Type Description Default
center NDArray

center point of the rectangle

required
width float

width of the rectangle

required
height float

height of the rectangle

required
is_cast_int bool

cast the points coordinates to int

False

Returns:

Name Type Description
AxisAlignedRectangle AxisAlignedRectangle

new AxisAlignedRectangle object

Source code in otary/geometry/discrete/shape/axis_aligned_rectangle.py
@classmethod
def from_center(
    cls,
    center: NDArray,
    width: float,
    height: float,
    is_cast_int: bool = False,
) -> AxisAlignedRectangle:
    """Create an AxisAlignedRectangle from a center point

    Args:
        center (NDArray): center point of the rectangle
        width (float): width of the rectangle
        height (float): height of the rectangle
        is_cast_int (bool, optional): cast the points coordinates to int

    Returns:
        AxisAlignedRectangle: new AxisAlignedRectangle object
    """
    return cls.from_rectangle(
        super().from_center(center, width, height, is_cast_int=is_cast_int)
    )

from_polygon(polygon) classmethod

Create an AxisAlignedRectangle from any Polygon by computing its Axis Aligned Bounding Box (AABB). The resulting AxisAlignedRectangle will be the smallest axis-aligned rectangle that fully contains the input polygon.

Parameters:

Name Type Description Default
polygon Polygon | NDArray

Polygon object or array of points

required

Returns:

Name Type Description
AxisAlignedRectangle AxisAlignedRectangle

AxisAlignedRectangle object

Source code in otary/geometry/discrete/shape/axis_aligned_rectangle.py
@classmethod
def from_polygon(cls, polygon: Polygon | NDArray) -> AxisAlignedRectangle:
    """Create an AxisAlignedRectangle from any Polygon by computing its
    Axis Aligned Bounding Box (AABB).
    The resulting AxisAlignedRectangle will be the smallest axis-aligned rectangle
    that fully contains the input polygon.

    Args:
        polygon (Polygon | NDArray): Polygon object or array of points

    Returns:
        AxisAlignedRectangle: AxisAlignedRectangle object
    """
    if isinstance(polygon, Polygon):
        polygon_arr = polygon.asarray
    elif isinstance(polygon, np.ndarray):
        polygon_arr = polygon
    else:
        raise TypeError(
            "Input must be a Polygon object or a numpy ndarray of points."
        )

    if len(polygon_arr) <= 2:
        raise ValueError(
            "Cannot create an AxisAlignedRectangle from a Polygon with less than "
            "3 points."
        )

    if len(polygon_arr.shape) != 2 or polygon_arr.shape[1] != 2:
        raise ValueError(
            "Cannot create an AxisAlignedRectangle from a Polygon with more than "
            "2 dimensions."
        )

    polygon_arr = polygon_arr.astype(np.float32)

    xmin = np.min(polygon_arr[:, 0])
    xmax = np.max(polygon_arr[:, 0])
    ymin = np.min(polygon_arr[:, 1])
    ymax = np.max(polygon_arr[:, 1])

    return cls(points=[[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax]])

from_rectangle(rectangle) classmethod

Create an AxisAlignedRectangle from an ordinary Rectangle. Only works if the input Rectangle forms an AxisAlignedRectangle with its points.

If you want to create an AxisAlignedRectangle from any Rectangle, use the from_polygon method instead.

Parameters:

Name Type Description Default
rectangle Rectangle

Rectangle object

required

Returns:

Name Type Description
AxisAlignedRectangle AxisAlignedRectangle

AxisAlignedRectangle object

Source code in otary/geometry/discrete/shape/axis_aligned_rectangle.py
@classmethod
def from_rectangle(cls, rectangle: Rectangle) -> AxisAlignedRectangle:
    """Create an AxisAlignedRectangle from an ordinary Rectangle.
    Only works if the input Rectangle forms an AxisAlignedRectangle with its points.

    If you want to create an AxisAlignedRectangle from any Rectangle, use the
    from_polygon method instead.

    Args:
        rectangle (Rectangle): Rectangle object

    Returns:
        AxisAlignedRectangle: AxisAlignedRectangle object
    """
    return cls(points=rectangle.points)

rotate(angle, is_degree=False, is_clockwise=True, pivot=None)

The in-place rotate method is not supported for AxisAlignedRectangle. Use the rotate_transform method instead.

Source code in otary/geometry/discrete/shape/axis_aligned_rectangle.py
def rotate(
    self,
    angle: float,
    is_degree: bool = False,
    is_clockwise: bool = True,
    pivot: Optional[NDArray] = None,
):
    """The in-place rotate method is not supported for AxisAlignedRectangle.
    Use the rotate_transform method instead.
    """
    raise TypeError("AxisAlignedRectangle does not support arbitrary rotation.")

rotate_transform(angle, is_degree=False, is_clockwise=True, pivot=None)

Rotate the AxisAlignedRectangle and transform it into a Rectangle. The result is a Rectangle object since the result is not guaranteed to be axis-aligned anymore.

Parameters:

Name Type Description Default
angle float

rotation angle

required
is_degree bool

whether the angle is in degree or radian. Defaults to False which means radians.

False
is_clockwise bool

whether the rotation is clockwise or counter-clockwise. Defaults to True.

True
pivot NDArray

pivot point. Defaults to None which means that by default the centroid point of the shape is taken as the pivot point.

None

Returns:

Name Type Description
Rectangle Rectangle

object resulting from the rotation

Example

The rotate method does not modify the original AxisAlignedRectangle object:

>>> rect = ot.AxisAlignedRectangle(points=[[0, 0], [4, 0], [4, 3], [0, 3]])
>>> rot_rect = rect.rotate(angle=45, is_degree=True)
>>> rot_rect.asarray
array([[ 1.6464466 , -0.9748737 ],
       [ 4.4748735 ,  1.8535534 ],
       [ 2.3535533 ,  3.9748738 ],
       [-0.47487372,  1.1464466 ]], dtype=float32)
>>> rect
array([[0, 0],
       [4, 0],
       [4, 3],
       [0, 3]], dtype=int32)

Source code in otary/geometry/discrete/shape/axis_aligned_rectangle.py
def rotate_transform(
    self,
    angle: float,
    is_degree: bool = False,
    is_clockwise: bool = True,
    pivot: Optional[NDArray] = None,
) -> Rectangle:
    """Rotate the AxisAlignedRectangle and transform it into a Rectangle.
    The result is a Rectangle object since the result is not
    guaranteed to be axis-aligned anymore.

    Args:
        angle (float): rotation angle
        is_degree (bool, optional): whether the angle is in degree or radian.
            Defaults to False which means radians.
        is_clockwise (bool, optional): whether the rotation is clockwise or
            counter-clockwise. Defaults to True.
        pivot (NDArray, optional): pivot point.
            Defaults to None which means that by default the centroid point of
            the shape is taken as the pivot point.

    Returns:
        Rectangle: object resulting from the rotation

    Example:
        The rotate method does not modify the original AxisAlignedRectangle object:
        ```python
        >>> rect = ot.AxisAlignedRectangle(points=[[0, 0], [4, 0], [4, 3], [0, 3]])
        >>> rot_rect = rect.rotate(angle=45, is_degree=True)
        >>> rot_rect.asarray
        array([[ 1.6464466 , -0.9748737 ],
               [ 4.4748735 ,  1.8535534 ],
               [ 2.3535533 ,  3.9748738 ],
               [-0.47487372,  1.1464466 ]], dtype=float32)
        >>> rect
        array([[0, 0],
               [4, 0],
               [4, 3],
               [0, 3]], dtype=int32)
        ```
    """
    # pylint: disable=R0801
    if pivot is None:
        pivot = self.centroid

    rot_points = rotate_2d_points(
        points=self.points,
        angle=angle,
        pivot=pivot,
        is_degree=is_degree,
        is_clockwise=is_clockwise,
    )
    return Rectangle(points=rot_points)