-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
shape.py
123 lines (100 loc) · 3.2 KB
/
shape.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""Abstract base class for shapes."""
from abc import ABC, abstractmethod
from numbers import Number
from typing import Iterable, Optional
from matplotlib.axes import Axes
from scipy.spatial import distance
class Shape(ABC):
"""Abstract base class for a shape."""
def __repr__(self) -> str:
"""
Return string representation of the shape.
Returns
-------
str
The unambiguous string representation of the shape.
"""
return self._recursive_repr()
def __str__(self) -> str:
"""
Return string representation of the shape.
Returns
-------
str
The human-readable string representation of the shape.
"""
return self.__class__.__name__.lower()
@abstractmethod
def distance(self, x: Number, y: Number) -> float:
"""
Calculate the distance between this shape and a point (x, y).
Parameters
----------
x, y : numbers.Number
Coordinates of a point in 2D space.
Returns
-------
float
The distance between this shape and the point (x, y).
"""
raise NotImplementedError
@staticmethod
def _euclidean_distance(a: Iterable[Number], b: Iterable[Number]) -> float:
"""
Calculate the Euclidean distance between points a and b.
Parameters
----------
a, b : Iterable[numbers.Number]
Coordinates of points in two-dimensional space.
Returns
-------
float
The Euclidean distance between a and b.
See Also
--------
scipy.spatial.distance.euclidean : Euclidean distance calculation.
"""
return distance.euclidean(a, b)
def _recursive_repr(self, attr: Optional[str] = None) -> str:
"""
Return string representation of the shape incorporating
any items inside a specific attribute.
Parameters
----------
attr : str, optional
The attribute to incorporate into the result; must
be iterable.
Returns
-------
str
The unambiguous string representation of the shape.
"""
value = f'<{self.__class__.__name__}>'
if not attr:
return value
indented_line = '\n '
offset = len(attr) + 4
hanging_indent = f'{indented_line:<{offset}}'
return (
value
+ f'{indented_line}{attr}={hanging_indent}'
+ f'{hanging_indent}'.join(repr(item) for item in getattr(self, attr))
)
@abstractmethod
def plot(self, ax: Axes = None) -> Axes:
"""
Plot the shape.
Parameters
----------
ax : matplotlib.axes.Axes, optional
An optional :class:`~matplotlib.axes.Axes` object to plot on.
Returns
-------
matplotlib.axes.Axes
The :class:`~matplotlib.axes.Axes` object containing the plot.
Notes
-----
When implementing this method for subclasses, make sure to apply the
:func:`.plotting.style.plot_with_custom_style` decorator.
"""
raise NotImplementedError