I have coordinates of some points. My task is to get the direction of those points and find where future possible points will be located in the calculated direction. To do so, I have planned the following-
Fit a line to the points
Draw a quarter circle at the end of the fitted line. In common sense, the quarter circle might not be the right option to go for. However, it is a part of another problem and has to be solved this way.
I am using the following codes to fit a line
from matplotlib import pyplot as plt
from scipy import stats
x = [1,2,3,2,5,6,7,8,9,10]
y = [2,4,11,8,8,18,14,11,18,20]
slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
line = [slope*i+intercept for i in x]
plt.plot(x, line)
Suppose, the two points on the fitted line is (9,17) and (10,19). How can I draw a quarter circle at (10,19) with a radius of 5 in the direction of the line?
Ultimately, I will have a point location and I have to check whether the point falls inside the quarter circle or not, I assume which could be done with shapely.
Instead of calculating all the math by yourself, you can delegate it to Shapely.
First, create a circle at the end of the line with the help of buffer:
from shapely.affinity import rotate
from shapely.geometry import LineString, Point
from shapely.ops import split
a = (10, 20)
b = (15, 30)
ab = LineString([a, b]) # the line you got from linear regression
circle = Point(b).buffer(5)
Now, let's get two new lines that will delimit the area of the sector we want. We will do it by rotating the line using rotate to 135º at each direction, so that the sector's central angle will be 360º - 135º * 2 = 90º, which is a quarter of circle:
left_border = rotate(ab, -135, origin=b)
right_border = rotate(ab, 135, origin=b)
Finally, use split to get the sector:
splitter = LineString([*left_border.coords, *right_border.coords[::-1]])
sector = split(circle, splitter)[1]
From here you can easily find out if a point lies inside of the sector using contains method. For example:
points_of_interest = [Point(16, 32), Point(12, 30)]
for point in points_of_interest:
print(sector.contains(point))
# True
# False
To check whether point P falls inside the quarter circle, you can find distance from line end B (length of BP) and cosine of angle between unit line direction vector d and vector BP
distance = sqrt(BP.x * BP.x + BP.y * BP.y)
cosine = (d.x * BP.x + d.y * BP.y) / (distance)
if (distance < radius) and (cosine >= sqrt(2)/2)
P in sector
Unit vector d might be calculated from data you already have:
d.x = sign(slope) * sqrt(1/(1+slope**2))
d.y = sqrt(slope**2/1+slope**2)
Note that sign of components is not defined clearly (because two opposite vectors have the same slope)
To address the main question - end points of arc might be calculated using rotated (by Pi/4) direction vector
cf = sqrt(2)/2
arcbegin.x = b.x + radius * d.x * cf - radius * d.y * cf
arcbegin.y = b.y + radius * d.x * cf + radius * d.y * cf
arcend.x = b.x + radius * d.x * cf + radius * d.y * cf
arcend.y = b.y - radius * d.x * cf + radius * d.y * cf
I think You should implement the arch as follows. (I just shown the your missing logic, You haft to add your plot ). Good luck
from matplotlib import pyplot as plt
from scipy import stats
x = [1,2,3,2,5,6,7,8,9,10]
y = [2,4,11,8,8,18,14,11,18,20]
slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
line = [slope*i + intercept for i in x]
# Logic Part *****************************************************
from matplotlib.patches import Arc
import math
# circuile parameters
R = 5
xEnd,yEnd = 10 , 20 #Your end point cords, in your case Point B
LowerThita = math.degrees(math.atan(slope)) - 45
UpperThita = math.degrees(math.atan(slope)) + 45
# Figure setup
fig, ax = plt.subplots()
ax.set_xlim(-R , (R+xEnd) * 1.05)
ax.set_ylim(-R , (R+yEnd) * 1.05)
# Arcs
ax.add_patch(Arc((xEnd, yEnd), R, R,
theta1=LowerThita, theta2=UpperThita, edgecolor='k'))
plt.show()
#NOTE : You Haft to add your line to the plot
Related
I am plotting a vector field using the numpy function quiver() and it works. But I would like to emphasize the cowlick in the following plot:
I am not sure how to go about it, but increasing the density of arrows in the center could possibly do the trick. To do so, I would like to resort to some option within np.meshgrid() that would allow me to get more tightly packed x,y coordinate points in the center. A linear, quadratic or other specification does not seem to be built in. I am not sure if sparse can be modified to this end.
The code:
lim = 10
int = 0.22 *lim
x,y = np.meshgrid(np.arange(-lim, lim, int), np.arange(-lim, lim, int))
u = 3 * np.cos(np.arctan2(y,x)) - np.sqrt(x**2+y**2) * np.sin(np.arctan2(y,x))
v = 3 * np.sin(np.arctan2(y,x)) + np.sqrt(x**2+y**2) * np.cos(np.arctan2(y,x))
color = x**2 + y**2
plt.rcParams["image.cmap"] = "Greys_r"
mult = 1
plt.figure(figsize=(mult*lim, mult*lim))
plt.quiver(x,y,u,v,color, linewidths=.006, lw=.1)
plt.show()
Closing the loop on this, thanks to the accepted answer I was able to finally strike a balance between the density of the mesh as I learned from to do from #flwr and keeping the "cowlick" structure of the vector field conspicuous (avoiding the radial structure around the origin as much as possible):
You can construct the points whereever you want to calculate your field on and quivers will be happy about it. The code below uses polar coordinates and stretches the radial coordinate non-linearly.
import numpy as np
import matplotlib.pyplot as plt
lim = 10
N = 10
theta = np.linspace(0.1, 2*np.pi, N*2)
stretcher_factor = 2
r = np.linspace(0.3, lim**(1/stretcher_factor), N)**stretcher_factor
R, THETA = np.meshgrid(r, theta)
x = R * np.cos(THETA)
y = R * np.sin(THETA)
# x,y = np.meshgrid(x, y)
r = x**2 + y**2
u = 3 * np.cos(THETA) - np.sqrt(r) * np.sin(THETA)
v = 3 * np.sin(THETA) + np.sqrt(r) * np.cos(THETA)
plt.rcParams["image.cmap"] = "Greys_r"
mult = 1
plt.figure(figsize=(mult*lim, mult*lim))
plt.quiver(x,y,u,v,r, linewidths=.006, lw=.1)
Edit: Bug taking meshgrid twice
np.meshgrid just makes a grid of the vectors you provide.
What you could do is contract this regular grid in the center to have more points in the center (best visible with more points), e.g. like so:
# contract in the center
a = 0.5 # how far to contract
b = 0.8 # how strongly to contract
c = 1 - b*np.exp(-((x/lim)**2 + (y/lim)**2)/a**2)
x, y = c*x, c*y
plt.plot(x,y,'.k')
plt.show()
Alternatively you can x,y cooridnates that are not dependent on a grid at all:
x = np.random.randn(500)
y = np.random.randn(500)
plt.plot(x,y,'.k')
plt.show()
But I think you'd prefer a slightly more regular patterns you could look into poisson disk sampling with adaptive distances or something like that, but the key point here is that for using quiver, you can use ANY set of coordinates, they do not have to be in a regular grid.
I'm working on a Python-based data analysis. I have some x-y data points, and some ellipses, and I want to determine whether points are inside any of the ellipses. The way that I've been doing this works, but it's kludgy. As I think about distributing my software to other people, I find myself wanting a cleaner way.
Right now, I'm using matplotlib.patches.Ellipse objects. Matplotlib Ellipses have a useful method called contains_point(). You can work in data coordinates on a Matplotlib Axes object by calling Axes.transData.transform().
The catch is that I have to create a Figure and an Axes object to hold the Ellipses. And when my program runs, an annoying Matplotlib Figure object will get rendered, showing the Ellipses, which I don't actually need to see. I have tried several methods to suppress this output. I have succeeded in deleting the Ellipses from the Axes, using Axes.clear(), resulting in an empty graph. But I can't get Matplolib's pyplot.close(fig_number) to delete the Figure itself before calling pyplot.show().
Any advice is appreciated, thanks!
Inspired by how a carpenter draws an ellipse using two nails and a piece of string, here is a numpy-friendly implementation to test whether points lie inside given ellipses.
One of the definitions of an ellipse, is that the sum of the distances to the two foci is constant, equal to the width (or height if it would be larger) of the ellipse. The distance between the center and the foci is sqrt(a*a - b*b), where a and b are half of the width and height. Using that distance and rotation by the desired angle finds the locations of the foci. numpy.linalg.norm can be used to calculate the distances using numpy's efficient array operations.
After the calculations, a plot is generated to visually check whether everything went correct.
import numpy as np
from numpy.linalg import norm # calculate the length of a vector
x = np.random.uniform(0, 40, 20000)
y = np.random.uniform(0, 20, 20000)
xy = np.dstack((x, y))
el_cent = np.array([20, 10])
el_width = 28
el_height = 17
el_angle = 20
# distance between the center and the foci
foc_dist = np.sqrt(np.abs(el_height * el_height - el_width * el_width) / 4)
# vector from center to one of the foci
foc_vect = np.array([foc_dist * np.cos(el_angle * np.pi / 180), foc_dist * np.sin(el_angle * np.pi / 180)])
# the two foci
el_foc1 = el_cent + foc_vect
el_foc2 = el_cent - foc_vect
# for each x,y: calculate z as the sum of the distances to the foci;
# np.ravel is needed to change the array of arrays (of 1 element) into a single array
z = np.ravel(norm(xy - el_foc1, axis=-1) + norm(xy - el_foc2, axis=-1) )
# points are exactly on the ellipse when the sum of distances is equal to the width
# z = np.where(z <= max(el_width, el_height), 1, 0)
# now create a plot to check whether everything makes sense
from matplotlib import pyplot as plt
from matplotlib import patches as mpatches
fig, ax = plt.subplots()
# show the foci as red dots
plt.plot(*el_foc1, 'ro')
plt.plot(*el_foc2, 'ro')
# create a filter to separate the points inside the ellipse
filter = z <= max(el_width, el_height)
# draw all the points inside the ellipse with the plasma colormap
ax.scatter(x[filter], y[filter], s=5, c=z[filter], cmap='plasma')
# draw all the points outside with the cool colormap
ax.scatter(x[~filter], y[~filter], s=5, c=z[~filter], cmap='cool')
# add the original ellipse to verify that the boundaries match
ellipse = mpatches.Ellipse(xy=el_cent, width=el_width, height=el_height, angle=el_angle,
facecolor='None', edgecolor='black', linewidth=2,
transform=ax.transData)
ax.add_patch(ellipse)
ax.set_aspect('equal', 'box')
ax.autoscale(enable=True, axis='both', tight=True)
plt.show()
The simplest solution here is to use shapely.
If you have an array of shape Nx2 containing a set of vertices (xy) then it is trivial to construct the appropriate shapely.geometry.polygon object and check if an arbitrary point or set of points (points) is contained within -
import shapely.geometry as geom
ellipse = geom.Polygon(xy)
for p in points:
if ellipse.contains(geom.Point(p)):
# ...
Alternatively, if the ellipses are defined by their parameters (i.e. rotation angle, semimajor and semiminor axis) then the array containing the vertices must be constructed and then the same process applied. I would recommend using the polar form relative to center as this is the most compatible with how shapely constructs the polygons.
import shapely.geometry as geom
from shapely import affinity
n = 360
a = 2
b = 1
angle = 45
theta = np.linspace(0, np.pi*2, n)
r = a * b / np.sqrt((b * np.cos(theta))**2 + (a * np.sin(theta))**2)
xy = np.stack([r * np.cos(theta), r * np.sin(theta)], 1)
ellipse = affinity.rotate(geom.Polygon(xy), angle, 'center')
for p in points:
if ellipse.contains(geom.Point(p)):
# ...
This method is advantageous because it supports any properly defined polygons - not just ellipses, it doesn't rely on matplotlib methods to perform the containment checking, and it produces a very readable code (which is often important when "distributing [one's] software to other people").
Here is a complete example (with added plotting to show it working)
import shapely.geometry as geom
from shapely import affinity
import matplotlib.pyplot as plt
import numpy as np
n = 360
theta = np.linspace(0, np.pi*2, n)
a = 2
b = 1
angle = 45.0
r = a * b / np.sqrt((b * np.cos(theta))**2 + (a * np.sin(theta))**2)
xy = np.stack([r * np.cos(theta), r * np.sin(theta)], 1)
ellipse = affinity.rotate(geom.Polygon(xy), angle, 'center')
x, y = ellipse.exterior.xy
# Create a Nx2 array of points at grid coordinates throughout
# the ellipse extent
rnd = np.array([[i,j] for i in np.linspace(min(x),max(x),50)
for j in np.linspace(min(y),max(y),50)])
# Filter for points which are contained in the ellipse
res = np.array([p for p in rnd if ellipse.contains(geom.Point(p))])
plt.plot(x, y, lw = 1, color='k')
plt.scatter(rnd[:,0], rnd[:,1], s = 50, color=(0.68, 0.78, 0.91)
plt.scatter(res[:,0], res[:,1], s = 15, color=(0.12, 0.67, 0.71))
plt.show()
I'm using streamplot in order to plot stress trajectories around an open circle. I do not want the stress trajectories to be analyzed inside the radius of the circle for two reasons: (1) The stresses will not propagate through the air as they would through the medium surrounding the hole, and (2) The math doesn't allow for it. I have been messing around with the idea of a mask but I haven't been able to get it to work. There might be a better way. Does anyone know how I can plot these trajectories without them plotting inside the radius of the hole? I effectively need some sort of command to tell the streamplot to stop whenever it gets to the outer radius of the hole, but then also know where to pick back up again. The first bit of code below is just the math used to derive the directions of the stress trajectories. I included this for reference. Following this I plot the trajectories.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
def stress_trajectory_cartesian(X,Y,chi,F,a):
# r is the radius out from the center of the hole at which we want to know the stress
# Theta is the angle from reference at which we want to know the stress
# a is the radius of the hole
r = np.sqrt(np.power(X,2)+np.power(Y,2))*1.0
c = (1.0*a)/(1.0*r)
theta = np.arctan2(Y,X)
A = 0.5*(1 - c**2. + (1 - 4*c**2. + 3*c**4.)*np.cos(2*theta))
B = 0.5*(1 - c**2. - (1 - 4*c**2. + 3*c**4.)*np.cos(2*theta))
C = 0.5*(1 + c**2. - (1 + 3*c**4.)*np.cos(2*theta))
D = 0.5*(1 + c**2. + (1+ 3*c**4.)*np.cos(2*theta))
E = 0.5*((1 + 2*c**2. - 3*c**4.)*np.sin(2*theta))
tau_r = 1.0*F*c**2. + (A-1.0*chi*B) # Radial stress
tau_theta = -1.*F*c**2. + (C - 1.0*chi*D) # Tangential stress
tau_r_theta = (-1 - 1.0*chi)*E # Shear stress
tau_xx = .5*tau_r*(np.cos(2*theta)+1) -1.0*tau_r_theta*np.sin(2*theta) + .5*(1-np.cos(2*theta))*tau_theta
tau_xy = .5*np.sin(2*theta)*(tau_r - tau_theta) + 1.0*tau_r_theta*np.cos(2*theta)
tau_yy = .5*(1-np.cos(2*theta))*tau_r + 1.0*tau_r_theta*np.sin(2*theta) + .5*(np.cos(2*theta)+1)*tau_theta
tan_2B = (2.*tau_xy)/(1.0*tau_xx - 1.0*tau_yy)
beta1 = .5*np.arctan(tan_2B)
beta2 = .5*np.arctan(tan_2B) + np.pi/2.
return beta1, beta2
# Functions to plot beta as a vector field in the Cartesian plane
def stress_beta1_cartesian(X,Y,chi,F,a):
return stress_trajectory_cartesian(X,Y,chi,F,a)[0]
def stress_beta2_cartesian(X,Y,chi,F,a):
return stress_trajectory_cartesian(X,Y,chi,F,a)[1]
#Used to return the directions of the betas
def to_unit_vector_x(angle):
return np.cos(angle)
def to_unit_vector_y(angle):
return np.sin(angle)
The code below plots the stress trajectories:
# Note that R_min is taken as the radius of the hole here
# Using R_min for a in these functions under the assumption that we don't want to analyze stresses across the hole
def plot_stresses_cartesian(F,chi,R_min):
Y_grid, X_grid = np.mgrid[-5:5:100j, -5:5:100j]
R_grid = np.sqrt(X_grid**2. + Y_grid**2.)
cart_betas1 = stress_beta1_cartesian(X_grid,Y_grid,chi,F,R_min)
beta_X1s = to_unit_vector_x(cart_betas1)
beta_Y1s = to_unit_vector_y(cart_betas1)
beta_X1s[R_grid<1] = np.nan
beta_Y1s[R_grid<1] = np.nan
cart_betas2 = stress_beta2_cartesian(X_grid,Y_grid,chi,F,R_min)
beta_X2s = to_unit_vector_x(cart_betas2)
beta_Y2s = to_unit_vector_y(cart_betas2)
beta_X2s[R_grid<1] = np.nan
beta_Y2s[R_grid<1] = np.nan
fig = plt.figure(figsize=(5,5))
#streamplot
ax=fig.add_subplot(111)
ax.set_title('Stress Trajectories')
plt.streamplot(X_grid, Y_grid, beta_X1s, beta_Y1s, minlength=0.9, arrowstyle='-', density=2.5, color='b')
plt.streamplot(X_grid, Y_grid, beta_X2s, beta_Y2s, minlength=0.9, arrowstyle='-', density=2.5, color='r')
plt.axis("image")
plt.xlabel(r'$\chi = $'+str(round(chi,1)) + ', ' + r'$F = $'+ str(round(F,1)))
plt.ylim(-5,5)
plt.xlim(-5,5)
plt.show()
plot_stresses_cartesian(0,1,1)
I think that you just need to have NaN values for the region that you do not want to consider. I generated a simple example below.
import numpy as np
import matplotlib.pyplot as plt
Y, X = np.mgrid[-5:5:100j, -5:5:100j]
R = np.sqrt(X**2 + Y**2)
U = -1 - X**2 + Y
V = 1 + X - Y**2
U[R<1] = np.nan
V[R<1] = np.nan
plt.streamplot(X, Y, U, V, density=2.5, arrowstyle='-')
plt.axis("image")
plt.savefig("stream.png", dpi=300)
plt.show()
With plot
I would like to represent the elliptical orbit of a binary system of two stars. What I aim to, is something like this:
Where I have a grid of the sizes along the axes, an in-scale star at the focus, and the orbit of the secondary star. The decimal numbers along the orbit are the orbital phases. The arrow at the bottom is the Earth direction, and the thick part at the orbit is related to the observation for that specific case - I don't need it. What I want to change from this plot is:
Orbital phase: instead of numbers along the orbit, I would like "dashed rays" from the focus to the orbit, and the orbital phase above them:
I don't want the cross along (0, 0);
I would like to re-orient the orbit, in order that the 0.0 phase is around the top left part of the plot, and the Earth direction is an upward pointing straight arrow (the parameters of my system are different from the one plotted here).
I tried to look for python examples, but the only thing I came out with (from here), is a polar plot:
which is not really representative of what I want, but still is a beginning:
import numpy as np
import matplotlib.pyplot as plt
cos = np.cos
pi = np.pi
a = 10
e = 0.1
theta = np.linspace(0,2*pi, 360)
r = (a*(1-e**2))/(1+e*cos(theta))
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
ax.set_yticklabels([])
ax.plot(theta,r)
print(np.c_[r,theta])
plt.show()
Here's something that gets you very close. You do not need polar coordinates to plot a decent ellipse. There is a so-called artist you can readily utilize.
You probably have to customize the axis labels and maybe insert an arrow or two if you want:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
# initializing the figure:
fig = plt.figure()
# the (carthesian) axis:
ax = fig.add_subplot(111,aspect='equal')
ax.grid(True)
# parameters of the ellipse:
a = 5.0
e = 4.0
b = np.sqrt(a**2.0 - e**2.0)
# the center of the ellipse:
x = 6.0
y = 6.0
# the angle by which the ellipse is rotated:
angle = -45.0
#angle = 0.0
# plotting the ellipse, using an artist:
ax.add_artist(Ellipse(xy=[x,y], width=2.0*a, height=2.0*b, \
angle=angle, facecolor='none'))
ax.set_xlim(0,2.0*x)
ax.set_ylim(0,2.0*y)
# marking the focus (actually, both)
# and accounting for the rotation of the ellipse by angle
xf = [x - e*np.cos(angle * np.pi/180.0),
x + e*np.cos(angle * np.pi/180.0)]
yf = [y - e*np.sin(angle * np.pi/180.0),
y + e*np.sin(angle * np.pi/180.0)]
ax.plot(xf,yf,'xr')
# plotting lines from the focus to the ellipse:
# these should be your "rays"
t = np.arange(np.pi,3.0*np.pi,np.pi/5.0)
p = b**2.0 / a
E = e / a
r = [p/(1-E*np.cos(ti)) for ti in t]
# converting the radius based on the focus
# into x,y coordinates on the ellipse:
xr = [ri*np.cos(ti) for ri,ti in zip(r,t)]
yr = [ri*np.sin(ti) for ri,ti in zip(r,t)]
# accounting for the rotation by anlge:
xrp = [xi*np.cos(angle * np.pi/180.0) - \
yi*np.sin(angle * np.pi/180.0) for xi,yi in zip(xr,yr)]
yrp = [xi*np.sin(angle * np.pi/180.0) + \
yi*np.cos(angle * np.pi/180.0) for xi,yi in zip(xr,yr)]
for q in range(0,len(t)):
ax.plot([xf[0], xf[0]+xrp[q]],[yf[0], yf[0]+yrp[q]],'--b')
# put labels outside the "rays"
offset = 0.75
rLabel = [ri+offset for ri in r]
xrl = [ri*np.cos(ti) for ri,ti in zip(rLabel,t)]
yrl = [ri*np.sin(ti) for ri,ti in zip(rLabel,t)]
xrpl = [xi*np.cos(angle * np.pi/180.0) - \
yi*np.sin(angle * np.pi/180.0) for xi,yi in zip(xrl,yrl)]
yrpl = [xi*np.sin(angle * np.pi/180.0) + \
yi*np.cos(angle * np.pi/180.0) for xi,yi in zip(xrl,yrl)]
# for fancy label rotation reduce the range of the angle t:
tlabel = [(ti -np.pi)*180.0/np.pi for ti in t]
for q in range(0,len(tlabel)):
if tlabel[q] >= 180.0:
tlabel[q] -= 180.0
# convert the angle t from radians into degrees:
tl = [(ti-np.pi)*180.0/np.pi for ti in t]
for q in range(0,len(t)):
rotate_label = angle + tlabel[q]
label_text = '%.1f' % tl[q]
ax.text(xf[0]+xrpl[q],yf[0]+yrpl[q],label_text,\
va='center', ha='center',rotation=rotate_label)
plt.show()
The example above will result in this figure:
Explanations:
You can use an artist to plot the ellipse, instead of using polar coordinates
The nomenclature is based on the definitions available on Wikipedia
The angle in the artist setup rotates the ellipse. This angle is later used to rotate coordinates for the rays and labels (this is just math)
The rays are derived from the polar form of the ellipse relative to a focus.
The angle t runs from pi to 3.0*pi because I assumed that this would correspond to your idea of where the rays should start. You get the same rays for 0 to 2.0*pi. I used np.arange instead of linspace because I wanted a defined increment (pi/5.0, or 36 degrees) in this example.
The labels at the end of the rays are placed as text, the variable offset controls the distance between the ellipse and the labels. Adjust this as needed.
For the alignment of the label text orientation with the rays, I reduced the angle, t, to the range 0 to 180 degrees. This makes for better readability compared to the full range of 0 to 360 degrees.
For the label text I just used the angle, t, for simplicity. Replace this with whatever information better suits your purpose.
The angle, t, was converted from radians to degrees before the loop that places the label. Inside the loop, each element of tl is converted to a string. This allows for more formatting control (e.g. %.3f if you needed 3 decimals)
SO over break week our teacher gave us a little project to do requiring a Spirograph, here is the code he helped us write before
from graphics import *
from math import *
def ar(a):
return a*3.141592654/180
def main():
x0 = 100
y0 = 100
startangle = 60
stepangle = 120
radius = 50
win = GraphWin()
p1 = Point(x0 + radius * cos(ar(startangle)), y0 + radius * sin(ar(startangle)))
for i in range(stepangle+startangle,360+stepangle+startangle,stepangle):
p2 = Point(x0 + radius * cos(ar(i)), y0 + radius * sin(ar(i)))
Line(p1,p2).draw(win)
p1 = p2
input("<ENTER> to quit...")
win.close()
main()
he then wants us to develop the program that consecutively draws 12 equilateral triangles (rotating the triangle each time by 30 degrees through a full 360 circle). This can be achieved by “stepping” the STARTANGLE parameter. My question I am stuck on where to go from here, what does he mean by "stepping?" I assume making some sort of loop, is it possible someone can give me a push in the right step?
This is a solution using matplotlib. The general procedure would be the same. But you will have to modify it for using the libraries you're allowed to use.
from math import radians, sin, cos
import matplotlib.pyplot as plt
startAngle = 0
stepAngle = 30
origin = (0,0)
points = []
points.append(origin)
points.append((cos(radians(startAngle)), sin(radians(startAngle))))
for i in range(startAngle + stepAngle, 360 + stepAngle, stepAngle):
x = cos(radians(i))
y = sin(radians(i))
points.append((x,y))
points.append(origin)
points.append((x,y))
x,y = zip(*points) #separate the tupples into x and y coordinates.
plt.plot(x,y) #plots the points, drawing lines between each point
plt.show()
plt.plot draw lines between each point in the list. We're adding inn the origin points so we get triangles instead of just a polygon around the center.