Skip to content

Commit

Permalink
Use better variable names for circle attributes (#205)
Browse files Browse the repository at this point in the history
- `cx`, `cy` -> `center`
- `r` -> `radius`
  • Loading branch information
stefmolin authored Jul 22, 2024
1 parent 4f0d702 commit 4d5319d
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 28 deletions.
28 changes: 13 additions & 15 deletions src/data_morph/shapes/circles.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,20 @@ class Circle(Shape):
----------
dataset : Dataset
The starting dataset to morph into other shapes.
r : numbers.Number, optional
radius : numbers.Number, optional
The radius of the circle.
"""

def __init__(self, dataset: Dataset, r: Number = None) -> None:
self.cx: Number = dataset.df.x.mean()
"""numbers.Number: The x coordinate of the circle's center."""
def __init__(self, dataset: Dataset, radius: Number = None) -> None:
self.center: np.ndarray = dataset.df[['x', 'y']].mean().to_numpy()
"""numpy.ndarray: The (x, y) coordinates of the circle's center."""

self.cy: Number = dataset.df.y.mean()
"""numbers.Number: The y coordinate of the circle's center."""

self.r: Number = r or dataset.df.std().mean() * 1.5
self.radius: Number = radius or dataset.df[['x', 'y']].std().mean() * 1.5
"""numbers.Number: The radius of the circle."""

self._center = np.array([self.cx, self.cy])

def __repr__(self) -> str:
return f'<{self.__class__.__name__} cx={self.cx} cy={self.cy} r={self.r}>'
x, y = self.center
return f'<{self.__class__.__name__} center={(float(x), float(y))} radius={self.radius}>'

def distance(self, x: Number, y: Number) -> float:
"""
Expand All @@ -62,7 +58,9 @@ def distance(self, x: Number, y: Number) -> float:
float
The absolute distance between this circle's edge and the point (x, y).
"""
return abs(self._euclidean_distance(self._center, np.array([x, y])) - self.r)
return abs(
self._euclidean_distance(self.center, np.array([x, y])) - self.radius
)

@plot_with_custom_style
def plot(self, ax: Axes = None) -> Axes:
Expand All @@ -83,7 +81,7 @@ def plot(self, ax: Axes = None) -> Axes:
fig, ax = plt.subplots(layout='constrained')
fig.get_layout_engine().set(w_pad=0.2, h_pad=0.2)
_ = ax.axis('equal')
_ = ax.add_patch(plt.Circle((self.cx, self.cy), self.r, ec='k', fill=False))
_ = ax.add_patch(plt.Circle(self.center, self.radius, ec='k', fill=False))
_ = ax.autoscale()
return ax

Expand Down Expand Up @@ -127,8 +125,8 @@ def __init__(self, dataset: Dataset, num_rings: int = 4) -> None:
]
"""list[Circle]: The individual rings represented by :class:`Circle` objects."""

self._centers = np.array([circle._center for circle in self.circles])
self._radii = np.array([circle.r for circle in self.circles])
self._centers = np.array([circle.center for circle in self.circles])
self._radii = np.array([circle.radius for circle in self.circles])

def __repr__(self) -> str:
return self._recursive_repr('circles')
Expand Down
29 changes: 16 additions & 13 deletions tests/shapes/test_circles.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

pytestmark = [pytest.mark.shapes, pytest.mark.circles]

CIRCLE_REPR = r'<Circle center=\((\d+\.*\d*), (\d+\.*\d*)\) radius=(\d+\.*\d*)>'


class CirclesModuleTestBase:
"""Base for testing circle shapes."""
Expand Down Expand Up @@ -42,33 +44,33 @@ class TestBullseye(CirclesModuleTestBase):
repr_regex = (
r'^<Bullseye>\n'
r' circles=\n'
r' <Circle cx=(\d+\.*\d*) cy=(\d+\.*\d*) r=(\d+\.*\d*)>\n'
r' <Circle cx=(\d+\.*\d*) cy=(\d+\.*\d*) r=(\d+\.*\d*)>$'
r' ' + CIRCLE_REPR + '\n'
r' ' + CIRCLE_REPR + '$'
)

def test_init(self, shape):
"""Test that the Bullseye contains two concentric circles."""
assert len(shape.circles) == 2

a, b = shape.circles
assert a.cx == b.cx
assert a.cy == b.cy
assert a.r != b.r
assert np.array_equal(a.center, b.center)
assert a.radius != b.radius


class TestCircle(CirclesModuleTestBase):
"""Test the Circle class."""

shape_name = 'circle'
distance_test_cases = [[(20, 50), 10.490381], [(10, 25), 15.910168]]
repr_regex = r'^<Circle cx=(\d+\.*\d*) cy=(\d+\.*\d*) r=(\d+\.*\d*)>$'
repr_regex = '^' + CIRCLE_REPR + '$'

def test_is_circle(self, shape):
"""Test that the Circle is a valid circle (mathematically)."""
angles = np.arange(0, 361, 45)
cx, cy = shape.center
for x, y in zip(
shape.cx + shape.r * np.cos(angles),
shape.cy + shape.r * np.sin(angles),
cx + shape.radius * np.cos(angles),
cy + shape.radius * np.sin(angles),
):
assert pytest.approx(shape.distance(x, y)) == 0

Expand All @@ -81,8 +83,10 @@ class TestRings(CirclesModuleTestBase):
repr_regex = (
r'^<Rings>\n'
r' circles=\n'
r' <Circle cx=(\d+\.*\d*) cy=(\d+\.*\d*) r=(\d+\.*\d*)>\n'
r' <Circle cx=(\d+\.*\d*) cy=(\d+\.*\d*) r=(\d+\.*\d*)>'
r' ' + CIRCLE_REPR + '\n'
r' ' + CIRCLE_REPR + '\n'
r' ' + CIRCLE_REPR + '\n'
r' ' + CIRCLE_REPR + '$'
)

@pytest.mark.parametrize('num_rings', [3, 5])
Expand All @@ -92,11 +96,10 @@ def test_init(self, shape_factory, num_rings):

assert len(shape.circles) == num_rings
assert all(
getattr(circle, center_coord) == getattr(shape.circles[0], center_coord)
np.array_equal(circle.center, shape.circles[0].center)
for circle in shape.circles[1:]
for center_coord in ['cx', 'cy']
)
assert len({circle.r for circle in shape.circles}) == num_rings
assert len({circle.radius for circle in shape.circles}) == num_rings

@pytest.mark.parametrize('num_rings', ['3', -5, 1, True])
def test_num_rings_is_valid(self, shape_factory, num_rings):
Expand Down

0 comments on commit 4d5319d

Please sign in to comment.