From 44e835a72f341a94b73a8129e4dbae135baaa5f7 Mon Sep 17 00:00:00 2001 From: Jonas Rinke Date: Sun, 15 Nov 2020 14:51:59 +0100 Subject: [PATCH 1/2] Added domain coloring in Python --- .../code/python/domain_coloring.py | 72 +++++++++++++++++++ contents/domain_coloring/domain_coloring.md | 2 + 2 files changed, 74 insertions(+) create mode 100644 contents/domain_coloring/code/python/domain_coloring.py diff --git a/contents/domain_coloring/code/python/domain_coloring.py b/contents/domain_coloring/code/python/domain_coloring.py new file mode 100644 index 000000000..ea41f67a7 --- /dev/null +++ b/contents/domain_coloring/code/python/domain_coloring.py @@ -0,0 +1,72 @@ + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.colors +from matplotlib.cm import ScalarMappable + +def f(z): + return z**2 + +def magnitude_shading(f_val): + f_val_abs = np.abs(f_val) + return 0.5 + 0.5 * (f_val_abs - np.floor(f_val_abs)) + +def gridlines(f_val, threshold): + return np.abs(np.sin(np.pi * np.real(f_val))) ** threshold * \ + np.abs(np.sin(np.pi * np.imag(f_val))) ** threshold + +def color(f_val, threshold): + hue = (np.pi - np.angle(f_val)) / (2.0 * np.pi) + saturation = magnitude_shading(f_val) + value = gridlines(f_val, threshold) + + # Currently we have a tuple of 2D-arrays (hue, saturation, value). + # This makes it a 2D-array of tuples, which the conversion function requires. + hsv = np.moveaxis((hue, saturation, value), 0, -1) + return matplotlib.colors.hsv_to_rgb(hsv) + +if __name__ == "__main__": + # Create a new figure containing a single plot + fig, axes = plt.subplots(1, 1) + + # Set the title for the plot + axes.set_title("$f(x)=z^2$") + + # Create color bar + cbar = fig.colorbar(ScalarMappable(matplotlib.colors.Normalize(0.0, 2.0 * np.pi), "hsv"), + ax=axes, label="Phase Angle") + + # Set x and y labels + axes.set_xlabel("$Re(z)$") + axes.set_ylabel("$Im(z)$") + + # Set color bar tick locations and labels + cbar.set_ticks([0.0, np.pi, 2.0 * np.pi]) + cbar.set_ticklabels(["$0.0$", "$\pi$", "$2\pi$"]) + + # Hide x and y ticks + for tick in axes.get_xticklines(): + tick.set_visible(False) + + for tick in axes.get_yticklines(): + tick.set_visible(False) + + # Create a 500x500 input grid + coords = np.linspace(-2.0, 2.0, 500) + z_real, z_imag = np.meshgrid(coords, coords) + z = z_real + 1j * z_imag + + # Calculate function values + f_val = f(z) + + # Map function values to colors + colors = color(f_val, 0.1) + + # Plot the colors + # origin='lower' places index (0,0) of the color array in the lower-left corner + # aspect='equal' ensures that the plot is square + # extent=(-2.0, 2.0, -2.0, 2.0) sets the x and y ranges + axes.imshow(colors, extent=(-2.0, 2.0, -2.0, 2.0), origin='lower', aspect='equal') + + # Save output + fig.savefig("domain.png") diff --git a/contents/domain_coloring/domain_coloring.md b/contents/domain_coloring/domain_coloring.md index 31facd2cf..39159e650 100644 --- a/contents/domain_coloring/domain_coloring.md +++ b/contents/domain_coloring/domain_coloring.md @@ -174,6 +174,8 @@ Here is the full script to generate a domain colored output of $$f(z)=z^2$$. {% method %} {% sample lang="gnuplot" %} [import, lang:"gnuplot"](code/gnuplot/domain_coloring.gp) +{% sample lang="python & matplotlib" %} +[import, lang:"python"](code/python/domain_coloring.py) {% endmethod %} ### Bibliography From 259e42854c4700c544767866d5965fb0886a09b3 Mon Sep 17 00:00:00 2001 From: Jonas Rinke Date: Sun, 15 Nov 2020 21:01:55 +0100 Subject: [PATCH 2/2] Some cleanup --- .../code/python/domain_coloring.py | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/contents/domain_coloring/code/python/domain_coloring.py b/contents/domain_coloring/code/python/domain_coloring.py index ea41f67a7..cb8be03f5 100644 --- a/contents/domain_coloring/code/python/domain_coloring.py +++ b/contents/domain_coloring/code/python/domain_coloring.py @@ -4,16 +4,20 @@ import matplotlib.colors from matplotlib.cm import ScalarMappable + def f(z): return z**2 + def magnitude_shading(f_val): f_val_abs = np.abs(f_val) return 0.5 + 0.5 * (f_val_abs - np.floor(f_val_abs)) + def gridlines(f_val, threshold): - return np.abs(np.sin(np.pi * np.real(f_val))) ** threshold * \ - np.abs(np.sin(np.pi * np.imag(f_val))) ** threshold + return (np.abs(np.sin(np.pi * np.real(f_val))) ** threshold + * np.abs(np.sin(np.pi * np.imag(f_val))) ** threshold) + def color(f_val, threshold): hue = (np.pi - np.angle(f_val)) / (2.0 * np.pi) @@ -25,6 +29,7 @@ def color(f_val, threshold): hsv = np.moveaxis((hue, saturation, value), 0, -1) return matplotlib.colors.hsv_to_rgb(hsv) + if __name__ == "__main__": # Create a new figure containing a single plot fig, axes = plt.subplots(1, 1) @@ -33,8 +38,10 @@ def color(f_val, threshold): axes.set_title("$f(x)=z^2$") # Create color bar - cbar = fig.colorbar(ScalarMappable(matplotlib.colors.Normalize(0.0, 2.0 * np.pi), "hsv"), - ax=axes, label="Phase Angle") + cbar = fig.colorbar( + ScalarMappable(matplotlib.colors.Normalize(0.0, 2.0 * np.pi), "hsv"), + ax=axes, + label="Phase Angle") # Set x and y labels axes.set_xlabel("$Re(z)$") @@ -63,10 +70,14 @@ def color(f_val, threshold): colors = color(f_val, 0.1) # Plot the colors - # origin='lower' places index (0,0) of the color array in the lower-left corner - # aspect='equal' ensures that the plot is square # extent=(-2.0, 2.0, -2.0, 2.0) sets the x and y ranges - axes.imshow(colors, extent=(-2.0, 2.0, -2.0, 2.0), origin='lower', aspect='equal') + # origin="lower" places index (0,0) of the color array in the lower-left corner + # aspect="equal" ensures that the plot is square + axes.imshow( + colors, + extent=(-2.0, 2.0, -2.0, 2.0), + origin="lower", + aspect="equal") # Save output fig.savefig("domain.png")