From 0a316a50e02243840a497ed8672e9bd43ef421ab Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 15 Feb 2021 19:28:32 +0530 Subject: [PATCH 1/7] Add files via upload Implementation of the algorithm for the Koch snowflake --- other/koch_snowflake.py | 119 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 other/koch_snowflake.py diff --git a/other/koch_snowflake.py b/other/koch_snowflake.py new file mode 100644 index 000000000000..e318393a3f7f --- /dev/null +++ b/other/koch_snowflake.py @@ -0,0 +1,119 @@ +""" +Description + The Koch snowflake is a fractal curve and one of the earliest fractals to + have been described. The Koch snowflake can be built up iteratively, in a + sequence of stages. The first stage is an equilateral triangle, and each + successive stage is formed by adding outward bends to each side of the + previous stage, making smaller equilateral triangles. + This can be achieved through the following steps for each line: + 1. divide the line segment into three segments of equal length. + 2. draw an equilateral triangle that has the middle segment from step 1 + as its base and points outward. + 3. remove the line segment that is the base of the triangle from step 2. + (description adapted from https://en.wikipedia.org/wiki/Koch_snowflake ) + (for a more detailed explanation and an implementation in the + Processing language, see https://natureofcode.com/book/chapter-8-fractals/ + #84-the-koch-curve-and-the-arraylist-technique ) + +Requirements(pip) + - numpy + - matplotlib +""" + + +from __future__ import annotations +import numpy + + +# initial triangle of Koch snowflake +VECTOR1 = numpy.array([0, 0]) +VECTOR2 = numpy.array([0.5, 0.8660254]) +VECTOR3 = numpy.array([1, 0]) +INITIAL_VECTORS = [VECTOR1, VECTOR2, VECTOR3, VECTOR1] + +# uncomment for simple Koch curve instead of Koch snowflake +# INITIAL_VECTORS = [VECTOR1, VECTOR3] + + +def iterate(initial_vectors: list[numpy.ndarray], steps: int) -> list[numpy.ndarray]: + """ + Go through the number of iterations determined by the argument "steps". + Be careful with high values (above 5) since the time to calculate increases + exponentially. + >>> iterate([numpy.array([0, 0]), numpy.array([1, 0])], 1) + [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ +0.28867513]), array([0.66666667, 0. ]), array([1, 0])] + """ + vectors = initial_vectors + for i in range(steps): + vectors = iteration_step(vectors) + return vectors + + +def iteration_step(vectors: list[numpy.ndarray]) -> list[numpy.ndarray]: + """ + Loops through each pair of adjacent vectors. Each line between two adjacent + vectors is divided into 4 segments by adding 3 additional vectors in-between + the original two vectors. The vector in the middle is constructed through a + 60 degree rotation so it is bent outwards. + >>> iteration_step([numpy.array([0, 0]), numpy.array([1, 0])]) + [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ +0.28867513]), array([0.66666667, 0. ]), array([1, 0])] + """ + new_vectors = [] + for i in range(len(vectors) - 1): + start_vector = vectors[i] + end_vector = vectors[i + 1] + new_vectors.append(start_vector) + difference_vector = end_vector - start_vector + new_vectors.append(start_vector + difference_vector / 3) + new_vectors.append( + start_vector + difference_vector / 3 + rotate(difference_vector / 3, 60) + ) + new_vectors.append(start_vector + difference_vector * 2 / 3) + new_vectors.append(vectors[-1]) + return new_vectors + + +def rotate(vector: numpy.ndarray, angle_in_degrees: float) -> numpy.ndarray: + """ + Standard rotation of a 2D vector with a rotation matrix + (see https://en.wikipedia.org/wiki/Rotation_matrix ) + >>> rotate(numpy.array([1, 0]), 60) + array([0.5 , 0.8660254]) + >>> rotate(numpy.array([1, 0]), 90) + array([6.123234e-17, 1.000000e+00]) + """ + theta = numpy.radians(angle_in_degrees) + c, s = numpy.cos(theta), numpy.sin(theta) + rotation_matrix = numpy.array(((c, -s), (s, c))) + return numpy.dot(rotation_matrix, vector) + + +def plot(vectors: list[numpy.ndarray]) -> None: + import matplotlib.pyplot as plt # type: ignore + + # avoid stretched display of graph + axes = plt.gca() + axes.set_aspect("equal") + + # matplotlib.pyplot.plot takes a list of all x-coordinates and a list of all + # y-coordinates as inputs, which need to be constructed from our vector-list + x_coordinates = [] + for vector in vectors: + x_coordinates.append(vector[0]) + y_coordinates = [] + for vector in vectors: + y_coordinates.append(vector[1]) + + plt.plot(x_coordinates, y_coordinates) + plt.show() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + processed_vectors = iterate(INITIAL_VECTORS, 5) + plot(processed_vectors) From 526f49d0bef626cb0fca376482538f8fca49ae52 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 15 Feb 2021 20:06:52 +0530 Subject: [PATCH 2/7] added underscore to variable names --- other/koch_snowflake.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/other/koch_snowflake.py b/other/koch_snowflake.py index e318393a3f7f..6cdd2a2469e2 100644 --- a/other/koch_snowflake.py +++ b/other/koch_snowflake.py @@ -26,13 +26,13 @@ # initial triangle of Koch snowflake -VECTOR1 = numpy.array([0, 0]) -VECTOR2 = numpy.array([0.5, 0.8660254]) -VECTOR3 = numpy.array([1, 0]) -INITIAL_VECTORS = [VECTOR1, VECTOR2, VECTOR3, VECTOR1] +VECTOR_1 = numpy.array([0, 0]) +VECTOR_2 = numpy.array([0.5, 0.8660254]) +VECTOR_3 = numpy.array([1, 0]) +INITIAL_VECTORS = [VECTOR_1, VECTOR_2, VECTOR_3, VECTOR_1] # uncomment for simple Koch curve instead of Koch snowflake -# INITIAL_VECTORS = [VECTOR1, VECTOR3] +# INITIAL_VECTORS = [VECTOR_1, VECTOR_3] def iterate(initial_vectors: list[numpy.ndarray], steps: int) -> list[numpy.ndarray]: From 62f0d602d4a15d913d9c2762b936fc8208aa80d7 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 20 Feb 2021 09:26:21 +0530 Subject: [PATCH 3/7] added newline and comment I fixed the sorting of the imports and I added a comment to the plot-function to explain what it does and why it doesn't use a doctest. Thank you to user mrmaxguns for suggesting these changes. --- other/koch_snowflake.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/other/koch_snowflake.py b/other/koch_snowflake.py index 6cdd2a2469e2..43bce6175e7e 100644 --- a/other/koch_snowflake.py +++ b/other/koch_snowflake.py @@ -22,6 +22,7 @@ from __future__ import annotations + import numpy @@ -91,7 +92,12 @@ def rotate(vector: numpy.ndarray, angle_in_degrees: float) -> numpy.ndarray: def plot(vectors: list[numpy.ndarray]) -> None: - import matplotlib.pyplot as plt # type: ignore + """ + Utility function to plot the vectors using matplotlib.pyplot + No doctest was implemented since this function does not have a return value + """ + import matplotlib.pyplot. + as plt # type: ignore # avoid stretched display of graph axes = plt.gca() From 4530a56c7b2ee54c632a4201907b3493a708c13a Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 20 Feb 2021 09:53:20 +0530 Subject: [PATCH 4/7] fixed accidental newline in the middle of expression --- other/koch_snowflake.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/other/koch_snowflake.py b/other/koch_snowflake.py index 43bce6175e7e..f2f8377cd665 100644 --- a/other/koch_snowflake.py +++ b/other/koch_snowflake.py @@ -25,7 +25,6 @@ import numpy - # initial triangle of Koch snowflake VECTOR_1 = numpy.array([0, 0]) VECTOR_2 = numpy.array([0.5, 0.8660254]) @@ -96,8 +95,7 @@ def plot(vectors: list[numpy.ndarray]) -> None: Utility function to plot the vectors using matplotlib.pyplot No doctest was implemented since this function does not have a return value """ - import matplotlib.pyplot. - as plt # type: ignore + import matplotlib.pyplot as plt # type: ignore # avoid stretched display of graph axes = plt.gca() From 4224ad4566079d4b2bfcc996c2f16a7f515101e1 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 20 Feb 2021 17:16:16 +0530 Subject: [PATCH 5/7] improved looping --- other/koch_snowflake.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/other/koch_snowflake.py b/other/koch_snowflake.py index f2f8377cd665..c53e8e003298 100644 --- a/other/koch_snowflake.py +++ b/other/koch_snowflake.py @@ -61,8 +61,7 @@ def iteration_step(vectors: list[numpy.ndarray]) -> list[numpy.ndarray]: 0.28867513]), array([0.66666667, 0. ]), array([1, 0])] """ new_vectors = [] - for i in range(len(vectors) - 1): - start_vector = vectors[i] + for i, start_vector in enumerate(vectors[:-1]): end_vector = vectors[i + 1] new_vectors.append(start_vector) difference_vector = end_vector - start_vector @@ -102,14 +101,9 @@ def plot(vectors: list[numpy.ndarray]) -> None: axes.set_aspect("equal") # matplotlib.pyplot.plot takes a list of all x-coordinates and a list of all - # y-coordinates as inputs, which need to be constructed from our vector-list - x_coordinates = [] - for vector in vectors: - x_coordinates.append(vector[0]) - y_coordinates = [] - for vector in vectors: - y_coordinates.append(vector[1]) - + # y-coordinates as inputs, which are constructed from the vector-list using + # zip() + x_coordinates, y_coordinates = zip(*vectors) plt.plot(x_coordinates, y_coordinates) plt.show() From 84de20006dda6fe5ac34c0cf0f083c4469e01f56 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 20 Feb 2021 17:17:22 +0530 Subject: [PATCH 6/7] moved "koch_snowflake.py" from "other" to "graphics" --- {other => graphics}/koch_snowflake.py | 234 +++++++++++++------------- 1 file changed, 117 insertions(+), 117 deletions(-) rename {other => graphics}/koch_snowflake.py (97%) diff --git a/other/koch_snowflake.py b/graphics/koch_snowflake.py similarity index 97% rename from other/koch_snowflake.py rename to graphics/koch_snowflake.py index c53e8e003298..a5dbabc98876 100644 --- a/other/koch_snowflake.py +++ b/graphics/koch_snowflake.py @@ -1,117 +1,117 @@ -""" -Description - The Koch snowflake is a fractal curve and one of the earliest fractals to - have been described. The Koch snowflake can be built up iteratively, in a - sequence of stages. The first stage is an equilateral triangle, and each - successive stage is formed by adding outward bends to each side of the - previous stage, making smaller equilateral triangles. - This can be achieved through the following steps for each line: - 1. divide the line segment into three segments of equal length. - 2. draw an equilateral triangle that has the middle segment from step 1 - as its base and points outward. - 3. remove the line segment that is the base of the triangle from step 2. - (description adapted from https://en.wikipedia.org/wiki/Koch_snowflake ) - (for a more detailed explanation and an implementation in the - Processing language, see https://natureofcode.com/book/chapter-8-fractals/ - #84-the-koch-curve-and-the-arraylist-technique ) - -Requirements(pip) - - numpy - - matplotlib -""" - - -from __future__ import annotations - -import numpy - -# initial triangle of Koch snowflake -VECTOR_1 = numpy.array([0, 0]) -VECTOR_2 = numpy.array([0.5, 0.8660254]) -VECTOR_3 = numpy.array([1, 0]) -INITIAL_VECTORS = [VECTOR_1, VECTOR_2, VECTOR_3, VECTOR_1] - -# uncomment for simple Koch curve instead of Koch snowflake -# INITIAL_VECTORS = [VECTOR_1, VECTOR_3] - - -def iterate(initial_vectors: list[numpy.ndarray], steps: int) -> list[numpy.ndarray]: - """ - Go through the number of iterations determined by the argument "steps". - Be careful with high values (above 5) since the time to calculate increases - exponentially. - >>> iterate([numpy.array([0, 0]), numpy.array([1, 0])], 1) - [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ -0.28867513]), array([0.66666667, 0. ]), array([1, 0])] - """ - vectors = initial_vectors - for i in range(steps): - vectors = iteration_step(vectors) - return vectors - - -def iteration_step(vectors: list[numpy.ndarray]) -> list[numpy.ndarray]: - """ - Loops through each pair of adjacent vectors. Each line between two adjacent - vectors is divided into 4 segments by adding 3 additional vectors in-between - the original two vectors. The vector in the middle is constructed through a - 60 degree rotation so it is bent outwards. - >>> iteration_step([numpy.array([0, 0]), numpy.array([1, 0])]) - [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ -0.28867513]), array([0.66666667, 0. ]), array([1, 0])] - """ - new_vectors = [] - for i, start_vector in enumerate(vectors[:-1]): - end_vector = vectors[i + 1] - new_vectors.append(start_vector) - difference_vector = end_vector - start_vector - new_vectors.append(start_vector + difference_vector / 3) - new_vectors.append( - start_vector + difference_vector / 3 + rotate(difference_vector / 3, 60) - ) - new_vectors.append(start_vector + difference_vector * 2 / 3) - new_vectors.append(vectors[-1]) - return new_vectors - - -def rotate(vector: numpy.ndarray, angle_in_degrees: float) -> numpy.ndarray: - """ - Standard rotation of a 2D vector with a rotation matrix - (see https://en.wikipedia.org/wiki/Rotation_matrix ) - >>> rotate(numpy.array([1, 0]), 60) - array([0.5 , 0.8660254]) - >>> rotate(numpy.array([1, 0]), 90) - array([6.123234e-17, 1.000000e+00]) - """ - theta = numpy.radians(angle_in_degrees) - c, s = numpy.cos(theta), numpy.sin(theta) - rotation_matrix = numpy.array(((c, -s), (s, c))) - return numpy.dot(rotation_matrix, vector) - - -def plot(vectors: list[numpy.ndarray]) -> None: - """ - Utility function to plot the vectors using matplotlib.pyplot - No doctest was implemented since this function does not have a return value - """ - import matplotlib.pyplot as plt # type: ignore - - # avoid stretched display of graph - axes = plt.gca() - axes.set_aspect("equal") - - # matplotlib.pyplot.plot takes a list of all x-coordinates and a list of all - # y-coordinates as inputs, which are constructed from the vector-list using - # zip() - x_coordinates, y_coordinates = zip(*vectors) - plt.plot(x_coordinates, y_coordinates) - plt.show() - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - - processed_vectors = iterate(INITIAL_VECTORS, 5) - plot(processed_vectors) +""" +Description + The Koch snowflake is a fractal curve and one of the earliest fractals to + have been described. The Koch snowflake can be built up iteratively, in a + sequence of stages. The first stage is an equilateral triangle, and each + successive stage is formed by adding outward bends to each side of the + previous stage, making smaller equilateral triangles. + This can be achieved through the following steps for each line: + 1. divide the line segment into three segments of equal length. + 2. draw an equilateral triangle that has the middle segment from step 1 + as its base and points outward. + 3. remove the line segment that is the base of the triangle from step 2. + (description adapted from https://en.wikipedia.org/wiki/Koch_snowflake ) + (for a more detailed explanation and an implementation in the + Processing language, see https://natureofcode.com/book/chapter-8-fractals/ + #84-the-koch-curve-and-the-arraylist-technique ) + +Requirements(pip) + - numpy + - matplotlib +""" + + +from __future__ import annotations + +import numpy + +# initial triangle of Koch snowflake +VECTOR_1 = numpy.array([0, 0]) +VECTOR_2 = numpy.array([0.5, 0.8660254]) +VECTOR_3 = numpy.array([1, 0]) +INITIAL_VECTORS = [VECTOR_1, VECTOR_2, VECTOR_3, VECTOR_1] + +# uncomment for simple Koch curve instead of Koch snowflake +# INITIAL_VECTORS = [VECTOR_1, VECTOR_3] + + +def iterate(initial_vectors: list[numpy.ndarray], steps: int) -> list[numpy.ndarray]: + """ + Go through the number of iterations determined by the argument "steps". + Be careful with high values (above 5) since the time to calculate increases + exponentially. + >>> iterate([numpy.array([0, 0]), numpy.array([1, 0])], 1) + [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ +0.28867513]), array([0.66666667, 0. ]), array([1, 0])] + """ + vectors = initial_vectors + for i in range(steps): + vectors = iteration_step(vectors) + return vectors + + +def iteration_step(vectors: list[numpy.ndarray]) -> list[numpy.ndarray]: + """ + Loops through each pair of adjacent vectors. Each line between two adjacent + vectors is divided into 4 segments by adding 3 additional vectors in-between + the original two vectors. The vector in the middle is constructed through a + 60 degree rotation so it is bent outwards. + >>> iteration_step([numpy.array([0, 0]), numpy.array([1, 0])]) + [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ +0.28867513]), array([0.66666667, 0. ]), array([1, 0])] + """ + new_vectors = [] + for i, start_vector in enumerate(vectors[:-1]): + end_vector = vectors[i + 1] + new_vectors.append(start_vector) + difference_vector = end_vector - start_vector + new_vectors.append(start_vector + difference_vector / 3) + new_vectors.append( + start_vector + difference_vector / 3 + rotate(difference_vector / 3, 60) + ) + new_vectors.append(start_vector + difference_vector * 2 / 3) + new_vectors.append(vectors[-1]) + return new_vectors + + +def rotate(vector: numpy.ndarray, angle_in_degrees: float) -> numpy.ndarray: + """ + Standard rotation of a 2D vector with a rotation matrix + (see https://en.wikipedia.org/wiki/Rotation_matrix ) + >>> rotate(numpy.array([1, 0]), 60) + array([0.5 , 0.8660254]) + >>> rotate(numpy.array([1, 0]), 90) + array([6.123234e-17, 1.000000e+00]) + """ + theta = numpy.radians(angle_in_degrees) + c, s = numpy.cos(theta), numpy.sin(theta) + rotation_matrix = numpy.array(((c, -s), (s, c))) + return numpy.dot(rotation_matrix, vector) + + +def plot(vectors: list[numpy.ndarray]) -> None: + """ + Utility function to plot the vectors using matplotlib.pyplot + No doctest was implemented since this function does not have a return value + """ + import matplotlib.pyplot as plt # type: ignore + + # avoid stretched display of graph + axes = plt.gca() + axes.set_aspect("equal") + + # matplotlib.pyplot.plot takes a list of all x-coordinates and a list of all + # y-coordinates as inputs, which are constructed from the vector-list using + # zip() + x_coordinates, y_coordinates = zip(*vectors) + plt.plot(x_coordinates, y_coordinates) + plt.show() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + processed_vectors = iterate(INITIAL_VECTORS, 5) + plot(processed_vectors) From 3ef66beb05483d5bbb1bb33b006689a90f57dbc2 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 20 Feb 2021 14:09:10 +0100 Subject: [PATCH 7/7] Update koch_snowflake.py --- graphics/koch_snowflake.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/graphics/koch_snowflake.py b/graphics/koch_snowflake.py index a5dbabc98876..07c1835b41ed 100644 --- a/graphics/koch_snowflake.py +++ b/graphics/koch_snowflake.py @@ -15,14 +15,15 @@ Processing language, see https://natureofcode.com/book/chapter-8-fractals/ #84-the-koch-curve-and-the-arraylist-technique ) -Requirements(pip) - - numpy +Requirements (pip): - matplotlib + - numpy """ from __future__ import annotations +import matplotlib.pyplot as plt # type: ignore import numpy # initial triangle of Koch snowflake @@ -94,8 +95,6 @@ def plot(vectors: list[numpy.ndarray]) -> None: Utility function to plot the vectors using matplotlib.pyplot No doctest was implemented since this function does not have a return value """ - import matplotlib.pyplot as plt # type: ignore - # avoid stretched display of graph axes = plt.gca() axes.set_aspect("equal")