From 5726e351cb8640d452ea21de981649cd021ecfc1 Mon Sep 17 00:00:00 2001 From: kunxianhuang Date: Sun, 6 Oct 2024 04:26:52 +0800 Subject: [PATCH] Add spade shape (#225) Co-authored-by: Kevin Romero Co-authored-by: Stefanie Molin <24376333+stefmolin@users.noreply.github.com> --- src/data_morph/shapes/factory.py | 1 + src/data_morph/shapes/points.py | 59 ++++++++++++++++++++++++++++++++ tests/shapes/test_points.py | 14 ++++++++ 3 files changed, 74 insertions(+) diff --git a/src/data_morph/shapes/factory.py b/src/data_morph/shapes/factory.py index 75623e2a..94c15b2c 100644 --- a/src/data_morph/shapes/factory.py +++ b/src/data_morph/shapes/factory.py @@ -54,6 +54,7 @@ class ShapeFactory: 'rings': circles.Rings, 'star': polygons.Star, 'club': points.Club, + 'spade': points.Spade, } AVAILABLE_SHAPES: list[str] = sorted(_SHAPE_MAPPING.keys()) diff --git a/src/data_morph/shapes/points.py b/src/data_morph/shapes/points.py index aaeaea25..d4013d9d 100644 --- a/src/data_morph/shapes/points.py +++ b/src/data_morph/shapes/points.py @@ -385,3 +385,62 @@ def __init__(self, dataset: Dataset) -> None: ys = y_shift + np.concatenate(y_lobes + y_stem) super().__init__(*np.stack([xs, ys], axis=1)) + + +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 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 + + x_shift = sum(x_bounds) / 2 + y_shift = sum(y_bounds) / 2 + + # graph upside-down heart + heart_points = Heart(dataset).points + heart_points[:, 1] = -heart_points[:, 1] + 2 * y_shift + + # line base + line_x = np.linspace(-6, 6, num=12) + line_y = np.repeat(-16, 12) + + # left wing + left_x = np.linspace(-6, 0, num=12) + left_y = 0.278 * np.power(left_x + 6, 2) - 16 + + # right wing + right_x = np.linspace(0, 6, num=12) + right_y = 0.278 * np.power(right_x - 6, 2) - 16 + + # shift and scale the base and wing + base_x = np.concatenate((line_x, left_x, right_x), axis=0) + base_y = np.concatenate((line_y, left_y, right_y), axis=0) + + # scale by the half the widest width of the spade + scale_factor = (x_bounds[1] - x_shift) / 16 + base_x = base_x * scale_factor + x_shift + base_y = base_y * scale_factor + y_shift + + # combine the base and the upside-down heart + x = np.concatenate((heart_points[:, 0], base_x), axis=0) + y = np.concatenate((heart_points[:, 1], base_y), axis=0) + + super().__init__(*np.stack([x, y], axis=1)) diff --git a/tests/shapes/test_points.py b/tests/shapes/test_points.py index f156f836..b5275314 100644 --- a/tests/shapes/test_points.py +++ b/tests/shapes/test_points.py @@ -159,3 +159,17 @@ class TestClub(PointsModuleTestBase): [(20, 50), 5.941155], [(10, 80), 10.288055], ] + + +class TestSpade(PointsModuleTestBase): + """Test the Spade class.""" + + shape_name = 'spade' + distance_test_cases = [ + [(19.97189615, 75.43271708), 0], + [(23.75, 55), 0], + [(11.42685318, 59.11304904), 0], + [(20, 75), 0.2037185], + [(0, 0), 57.350348], + [(10, 80), 10.968080], + ]