From 70811660613db9a8ef6d8a93b87791350c918768 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Mon, 10 Apr 2023 23:52:01 -0400 Subject: [PATCH 01/26] ADD: added rotate definition --- spyrograph/core/_trochoid.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 402d7bd..f329d2d 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -175,6 +175,9 @@ def scale(self, factor: Number) -> Union["_Trochoid", "_Cycloid"]: ) return scaled_shape + def rotate(self, theta: Number) -> Union["_Trochoid", "_Cycloid"]: + """Return shape with thetas rotated by a given input angle""" + def plot(self, **kwargs) -> Tuple["matplotlib.matplotlib.Figure", "matplotlib.axes._axes.Axes"]: """ Plot the shape and return the associated matplotlib Figure and Axes objects. From f5dc1163e16d16685e0526f393dc289e052b6e2c Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 00:02:46 -0400 Subject: [PATCH 02/26] ADD: added theta increment --- spyrograph/core/_trochoid.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index f329d2d..ec5e017 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -177,6 +177,21 @@ def scale(self, factor: Number) -> Union["_Trochoid", "_Cycloid"]: def rotate(self, theta: Number) -> Union["_Trochoid", "_Cycloid"]: """Return shape with thetas rotated by a given input angle""" + try: + scaled_shape = self.__class__( + R=self.R, + r=self.r, + d=self.d, + thetas=self.thetas+theta, + origin=self.origin + ) + except TypeError: + scaled_shape = self.__class__( + R=self.R, + r=self.r, + thetas=self.thetas+theta, + origin=self.origin + ) def plot(self, **kwargs) -> Tuple["matplotlib.matplotlib.Figure", "matplotlib.axes._axes.Axes"]: """ From 5f3a9ad87828ebbff2181a5f22ffb92519f10c0d Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 00:03:38 -0400 Subject: [PATCH 03/26] ADD: added rotated_shape return --- spyrograph/core/_trochoid.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index ec5e017..55a2bda 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -178,7 +178,7 @@ def scale(self, factor: Number) -> Union["_Trochoid", "_Cycloid"]: def rotate(self, theta: Number) -> Union["_Trochoid", "_Cycloid"]: """Return shape with thetas rotated by a given input angle""" try: - scaled_shape = self.__class__( + rotated_shape = self.__class__( R=self.R, r=self.r, d=self.d, @@ -186,12 +186,13 @@ def rotate(self, theta: Number) -> Union["_Trochoid", "_Cycloid"]: origin=self.origin ) except TypeError: - scaled_shape = self.__class__( + rotated_shape = self.__class__( R=self.R, r=self.r, thetas=self.thetas+theta, origin=self.origin ) + return rotated_shape def plot(self, **kwargs) -> Tuple["matplotlib.matplotlib.Figure", "matplotlib.axes._axes.Axes"]: """ From 63784f7eb3cbcab1722e72e7fd556e83e155d5d3 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 00:13:15 -0400 Subject: [PATCH 04/26] REMOVE: removed unnecessary definition --- spyrograph/core/_trochoid.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 55a2bda..f329d2d 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -177,22 +177,6 @@ def scale(self, factor: Number) -> Union["_Trochoid", "_Cycloid"]: def rotate(self, theta: Number) -> Union["_Trochoid", "_Cycloid"]: """Return shape with thetas rotated by a given input angle""" - try: - rotated_shape = self.__class__( - R=self.R, - r=self.r, - d=self.d, - thetas=self.thetas+theta, - origin=self.origin - ) - except TypeError: - rotated_shape = self.__class__( - R=self.R, - r=self.r, - thetas=self.thetas+theta, - origin=self.origin - ) - return rotated_shape def plot(self, **kwargs) -> Tuple["matplotlib.matplotlib.Figure", "matplotlib.axes._axes.Axes"]: """ From 406daa6f254ac5ee61234d2b00e516c1c0ba5596 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 20:59:19 -0400 Subject: [PATCH 05/26] ADD: added orientation argument to _Trochoid --- spyrograph/core/_trochoid.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index f329d2d..dbec4bc 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -33,7 +33,8 @@ class _Trochoid(ABC): def __init__( self, R: Number, r: Number, d: Number, thetas: List[Number] = None, theta_start: Number = None, theta_stop: Number = None, - theta_step: Number = None, origin: Tuple[Number, Number] = (0, 0) + theta_step: Number = None, origin: Tuple[Number, Number] = (0, 0), + orientation: Number = 0 ) -> None: """Model of a trochoid curve from given input parameters. A trochoid is a curve drawn by tracing a point from a circle as it rolls around the @@ -66,6 +67,8 @@ def __init__( cannot be set at the same time as thetas argument origin : Tuple[Number, Number] = (0, 0) Custom origin to center the shapes at. Default is (0,0) + orientation : Number = 0 + Angle of rotation for the shape """ self.R = R self.r = r From 98095b21d1b94fc54f83f2c10526cc2317dfc8e5 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:00:48 -0400 Subject: [PATCH 06/26] ADD: added orientation to Cycloid --- spyrograph/core/_cycloid.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spyrograph/core/_cycloid.py b/spyrograph/core/_cycloid.py index d214a59..14fea75 100644 --- a/spyrograph/core/_cycloid.py +++ b/spyrograph/core/_cycloid.py @@ -16,9 +16,10 @@ class _Cycloid(_Trochoid): def __init__( self, R: Number, r: Number, thetas: List[Number] = None, theta_start: Number = None, theta_stop: Number = None, - theta_step: Number = None, origin: Tuple[Number, Number] = (0, 0) + theta_step: Number = None, origin: Tuple[Number, Number] = (0, 0), + orientation: Number = None ) -> None: - super().__init__(R, r, r, thetas, theta_start, theta_stop, theta_step, origin) + super().__init__(R, r, r, thetas, theta_start, theta_stop, theta_step, origin, orientation) # pylint: disable=pointless-string-statement """Instantiate a cycloid curve from given input parameters. A hypocycloid is a curve drawn by tracing a point from a circle as it @@ -51,6 +52,8 @@ def __init__( cannot be set at the same time as thetas argument origin : Tuple[Number, Number] = (0, 0) Custom origin to center the shapes at. Default is (0,0) + orientation : Number = 0 + Angle of rotation for the shape """ @classmethod From d3f7e51e58a23ec352b98401fdd0b0fc51d0940b Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:04:15 -0400 Subject: [PATCH 07/26] ADD: added orientation save --- spyrograph/core/_trochoid.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index dbec4bc..ca94fe0 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -75,6 +75,7 @@ def __init__( self.d = d self.thetas = _validate_theta(thetas, theta_start, theta_stop, theta_step) self.origin = origin + self.orientation = orientation if self.R <= 0 or self.r <= 0 or self.d <= 0: raise ValueError(( From ea036ad2c57b8eeccb3e47f06bfae0e4c67a0cb7 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:06:40 -0400 Subject: [PATCH 08/26] REFACTOR: refactored path calculation into _calculate_path --- spyrograph/core/_trochoid.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index ca94fe0..42246f9 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -83,16 +83,7 @@ def __init__( "Please only pass positive values" )) - self.x = np.array([self._calculate_x(theta) for theta in self.thetas]) - self.y = np.array([self._calculate_y(theta) for theta in self.thetas]) - self.x += self.origin[0] - self.y += self.origin[1] - self.min_x = min(self.x) - self.max_x = max(self.x) - self.min_y = min(self.y) - self.max_y = max(self.y) - - self.coords = list(zip(self.x, self.y, self.thetas)) + def translate(self, x: Number = 0, y: Number = 0) -> "_Trochoid": """ @@ -580,6 +571,19 @@ def create_range( )) return shapes + def _calculate_path(self) -> None: + """Calculate the parametrized path""" + self.x = np.array([self._calculate_x(theta) for theta in self.thetas]) + self.y = np.array([self._calculate_y(theta) for theta in self.thetas]) + self.x += self.origin[0] + self.y += self.origin[1] + self.min_x = min(self.x) + self.max_x = max(self.x) + self.min_y = min(self.y) + self.max_y = max(self.y) + + self.coords = list(zip(self.x, self.y, self.thetas)) + def _show_full_path(self, pre_draw_turtle: "turtle.Turtle") -> turtle.Turtle: """Draw the full path prior to tracing""" # pylint: disable=no-member, unused-variable From 5dfed08d205c136842149696d41e57556e4cc0d7 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:07:19 -0400 Subject: [PATCH 09/26] ADD: added _calculate_path call to __init__ --- spyrograph/core/_trochoid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 42246f9..5a172c7 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -83,7 +83,7 @@ def __init__( "Please only pass positive values" )) - + self._calculate_path() def translate(self, x: Number = 0, y: Number = 0) -> "_Trochoid": """ From 44c826b19b2fe8ef25453909d2ace76297fcd30c Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:11:58 -0400 Subject: [PATCH 10/26] ADD: added _validate_inputs --- spyrograph/core/_trochoid.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 5a172c7..eae77cd 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -77,12 +77,7 @@ def __init__( self.origin = origin self.orientation = orientation - if self.R <= 0 or self.r <= 0 or self.d <= 0: - raise ValueError(( - "Negative and/or zero input parameters were passed. " - "Please only pass positive values" - )) - + self._validate_inputs() self._calculate_path() def translate(self, x: Number = 0, y: Number = 0) -> "_Trochoid": @@ -584,6 +579,14 @@ def _calculate_path(self) -> None: self.coords = list(zip(self.x, self.y, self.thetas)) + def _validate_inputs(self) -> None: + """Validate input parameters""" + if self.R <= 0 or self.r <= 0 or self.d <= 0: + raise ValueError(( + "Negative and/or zero input parameters were passed. " + "Please only pass positive values" + )) + def _show_full_path(self, pre_draw_turtle: "turtle.Turtle") -> turtle.Turtle: """Draw the full path prior to tracing""" # pylint: disable=no-member, unused-variable From ee2489395eaa38b01325286fe25bdb3a2ed2ba82 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:27:08 -0400 Subject: [PATCH 11/26] ADD: added rotate functionality --- spyrograph/core/_trochoid.py | 37 ++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index eae77cd..49aedae 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -165,8 +165,34 @@ def scale(self, factor: Number) -> Union["_Trochoid", "_Cycloid"]: ) return scaled_shape - def rotate(self, theta: Number) -> Union["_Trochoid", "_Cycloid"]: - """Return shape with thetas rotated by a given input angle""" + def rotate(self, angle: float): + """ + Rotate the shape by the given angle (in radians). + + Parameters + ---------- + angle : float + The angle to rotate the shape by, in radians. + """ + try: + scaled_shape = self.__class__( + R=self.R, + r=self.r, + d=self.d, + thetas=self.thetas, + origin=self.origin, + orientation=self.orientation + angle + ) + except TypeError: + scaled_shape = self.__class__( + R=self.R, + r=self.r, + thetas=self.thetas, + origin=self.origin, + orientation=self.orientation + angle + ) + return scaled_shape + self.orientation += angle def plot(self, **kwargs) -> Tuple["matplotlib.matplotlib.Figure", "matplotlib.axes._axes.Axes"]: """ @@ -566,12 +592,19 @@ def create_range( )) return shapes + def _apply_rotation(self, x, y, angle): + c, s = np.cos(angle), np.sin(angle) + rotation_matrix = np.array([[c, -s], [s, c]]) + rotated_coords = np.dot(rotation_matrix, np.array([x, y])) + return rotated_coords[0], rotated_coords[1] + def _calculate_path(self) -> None: """Calculate the parametrized path""" self.x = np.array([self._calculate_x(theta) for theta in self.thetas]) self.y = np.array([self._calculate_y(theta) for theta in self.thetas]) self.x += self.origin[0] self.y += self.origin[1] + self.x, self.y = self._apply_rotation(self.x, self.y, self.orientation) self.min_x = min(self.x) self.max_x = max(self.x) self.min_y = min(self.y) From b95f5547bee2f52052587542515cf69bf935dd45 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:35:21 -0400 Subject: [PATCH 12/26] REFACTOR: moved apply rotation func into misc --- spyrograph/core/_misc.py | 6 ++++++ spyrograph/core/_trochoid.py | 10 ++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spyrograph/core/_misc.py b/spyrograph/core/_misc.py index 153d453..6acd109 100644 --- a/spyrograph/core/_misc.py +++ b/spyrograph/core/_misc.py @@ -14,6 +14,12 @@ except ImportError: ImageGrab = None +def _apply_rotation(self, x, y, angle): + c, s = np.cos(angle), np.sin(angle) + rotation_matrix = np.array([[c, -s], [s, c]]) + rotated_coords = np.dot(rotation_matrix, np.array([x, y])) + return rotated_coords[0], rotated_coords[1] + def _validate_theta( thetas: List[Number], theta_start: Number, theta_stop: Number, theta_step: Number diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 49aedae..0e017ec 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -15,7 +15,7 @@ from spyrograph.core._misc import ( _get_products_of_inputs, _validate_only_one_iterable, _draw_animation, - _validate_theta, _save_trace, _get_animate_screen_size + _validate_theta, _save_trace, _get_animate_screen_size, _apply_rotation ) try: @@ -592,19 +592,13 @@ def create_range( )) return shapes - def _apply_rotation(self, x, y, angle): - c, s = np.cos(angle), np.sin(angle) - rotation_matrix = np.array([[c, -s], [s, c]]) - rotated_coords = np.dot(rotation_matrix, np.array([x, y])) - return rotated_coords[0], rotated_coords[1] - def _calculate_path(self) -> None: """Calculate the parametrized path""" self.x = np.array([self._calculate_x(theta) for theta in self.thetas]) self.y = np.array([self._calculate_y(theta) for theta in self.thetas]) self.x += self.origin[0] self.y += self.origin[1] - self.x, self.y = self._apply_rotation(self.x, self.y, self.orientation) + self.x, self.y = _apply_rotation(self.x, self.y, self.orientation) self.min_x = min(self.x) self.max_x = max(self.x) self.min_y = min(self.y) From 442b2f06c14ea141b63697dead0c6c617c098e9a Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:36:28 -0400 Subject: [PATCH 13/26] REMOVE: removed self arg --- spyrograph/core/_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyrograph/core/_misc.py b/spyrograph/core/_misc.py index 6acd109..7fedf8d 100644 --- a/spyrograph/core/_misc.py +++ b/spyrograph/core/_misc.py @@ -14,7 +14,7 @@ except ImportError: ImageGrab = None -def _apply_rotation(self, x, y, angle): +def _apply_rotation(x, y, angle): c, s = np.cos(angle), np.sin(angle) rotation_matrix = np.array([[c, -s], [s, c]]) rotated_coords = np.dot(rotation_matrix, np.array([x, y])) From fc5f81131701e67c3397f9738b68b7a0c29ab3d1 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:37:23 -0400 Subject: [PATCH 14/26] ADD: added return --- spyrograph/core/_misc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spyrograph/core/_misc.py b/spyrograph/core/_misc.py index 7fedf8d..51d9885 100644 --- a/spyrograph/core/_misc.py +++ b/spyrograph/core/_misc.py @@ -14,7 +14,8 @@ except ImportError: ImageGrab = None -def _apply_rotation(x, y, angle): +def _apply_rotation(x: "np.array", y: "np.array", angle: Number): + """Return rotated parametrized values""" c, s = np.cos(angle), np.sin(angle) rotation_matrix = np.array([[c, -s], [s, c]]) rotated_coords = np.dot(rotation_matrix, np.array([x, y])) From 9e00b702dfc2d03deac11f5fcae86e22b8bed86d Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:42:05 -0400 Subject: [PATCH 15/26] ADD: added better docstring --- spyrograph/core/_trochoid.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 0e017ec..00ea952 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -169,10 +169,25 @@ def rotate(self, angle: float): """ Rotate the shape by the given angle (in radians). + This method creates a new instance of the shape with the updated orientation attribute, + keeping the original shape unchanged. + Parameters ---------- angle : float The angle to rotate the shape by, in radians. + + Returns + ------- + rotated_shape : instance of the shape's class + A new instance of the shape with the updated orientation. + + Examples + -------- + >>> from spyrograph import Hypotrochoid + >>> import numpy as np + >>> shape = Hypotrochoid(R=233, r=200, d=233, thetas=np.arange(0, 100*np.pi, .5)) + >>> rotated_shape = shape.rotate(np.pi / 4) # Rotate the shape by 45 degrees """ try: scaled_shape = self.__class__( @@ -192,7 +207,6 @@ def rotate(self, angle: float): orientation=self.orientation + angle ) return scaled_shape - self.orientation += angle def plot(self, **kwargs) -> Tuple["matplotlib.matplotlib.Figure", "matplotlib.axes._axes.Axes"]: """ From 4b11a531d5080b67e950ba773563987757a96730 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:45:55 -0400 Subject: [PATCH 16/26] ADD: added orientation pass through for scale and translate --- spyrograph/core/_trochoid.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 00ea952..2a9e879 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -108,14 +108,16 @@ def translate(self, x: Number = 0, y: Number = 0) -> "_Trochoid": r=self.r, d=self.d, thetas=self.thetas, - origin=(self.origin[0]+x, self.origin[1]+y) + origin=(self.origin[0]+x, self.origin[1]+y), + orientation=self.orientation ) except TypeError: translated_shape = self.__class__( R=self.R, r=self.r, thetas=self.thetas, - origin=(self.origin[0]+x, self.origin[1]+y) + origin=(self.origin[0]+x, self.origin[1]+y), + orientation=self.orientation ) return translated_shape @@ -154,14 +156,16 @@ def scale(self, factor: Number) -> Union["_Trochoid", "_Cycloid"]: r=self.r*factor, d=self.d*factor, thetas=self.thetas, - origin=self.origin + origin=self.origin, + orientation=self.orientation ) except TypeError: scaled_shape = self.__class__( R=self.R*factor, r=self.r*factor, thetas=self.thetas, - origin=self.origin + origin=self.origin, + orientation=self.orientation ) return scaled_shape From 4f63893af40765f7aeae691a1d2a5a8259df4781 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:48:25 -0400 Subject: [PATCH 17/26] ADD: added orientation to n_cusps --- spyrograph/core/_cycloid.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spyrograph/core/_cycloid.py b/spyrograph/core/_cycloid.py index 14fea75..8666949 100644 --- a/spyrograph/core/_cycloid.py +++ b/spyrograph/core/_cycloid.py @@ -17,7 +17,7 @@ def __init__( self, R: Number, r: Number, thetas: List[Number] = None, theta_start: Number = None, theta_stop: Number = None, theta_step: Number = None, origin: Tuple[Number, Number] = (0, 0), - orientation: Number = None + orientation: Number = 0 ) -> None: super().__init__(R, r, r, thetas, theta_start, theta_stop, theta_step, origin, orientation) # pylint: disable=pointless-string-statement @@ -202,7 +202,8 @@ def create_range( def n_cusps( cls, R: Number, n: int, thetas: List[Number] = None, theta_start: Number = None, theta_stop: Number = None, - theta_step: Number = None, origin: Tuple[Number, Number] = (0, 0) + theta_step: Number = None, origin: Tuple[Number, Number] = (0, 0), + orientation: Number = 0 ) -> "Cycloid": """ Create and return a cycloid with a specified number of cusps. @@ -227,6 +228,8 @@ def n_cusps( to built-in range or np.arange). origin : Tuple[Number, Number], optional, default: (0, 0) Custom origin to center the shapes at. Default is (0, 0). + orientation : Number = 0 + Angle of rotation for the shape Returns ------- From 5825426dc1f5bc9939ec4a3eb556bea372a2ad03 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 21:52:04 -0400 Subject: [PATCH 18/26] ADD: added test_rotate_orientation --- tests/_trochoid.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/_trochoid.py b/tests/_trochoid.py index eac80b0..e13026d 100644 --- a/tests/_trochoid.py +++ b/tests/_trochoid.py @@ -276,3 +276,8 @@ def test_dataframe_property(self, instance) -> None: assert all(instance.df["x"].to_numpy() == instance.x) assert all(instance.df["y"].to_numpy() == instance.y) assert all(instance.df["theta"].to_numpy() == instance.thetas) + + def test_rotate_orientation(self, instance) -> None: + rotated_shape = instance.rotate(.1) + assert instance.orientation == 0 + assert rotated_shape.orientation == .1 \ No newline at end of file From 392fdab368989c33d2b96dae2f7f91a3fafb1dc2 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:00:09 -0400 Subject: [PATCH 19/26] ADD: added vertical space --- tests/_trochoid.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/_trochoid.py b/tests/_trochoid.py index e13026d..291c6fd 100644 --- a/tests/_trochoid.py +++ b/tests/_trochoid.py @@ -101,6 +101,44 @@ def test_scale_factor_parameters_smaller(self, instance): assert smaller_scaled_instance.d == instance.d*.5 assert (smaller_scaled_instance.thetas == instance.thetas).all() + def test_scale_origin_is_preserved(self, thetas): + if issubclass(self.class_name, _Cycloid): + translated_shape = self.class_name( + R=100, + r=200, + thetas=thetas, + origin=(100,100) + ) + elif issubclass(self.class_name, _Trochoid): + translated_shape = self.class_name( + R=100, + r=200, + d=300, + thetas=thetas, + origin=(100, 100) + ) + scaled_shape = translated_shape.scale(2) + assert scaled_shape.origin == translated_shape.origin + + def test_rotate_origin_is_preserved(self, thetas): + if issubclass(self.class_name, _Cycloid): + translated_shape = self.class_name( + R=100, + r=200, + thetas=thetas, + origin=(100, 100) + ) + elif issubclass(self.class_name, _Trochoid): + translated_shape = self.class_name( + R=100, + r=200, + d=300, + thetas=thetas, + origin=(100, 100) + ) + rotated_shape = translated_shape.rotate(2) + assert rotated_shape.origin == rotated_shape.origin + def test_create_range_theta_inputs(self, thetas): R = 5 r = 3 From 3e04888879814fad9eefc9d22cb332132a8c0d39 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:05:47 -0400 Subject: [PATCH 20/26] ADD: added rotated shape parameter preservation --- tests/_trochoid.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/_trochoid.py b/tests/_trochoid.py index 291c6fd..63855ed 100644 --- a/tests/_trochoid.py +++ b/tests/_trochoid.py @@ -318,4 +318,9 @@ def test_dataframe_property(self, instance) -> None: def test_rotate_orientation(self, instance) -> None: rotated_shape = instance.rotate(.1) assert instance.orientation == 0 - assert rotated_shape.orientation == .1 \ No newline at end of file + assert rotated_shape.orientation == .1 + + def test_parameters_are_preserved(self, instance) -> None: + rotated_shape = instance.rotate(1) + assert instance.R == rotated_shape.R + assert instance.r == rotated_shape.r From 4223e4e79d24cd3e28b61ce4034c79dd194fdd84 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:11:24 -0400 Subject: [PATCH 21/26] MODIFY: incr minor version number to 0.27.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2f9a5ad..89476a9 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setuptools.setup( name="spyrograph", - version="0.26.0", + version="0.27.0", author="Chris Greening", author_email="chris@christophergreening.com", description="Library for drawing spirographs in Python", From 3ce2dea365084a13f407f8403629bcbe3d192095 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:13:33 -0400 Subject: [PATCH 22/26] ADD: added better names of variables --- spyrograph/core/_misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spyrograph/core/_misc.py b/spyrograph/core/_misc.py index 51d9885..7cd90da 100644 --- a/spyrograph/core/_misc.py +++ b/spyrograph/core/_misc.py @@ -16,8 +16,8 @@ def _apply_rotation(x: "np.array", y: "np.array", angle: Number): """Return rotated parametrized values""" - c, s = np.cos(angle), np.sin(angle) - rotation_matrix = np.array([[c, -s], [s, c]]) + cos_angle, sin_angle = np.cos(angle), np.sin(angle) + rotation_matrix = np.array([[cos_angle, -sin_angle], [sin_angle, cos_angle]]) rotated_coords = np.dot(rotation_matrix, np.array([x, y])) return rotated_coords[0], rotated_coords[1] From dbac81a6492ad6740edc59e633d49ef49a0199e1 Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:14:23 -0400 Subject: [PATCH 23/26] ADD: added orientation to cls --- spyrograph/core/_cycloid.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spyrograph/core/_cycloid.py b/spyrograph/core/_cycloid.py index 8666949..1b3b213 100644 --- a/spyrograph/core/_cycloid.py +++ b/spyrograph/core/_cycloid.py @@ -249,5 +249,6 @@ def n_cusps( theta_start=theta_start, theta_stop=theta_stop, theta_step=theta_step, - origin=origin + origin=origin, + orientation=orientation ) From d880d8e4b9ac8a70804ce76b0fdc5f12a1e44bdc Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:16:25 -0400 Subject: [PATCH 24/26] ADD: added pylint disable --- spyrograph/core/_trochoid.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 2a9e879..24247ba 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -193,6 +193,7 @@ def rotate(self, angle: float): >>> shape = Hypotrochoid(R=233, r=200, d=233, thetas=np.arange(0, 100*np.pi, .5)) >>> rotated_shape = shape.rotate(np.pi / 4) # Rotate the shape by 45 degrees """ + # pylint: disable=no-value-for-parameter try: scaled_shape = self.__class__( R=self.R, From 087e604a127067d4ea4b7fe9849fcf753a3bbfaf Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:32:19 -0400 Subject: [PATCH 25/26] ADD: added degrees flag for specifying rotation in degrees instead of radians --- spyrograph/core/_trochoid.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spyrograph/core/_trochoid.py b/spyrograph/core/_trochoid.py index 24247ba..eb0e040 100644 --- a/spyrograph/core/_trochoid.py +++ b/spyrograph/core/_trochoid.py @@ -169,7 +169,7 @@ def scale(self, factor: Number) -> Union["_Trochoid", "_Cycloid"]: ) return scaled_shape - def rotate(self, angle: float): + def rotate(self, angle: float, degrees: bool = False): """ Rotate the shape by the given angle (in radians). @@ -179,7 +179,9 @@ def rotate(self, angle: float): Parameters ---------- angle : float - The angle to rotate the shape by, in radians. + The angle to rotate the shape by, in radians + degrees : bool + Rotate in degrees Returns ------- @@ -194,6 +196,8 @@ def rotate(self, angle: float): >>> rotated_shape = shape.rotate(np.pi / 4) # Rotate the shape by 45 degrees """ # pylint: disable=no-value-for-parameter + if degrees: + angle = np.deg2rad(angle) try: scaled_shape = self.__class__( R=self.R, From 3d73f22608e4a905743b283d0ae35b15acaa7b8d Mon Sep 17 00:00:00 2001 From: Christopher Greening Date: Tue, 11 Apr 2023 22:51:15 -0400 Subject: [PATCH 26/26] ADD: added custom orientation test --- tests/_cycloid.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/_cycloid.py b/tests/_cycloid.py index 3bc0baf..a08cabd 100644 --- a/tests/_cycloid.py +++ b/tests/_cycloid.py @@ -63,3 +63,12 @@ def test_n_cusps_custom_origin(self, thetas): ) assert ((custom_origin_obj.x - base_obj.x).round() == 54.0).all() assert ((custom_origin_obj.y - base_obj.y).round() == -233.0).all() + + def test_n_cusps_custom_orientation(self, thetas) -> None: + custom_orientation_obj = self.class_name.n_cusps( + R = 300, + n = 2, + thetas=thetas, + orientation = 1 + ) + assert custom_orientation_obj.orientation == 1 \ No newline at end of file