#!/usr/bin/env python3
#
# Copyright 2021 Graviti. Licensed under MIT License.
#
"""The implementation of the TensorBay polygon."""
from typing import Dict, Iterable, List, Mapping, Optional, Sequence, Type, TypeVar
from tensorbay.geometry.point_list import MultiPointList2D, PointList2D
from tensorbay.geometry.vector import Vector2D
from tensorbay.utility import UserMutableSequence, common_loads
[docs]class Polygon(PointList2D[Vector2D]):
"""This class defines the concept of Polygon.
:class:`Polygon` contains the coordinates of the vertexes of the polygon and provides
:meth:`Polygon.area` to calculate the area of the polygon.
Examples:
>>> Polygon([[1, 2], [2, 3], [2, 2]])
Polygon [
Vector2D(1, 2),
Vector2D(2, 3),
Vector2D(2, 2)
]
"""
_P = TypeVar("_P", bound="Polygon")
_ElementType = Vector2D
[docs] @classmethod
def loads(cls: Type[_P], contents: Sequence[Mapping[str, float]]) -> _P:
"""Loads the information of :class:`Polygon`.
Arguments:
contents: A list of dictionary lists containing the coordinates
of the vertexes of the polygon.
Returns:
The loaded :class:`Polygon` object.
Examples:
>>> contents = [{"x": 1.0, "y": 1.0}, {"x": 2.0, "y": 2.0}, {"x": 2.0, "y": 3.0}]
>>> Polygon.loads(contents)
Polygon [
Vector2D(1.0, 1.0),
Vector2D(2.0, 2.0),
Vector2D(2.0, 3.0)
]
"""
return common_loads(cls, contents)
[docs] def area(self) -> float:
"""Return the area of the polygon.
The area is positive if the rotating direction of the points is counterclockwise,
and negative if clockwise.
Returns:
The area of the polygon.
Examples:
>>> polygon = Polygon([[1, 2], [2, 2], [2, 3]])
>>> polygon.area()
0.5
"""
area = 0.0
for i in range(len(self._data)): # pylint: disable=consider-using-enumerate
# pylint: disable=invalid-name
x1, y1 = self._data[i - 1]
x2, y2 = self._data[i]
area += x1 * y2 - x2 * y1
return area / 2
[docs]class MultiPolygon(MultiPointList2D[Polygon]):
"""This class defines the concept of MultiPolygon.
:class:`MultiPolygon` contains a list of polygons.
Arguments:
polygons: A list of polygons.
Examples:
>>> MultiPolygon([[[1.0, 4.0], [2.0, 3.7], [7.0, 4.0]],
... [[5.0, 7.0], [6.0, 7.0], [9.0, 8.0]]])
MultiPolygon [
Polygon [...]
Polygon [...]
...
]
"""
_P = TypeVar("_P", bound="MultiPolygon")
_ElementType = Polygon
def __init__(self, polygons: Optional[Iterable[Iterable[Iterable[float]]]]):
super().__init__(polygons)
[docs] @classmethod
def loads(cls: Type[_P], contents: Sequence[Sequence[Mapping[str, float]]]) -> _P:
"""Loads a :class:`MultiPolygon` from the given contents.
Arguments:
contents: A list of dict lists containing
the coordinates of the vertices of the polygon list.
Returns:
The loaded :class:`MultiPolyline2D` object.
Examples:
>>> contents = [[{'x': 1.0, 'y': 4.0}, {'x': 2.0, 'y': 3.7}, {'x': 7.0, 'y': 4.0}],
... [{'x': 5.0, 'y': 7.0}, {'x': 6.0, 'y': 7.0}, {'x': 9.0, 'y': 8.0}]]
>>> multipolygon = MultiPolygon.loads(contents)
>>> multipolygon
MultiPolygon [
Polygon [...]
Polygon [...]
...
]
"""
return common_loads(cls, contents)
[docs] def dumps(self) -> List[List[Dict[str, float]]]:
"""Dumps a :class:`MultiPolygon` into a polygon list.
Returns:
All the information of the :class:`MultiPolygon`.
Examples:
>>> multipolygon = MultiPolygon([[[1.0, 4.0], [2.0, 3.7], [7.0, 4.0]],
... [[5.0, 7.0], [6.0, 7.0], [9.0, 8.0]]])
>>> multipolygon.dumps()
[
[{'x': 1.0, 'y': 4.0}, {'x': 2.0, 'y': 3.7}, {'x': 7.0, 'y': 4.0}],
[{'x': 5,0, 'y': 7.0}, {'x': 6.0, 'y': 7.0}, {'x': 9.0, 'y': 8.0}]
]
"""
return self._dumps()
[docs]class RLE(UserMutableSequence[int]):
"""This class defines the concept of RLE.
:class:`RLE` contains an rle format mask.
Arguments:
rle: A rle format mask.
Examples:
>>> RLE([272, 2, 4, 4, 2, 9])
RLE [
272,
2,
...
]
"""
_data: List[int]
def __init__(self, rle: Optional[Iterable[int]] = None):
self._data = list(rle) if rle is not None else []
def _dumps(self) -> List[int]:
return self._data
def _loads(self, contents: List[int]) -> None:
self._data = contents
[docs] @classmethod
def loads(cls: Type["RLE"], contents: List[int]) -> "RLE":
"""Loads a :class:RLE` from the given contents.
Arguments:
contents: One rle mask.
Returns:
The loaded :class:`RLE` object.
Examples:
>>> contents = [272, 2, 4, 4, 2, 9]
>>> rle = RLE.loads(contents)
>>> rle
RLE [
272,
2,
...
]
"""
return common_loads(cls, contents)
[docs] def dumps(self) -> List[int]:
"""Dumps a :class:`RLE` into one rle mask.
Returns:
All the information of the :class:`RLE`.
Examples:
>>> rle = RLE([272, 2, 4, 4, 2, 9])
>>> rle.dumps()
[272, 2, 4, 4, 2, 9]
"""
return self._dumps()