Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spade shape #195

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/data_morph/shapes/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class ShapeFactory:
'rectangle': polygons.Rectangle,
'rings': circles.Rings,
'star': polygons.Star,
'spade': points.Spade,
}

AVAILABLE_SHAPES: list[str] = sorted(_SHAPE_MAPPING.keys())
Expand Down
65 changes: 65 additions & 0 deletions src/data_morph/shapes/points.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Shapes that are composed of points."""

import itertools
import math
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to just use numpy here.

Suggested change
import math

from numbers import Number

import numpy as np
Expand Down Expand Up @@ -304,3 +305,67 @@ def distance(self, x: Number, y: Number) -> int:
Always returns 0 to allow for scattering of the points.
"""
return 0


class Spade(PointCollection):
"""
Class for the spade shape.

.. plot::
:scale: 75
:caption:
This shape is generated using the panda dataset.

from data_morph.data.loader import DataLoader
from data_morph.shapes.points import Heart

_ = Spade(DataLoader.load_dataset('panda')).plot()
Comment on lines +319 to +322
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code gets executed while building the docs, so this won't work as is.

Suggested change
from data_morph.data.loader import DataLoader
from data_morph.shapes.points import Heart
_ = Spade(DataLoader.load_dataset('panda')).plot()
from data_morph.data.loader import DataLoader
from data_morph.shapes.points import Spade
_ = Spade(DataLoader.load_dataset('panda')).plot()


Parameters
----------
dataset : Dataset
The starting dataset to morph into other shapes.
"""

def __init__(self, dataset: Dataset) -> None:
x_bounds = dataset.data_bounds.x_bounds
y_bounds = dataset.data_bounds.y_bounds

# Graph heart curve
x_shift = sum(x_bounds) / 2
y_shift = sum(y_bounds) / 2

t = np.linspace(-3, 3, num=90)

heart_x = -(16 * np.sin(t) ** 3)
heart_y = -(
13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
)
Comment on lines +334 to +343
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are using the heart equations, we should share the logic rather than duplicate it.


# Graph line base
line_t = np.linspace(-6, 6, num=12)
line_x = line_t
line_y = [-16 for _ in line_t]
Comment on lines +346 to +348
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this?

Suggested change
line_t = np.linspace(-6, 6, num=12)
line_x = line_t
line_y = [-16 for _ in line_t]
line_x = np.linspace(-6, 6, num=12)
line_y = np.repeat(-16, 12)


# Graph left wing
left_t = np.linspace(-6, 0, num=12)
left_x = left_t
left_y = [0.278 * math.pow(point + 6.0, 2) - 16.0 for point in left_t]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use numpy instead of math and vectorize this. https://numpy.org/doc/stable/reference/generated/numpy.power.html


# Graph right wing
right_t = np.linspace(0, 6, num=12)
right_x = right_t
right_y = [0.278 * math.pow(point - 6.0, 2) - 16.0 for point in right_t]

x = np.concatenate((heart_x, line_x, left_x, right_x), axis=0)
y = np.concatenate((heart_y, line_y, left_y, right_y), axis=0)

# scale by the half the widest width of the heart
scale_factor = (x_bounds[1] - x_shift) / 16

super().__init__(
*np.stack(
[x * scale_factor + x_shift, y * scale_factor + y_shift],
axis=1,
)
)
12 changes: 12 additions & 0 deletions tests/shapes/test_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,15 @@ class TestUpParabola(ParabolaTestBase):
positive_quadratic_term = True
x_index = 0
y_index = 1


class TestSpade(PointsModuleTestBase):
"""Test the Spade class."""

shape_name = 'spade'
distance_test_cases = [
[(20.02810385, 75.43271708), 0],
[(29.90222827231392, 63.4652469587905), 0],
[(19.52424889753761, 60.33944515816489), 0],
[(20, 75), 0.2214756],
]
Loading