-
-
Notifications
You must be signed in to change notification settings - Fork 22
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 infinity and figure eight target shapes #190
base: main
Are you sure you want to change the base?
Changes from all commits
98ebfac
92683cc
9d51b10
82f870e
dbb735c
0853d0a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,6 +2,7 @@ | |||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import itertools | ||||||||||||||||||||||||||
from numbers import Number | ||||||||||||||||||||||||||
from typing import Tuple | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import numpy as np | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -131,6 +132,150 @@ def __init__(self, dataset: Dataset) -> None: | |||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class _LemniscateBernoulli(PointCollection): | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
Implements the Lemniscate of Bernoulli Equation. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Parameters | ||||||||||||||||||||||||||
---------- | ||||||||||||||||||||||||||
dataset : Dataset | ||||||||||||||||||||||||||
The starting dataset to morph into other shapes. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Notes | ||||||||||||||||||||||||||
----- | ||||||||||||||||||||||||||
The formula for the infinity shape is directly taken from Lemniscate of | ||||||||||||||||||||||||||
Bernoulli equation. | ||||||||||||||||||||||||||
`Infinity Curve <https://mathworld.wolfram.com/Lemniscate.html>`_: | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Weisstein, Eric W. "Lemniscate." From MathWorld-- | ||||||||||||||||||||||||||
A Wolfram Web Resource. https://mathworld.wolfram.com/Lemniscate.html | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
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 | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
t = np.linspace(-3, 3, num=2000) | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using 2,000 points is a lot, the algorithm needs to calculate the distance to each point. Can we reduce this? For example, the heart only uses 80. |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
x = (np.sqrt(2) * np.cos(t)) / (1 + np.square(np.sin(t))) | ||||||||||||||||||||||||||
y = (np.sqrt(2) * np.cos(t) * np.sin(t)) / (1 + np.square(np.sin(t))) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
scale_factor = (x_bounds[1] - x_shift) * 0.75 | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use the y information here? Maybe pick one using the data? With the sheep for example, it seems like the figure eight is being drawn too tall, so we should probably take the minimum of the x and y scale factors and use that. Try plotting a dataset and the shape on top of each other and see how that looks. |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Apply transforms | ||||||||||||||||||||||||||
transformed_x, transformed_y = self._shift_bernoulli(x, y) | ||||||||||||||||||||||||||
super().__init__( | ||||||||||||||||||||||||||
*np.stack( | ||||||||||||||||||||||||||
[ | ||||||||||||||||||||||||||
transformed_x * scale_factor + x_shift, | ||||||||||||||||||||||||||
transformed_y * scale_factor + y_shift, | ||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||
axis=1, | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
Comment on lines
+170
to
+178
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def _shift_bernoulli( | ||||||||||||||||||||||||||
self, x: np.ndarray, y: np.ndarray | ||||||||||||||||||||||||||
) -> Tuple[np.ndarray, np.ndarray]: | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
A method which allows for manipulation of the Lemniscate Points after | ||||||||||||||||||||||||||
they have been generated. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Parameters | ||||||||||||||||||||||||||
---------- | ||||||||||||||||||||||||||
x : np.ndarray | ||||||||||||||||||||||||||
The Lemniscate X points. | ||||||||||||||||||||||||||
y : np.ndarray | ||||||||||||||||||||||||||
The Lemniscate Y points. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Returns | ||||||||||||||||||||||||||
------- | ||||||||||||||||||||||||||
np.ndarray | ||||||||||||||||||||||||||
The manipulated Lemniscate X points. | ||||||||||||||||||||||||||
np.ndarray | ||||||||||||||||||||||||||
The manipulated Lemniscate Y points. | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
return x, y | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class Infinity(_LemniscateBernoulli): | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
Implements the Lemniscate of Bernoulli Equation, | ||||||||||||||||||||||||||
which by default creates an infinity 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 Infinity | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
_ = Infinity(DataLoader.load_dataset('panda')).plot() | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Notes | ||||||||||||||||||||||||||
----- | ||||||||||||||||||||||||||
The formula for the infinity shape is directly taken from Lemniscate of | ||||||||||||||||||||||||||
Bernoulli equation. | ||||||||||||||||||||||||||
Comment on lines
+219
to
+222
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels more like a see also section. Something like this, but figure out a good description:
Suggested change
|
||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def __str__(self) -> str: | ||||||||||||||||||||||||||
return 'infinity' | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Comment on lines
+225
to
+227
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We only need this if the name is different than the lowercased class name.
Suggested change
data-morph/src/data_morph/shapes/bases/shape.py Lines 25 to 34 in b6bebc9
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class FigureEight(_LemniscateBernoulli): | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
Class for the Figure Eight shape using a | ||||||||||||||||||||||||||
rotated Lemniscate of Bernoulli Equation. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
.. 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 FigureEight | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
_ = FigureEight(DataLoader.load_dataset('panda')).plot() | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Notes | ||||||||||||||||||||||||||
----- | ||||||||||||||||||||||||||
Implements the Lemniscate of Bernoulli Equation with a transform | ||||||||||||||||||||||||||
to draw a figure eight shape. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
See Base class for implementation specifice details. | ||||||||||||||||||||||||||
Comment on lines
+244
to
+249
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do a see also section here as well, but also link to the |
||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def _shift_bernoulli( | ||||||||||||||||||||||||||
self, x: np.ndarray, y: np.ndarray | ||||||||||||||||||||||||||
) -> Tuple[np.ndarray, np.ndarray]: | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
A method which allows for manipulation of the Lemniscate Points after | ||||||||||||||||||||||||||
they have been generated. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Parameters | ||||||||||||||||||||||||||
---------- | ||||||||||||||||||||||||||
x : np.ndarray | ||||||||||||||||||||||||||
The Lemniscate X points. | ||||||||||||||||||||||||||
y : np.ndarray | ||||||||||||||||||||||||||
The Lemniscate Y points. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Returns | ||||||||||||||||||||||||||
------- | ||||||||||||||||||||||||||
np.ndarray | ||||||||||||||||||||||||||
The manipulated Lemniscate X points. | ||||||||||||||||||||||||||
np.ndarray | ||||||||||||||||||||||||||
The manipulated Lemniscate Y points. | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
return y, x | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def __str__(self) -> str: | ||||||||||||||||||||||||||
return 'figure_eight' | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class LeftParabola(PointCollection): | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
Class for the left parabola shape. | ||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -25,6 +25,8 @@ def test_distance(self, shape, test_point, expected_distance): | |||||
Test the distance() method parametrized by distance_test_cases | ||||||
(see conftest.py). | ||||||
""" | ||||||
# print(shape.points[67]) | ||||||
print(shape.distance(*test_point)) | ||||||
Comment on lines
+28
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
assert pytest.approx(shape.distance(*test_point), abs=1e-5) == expected_distance | ||||||
|
||||||
|
||||||
|
@@ -81,6 +83,26 @@ class TestHeart(PointsModuleTestBase): | |||||
] | ||||||
|
||||||
|
||||||
class TestInfinity(PointsModuleTestBase): | ||||||
"""Test the Infinity class.""" | ||||||
|
||||||
shape_name = 'infinity' | ||||||
distance_test_cases = [ | ||||||
[(20, 60), 3.694037796061944], | ||||||
[(11.02368017, 68.01626706), 0.0], | ||||||
] | ||||||
|
||||||
|
||||||
class TestFigureEight(PointsModuleTestBase): | ||||||
"""Test the FigureEight class.""" | ||||||
|
||||||
shape_name = 'figure_eight' | ||||||
distance_test_cases = [ | ||||||
[(20, 60), 3.2674569898782337], | ||||||
[(23.01626706, 56.02368017), 0.0], | ||||||
] | ||||||
|
||||||
|
||||||
class TestScatter(PointsModuleTestBase): | ||||||
"""Test the Scatter class.""" | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's merge this with the
Infinity
class (calling itInfinity
). The docs have a broken link with this because they ignore anything prefixed with_
, and this class has all the functionality of theInfinity
class already.