|
| 1 | +--- |
| 2 | +title: "Art from UNC BIOL222" |
| 3 | +date: 2021-11-19T08:46:00-08:00 |
| 4 | +draft: false |
| 5 | +description: "UNC BIOL222: Art created with Matplotlib" |
| 6 | +categories: ["art", "academia"] |
| 7 | +displayInList: true |
| 8 | +author: Joseph Lucas |
| 9 | +resources: |
| 10 | +- name: featuredImage |
| 11 | + src: "fox.png" |
| 12 | + params: |
| 13 | + description: "Emily Foster's Fox" |
| 14 | + showOnTop: true |
| 15 | +--- |
| 16 | + |
| 17 | +As part of the University of North Carolina BIOL222 class, [Dr. Catherine Kehl](https://twitter.com/tylikcat) asked her students to "use `matplotlib.pyplot` to make art." BIOL222 is Introduction to Programming, aimed at students with no programming background. The emphasis is on practical, hands-on active learning. |
| 18 | + |
| 19 | +The students completed the assignment with festive enthusiasm around Halloween. Here are some great examples: |
| 20 | + |
| 21 | +Harris Davis showed an affinity for pumpkins, opting to go 3D! |
| 22 | + |
| 23 | +```python |
| 24 | +# get library for 3d plotting |
| 25 | +from mpl_toolkits.mplot3d import Axes3D |
| 26 | + |
| 27 | +# make a pumpkin :) |
| 28 | +rho = np.linspace(0, 3*np.pi,32) |
| 29 | +theta, phi = np.meshgrid(rho, rho) |
| 30 | +r, R = .5, .5 |
| 31 | +X = (R + r * np.cos(phi)) * np.cos(theta) |
| 32 | +Y = (R + r * np.cos(phi)) * np.sin(theta) |
| 33 | +Z = r * np.sin(phi) |
| 34 | + |
| 35 | +# make the stem |
| 36 | +theta1 = np.linspace(0,2*np.pi,90) |
| 37 | +r1 = np.linspace(0,3,50) |
| 38 | +T1, R1 = np.meshgrid(theta1, r1) |
| 39 | +X1 = R1 * .5*np.sin(T1) |
| 40 | +Y1 = R1 * .5*np.cos(T1) |
| 41 | +Z1 = -(np.sqrt(X1**2 + Y1**2) - .7) |
| 42 | +Z1[Z1 < .3] = np.nan |
| 43 | +Z1[Z1 > .7] = np.nan |
| 44 | + |
| 45 | +# Display the pumpkin & stem |
| 46 | +fig = plt.figure() |
| 47 | +ax = fig.gca(projection = '3d') |
| 48 | +ax.set_xlim3d(-1, 1) |
| 49 | +ax.set_ylim3d(-1, 1) |
| 50 | +ax.set_zlim3d(-1, 1) |
| 51 | +ax.plot_surface(X, Y, Z, color = 'tab:orange', rstride = 1, cstride = 1) |
| 52 | +ax.plot_surface(X1, Y1, Z1, color = 'tab:green', rstride = 1, cstride = 1) |
| 53 | +plt.show() |
| 54 | +``` |
| 55 | + |
| 56 | +Bryce Desantis stuck to the biological theme and demonstrated [fractal](https://en.wikipedia.org/wiki/Fractal) art. |
| 57 | + |
| 58 | +```python |
| 59 | +import numpy as np |
| 60 | +import matplotlib.pyplot as plt |
| 61 | + |
| 62 | +#Barnsley's Fern - Fractal; en.wikipedia.org/wiki/Barnsley_… |
| 63 | + |
| 64 | +#functions for each part of fern: |
| 65 | +#stem |
| 66 | +def stem(x,y): |
| 67 | + return (0, 0.16*y) |
| 68 | +#smaller leaflets |
| 69 | +def smallLeaf(x,y): |
| 70 | + return (0.85*x + 0.04*y, -0.04*x + 0.85*y + 1.6) |
| 71 | +#large left leaflets |
| 72 | +def leftLarge(x,y): |
| 73 | + return (0.2*x - 0.26*y, 0.23*x + 0.22*y + 1.6) |
| 74 | +#large right leftlets |
| 75 | +def rightLarge(x,y): |
| 76 | + return (-0.15*x + 0.28*y, 0.26*x + 0.24*y + 0.44) |
| 77 | +componentFunctions = [stem, smallLeaf, leftLarge, rightLarge] |
| 78 | + |
| 79 | +# number of data points and frequencies for parts of fern generated: |
| 80 | +#lists with all 75000 datapoints |
| 81 | +datapoints = 75000 |
| 82 | +x, y = 0, 0 |
| 83 | +datapointsX = [] |
| 84 | +datapointsY = [] |
| 85 | +#For 75,000 datapoints |
| 86 | +for n in range(datapoints): |
| 87 | + FrequencyFunction = np.random.choice(componentFunctions, p=[0.01, 0.85, 0.07, 0.07]) |
| 88 | + x, y = FrequencyFunction(x,y) |
| 89 | + datapointsX.append(x) |
| 90 | + datapointsY.append(y) |
| 91 | + |
| 92 | +#Scatter plot & scaled down to 0.1 to show more definition: |
| 93 | +plt.scatter(datapointsX,datapointsY,s=0.1, color='g') |
| 94 | +#Title of Figure |
| 95 | +plt.title("Barnsley's Fern - Assignment 3") |
| 96 | +#Changing background color |
| 97 | +ax = plt.axes() |
| 98 | +ax.set_facecolor("#d8d7bf") |
| 99 | +``` |
| 100 | + |
| 101 | +Grace Bell got a little trippy with this rotationally semetric art. It's pretty cool how she captured mouse events. It reminds us of a flower. What do you see? |
| 102 | + |
| 103 | +```python |
| 104 | +import matplotlib.pyplot as plt |
| 105 | +from matplotlib.tri import Triangulation |
| 106 | +from matplotlib.patches import Polygon |
| 107 | +import numpy as np |
| 108 | + |
| 109 | +#I found this sample code online and manipulated it to make the art piece! |
| 110 | +#was interested in because it combined what we used for functions as well as what we used for plotting with (x,y) |
| 111 | +def update_polygon(tri): |
| 112 | + if tri == -1: |
| 113 | + points = [0, 0, 0] |
| 114 | + else: |
| 115 | + points = triang.triangles[tri] |
| 116 | + xs = triang.x[points] |
| 117 | + ys = triang.y[points] |
| 118 | + polygon.set_xy(np.column_stack([xs, ys])) |
| 119 | + |
| 120 | +def on_mouse_move(event): |
| 121 | + if event.inaxes is None: |
| 122 | + tri = -1 |
| 123 | + else: |
| 124 | + tri = trifinder(event.xdata, event.ydata) |
| 125 | + update_polygon(tri) |
| 126 | + ax.set_title(f'In triangle {tri}') |
| 127 | + event.canvas.draw() |
| 128 | +#this is the info that creates the angles |
| 129 | +n_angles = 14 |
| 130 | +n_radii = 7 |
| 131 | +min_radius = 0.1 #the radius of the middle circle can move with this variable |
| 132 | +radii = np.linspace(min_radius, 0.95, n_radii) |
| 133 | +angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False) |
| 134 | +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) |
| 135 | +angles[:, 1::2] += np.pi / n_angles |
| 136 | +x = (radii*np.cos(angles)).flatten() |
| 137 | +y = (radii*np.sin(angles)).flatten() |
| 138 | +triang = Triangulation(x, y) |
| 139 | +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1), |
| 140 | + y[triang.triangles].mean(axis=1)) |
| 141 | + < min_radius) |
| 142 | + |
| 143 | +trifinder = triang.get_trifinder() |
| 144 | + |
| 145 | +fig, ax = plt.subplots(subplot_kw={'aspect': 'equal'}) |
| 146 | +ax.triplot(triang, 'y+-') #made the color of the plot yellow and there are "+" for the data points but you can't really see them because of the lines crossing |
| 147 | +polygon = Polygon([[0, 0], [0, 0]], facecolor='y') |
| 148 | +update_polygon(-1) |
| 149 | +ax.add_patch(polygon) |
| 150 | +fig.canvas.mpl_connect('motion_notify_event', on_mouse_move) |
| 151 | +plt.show() |
| 152 | +``` |
| 153 | + |
| 154 | +As a bonus, did you like that fox in the banner? That was created (and well documented) by Emily Foster! |
| 155 | +```python |
| 156 | +import numpy as np |
| 157 | +import matplotlib.pyplot as plt |
| 158 | + |
| 159 | +plt.axis('off') |
| 160 | + |
| 161 | +#head |
| 162 | +xhead = np.arange(-50,50,0.1) |
| 163 | +yhead = -0.007*(xhead*xhead) + 100 |
| 164 | + |
| 165 | +plt.plot(xhead, yhead, 'darkorange') |
| 166 | + |
| 167 | +#outer ears |
| 168 | +xearL = np.arange(-45.8,-9,0.1) |
| 169 | +yearL = -0.08*(xearL*xearL) -4*xearL + 70 |
| 170 | + |
| 171 | +xearR = np.arange(9,45.8,0.1) |
| 172 | +yearR = -0.08*(xearR*xearR) + 4*xearR + 70 |
| 173 | + |
| 174 | +plt.plot(xearL, yearL, 'black') |
| 175 | +plt.plot(xearR, yearR, 'black') |
| 176 | + |
| 177 | +#inner ears |
| 178 | +xinL = np.arange(-41.1,-13.7,0.1) |
| 179 | +yinL = -0.08*(xinL*xinL) -4*xinL + 59 |
| 180 | + |
| 181 | +xinR = np.arange(13.7,41.1,0.1) |
| 182 | +yinR = -0.08*(xinR*xinR) + 4*xinR + 59 |
| 183 | + |
| 184 | +plt.plot(xinL, yinL, 'salmon') |
| 185 | +plt.plot(xinR, yinR, 'salmon') |
| 186 | + |
| 187 | +# bottom of face |
| 188 | +xfaceL = np.arange(-49.6,-14,0.1) |
| 189 | +xfaceR = np.arange(14,49.3,0.1) |
| 190 | +xfaceM = np.arange(-14,14,0.1) |
| 191 | + |
| 192 | +plt.plot(xfaceL, abs(xfaceL), 'darkorange') |
| 193 | +plt.plot(xfaceR, abs(xfaceR), 'darkorange') |
| 194 | +plt.plot(xfaceM, abs(xfaceM), 'black') |
| 195 | + |
| 196 | +#nose |
| 197 | +xnose = np.arange(-14,14,0.1) |
| 198 | +ynose = -0.03*(xnose*xnose) + 20 |
| 199 | + |
| 200 | +plt.plot(xnose, ynose, 'black') |
| 201 | + |
| 202 | +#whiskers |
| 203 | +xwhiskR = [50, 70, 55, 70, 55, 70, 49.3] |
| 204 | +xwhiskL = [-50, -70, -55, -70, -55, -70, -49.3] |
| 205 | +ywhisk = [82.6, 85, 70, 65, 60, 45, 49.3] |
| 206 | + |
| 207 | +plt.plot(xwhiskR, ywhisk, 'darkorange') |
| 208 | +plt.plot(xwhiskL, ywhisk, 'darkorange') |
| 209 | + |
| 210 | +#eyes |
| 211 | +plt.plot(20,60, color = 'black', marker = 'o', markersize = 15) |
| 212 | +plt.plot(-20,60,color = 'black', marker = 'o', markersize = 15) |
| 213 | + |
| 214 | +plt.plot(22,62, color = 'white', marker = 'o', markersize = 6) |
| 215 | +plt.plot(-18,62,color = 'white', marker = 'o', markersize = 6) |
| 216 | +``` |
| 217 | + |
| 218 | +We look forward to seeing these students continue in their plotting and scientific adventures! |
0 commit comments