How to distribute points evenly on a circle in matplotlib? - python

I'm trying to create 10 points distributed on a circle, just like a watch but with 10 numbers instead of 12. and also be able to use the dots/points to plot lines between them.
This is the code I used to create a circle but can't figure out how to make them 10 points, and also how to use the points' coordinates in my code.
import matplotlib.pyplot as plt
import numpy as np
# T = testing
# myList = testing.digitcounter(18, 20)
def circle_points(r, n):
circles = []
for r, n in zip(r, n):
t = np.linspace(0, 2*np.pi, n)
x = r * np.cos(t)
y = r * np.sin(t)
circles.append(np.c_[x, y])
return circles
r = [0.15]
n = [15]
circles = circle_points(r, n)
fig, ax = plt.subplots()
for circle in circles:
ax.scatter(circle[:, 0], circle[:, 1])
ax.set_aspect('equal')
plt.show()

With t = np.linspace(0, 2*np.pi, 10, endpoint=False), you can create 10 angles equally distributed over a circle. The default endpoint=True would have the first and last angle coincide. To have the first point at the top, and going clockwise, interchange cos and sin in the formula.
To plot a continuous line between digits, you could use ax.plot(circle[digits, 0], circle[digits, 1]), with digits a numpy array of integers between 0 and 1. Note that this will contain a line of zero length when two subsequent digits would be equal.
import matplotlib.pyplot as plt
import numpy as np
def circle_points(r, n):
circles = []
for r, n in zip(r, n):
t = np.linspace(0, 2 * np.pi, n, endpoint=False)
x = r * np.sin(t)
y = r * np.cos(t)
circles.append(np.c_[x, y])
return circles
digits = np.random.randint(0, 10, 7) # 7 random digits between 0 and 9
circle10 = circle_points([0.15], [10])[0] # 10 points on a circle
circle10 = circle10[(np.arange(10) - 3) % 10, :] # with 3 at the top
fig, ax = plt.subplots()
ax.scatter(circle10[:, 0], circle10[:, 1], color='crimson')
ax.plot(circle10[digits, 0], circle10[digits, 1], color='dodgerblue', lw=3)
for i, (x, y) in enumerate(circle10):
ax.text(x * 1.1, y * 1.1, i, ha='center', va='center', color='crimson')
ax.set_aspect('equal')
ax.margins(x=0.1, y=0.1) # extra margins, because the text isn't taken into account for the default margins
ax.set_title("".join([f'{d}' for d in digits]), size=16)
ax.axis('off')
plt.show()
Similarly, a 17-pointed star could be drawn with:
N = 17
circle = circle_points([0.15], [N])[0]
for i in range(N):
ax.plot([circle[i, 0], circle[(i + 6) % N, 0]],
[circle[i, 1], circle[(i + 6) % N, 1]],
color='dodgerblue', lw=3)

Related

How to plot the equation for a semicircle

My half circle doesn't really look like how I expected.
Am I doing this right or am I missing something pretty big here ?
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
Use np.linspace to create an array for the x values. Use many points to create the circle
Use np.sqrt to solve for an array, instead of looping through each value.
import numpy as np
import matplotlib.pyplot as plt
# function for semicircle
def semicircle(r, h, k):
x0 = h - r # determine x start
x1 = h + r # determine x finish
x = np.linspace(x0, x1, 10000) # many points to solve for y
# use numpy for array solving of the semicircle equation
y = k + np.sqrt(r**2 - (x - h)**2)
return x, y
x, y = semicircle(6, 0, 0) # function call
plt.scatter(x, y, s=3, c='turquoise') # plot
plt.gca().set_aspect('equal', adjustable='box') # set the plot aspect to be equal
Answering my own question.
Plotting the output of the code:
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
[-5, 3.3166247903554]
[-4, 4.47213595499958]
[-3, 5.196152422706632]
[-2, 5.656854249492381]
[-1, 5.916079783099616]
[0, 6.0]
[1, 5.916079783099616]
[2, 5.656854249492381]
[3, 5.196152422706632]
[4, 4.47213595499958]
Looking at the output of the coordinates, it doesn't appear to be a circle.
But if we take our equation and our coordinates and graph them on this website we see that they are indeed a circle. It's an optical illusion that they aren't. Partially because the graph is not evenly displayed and also because plotting points in a range function with steps of 1 (line 13) doesn't plot points at equal arc length distances away from each other.

How to draw circles on the perimeter of a circle?

I'm trying to plot something like this:
I don't know how to find the center of smaller circles in for loops. First, I've tried to plot it with smaller number of circles(for example 2) but I don't know why the smaller circles are semi-circles??
My try:
import numpy as np
import matplotlib.pyplot as plt
r = 2, h = 1, k = 1
axlim = r + np.max((abs(h),np.max(abs(k))))
x = np.linspace(-axlim, axlim, 100)
X,Y = np.meshgrid(x,x)
F = (X-h)**2 + (Y-k)**2 - r**2
plt.contour(X,Y,F,0)
F1 = (X-(h+r))**2 + (Y-k)**2 - (r/3)**2
plt.contour(X,Y,F1,0)
F2 = (X-h)**2 + (Y-(k+r))**2 - (r/3)**2
plt.contour(X,Y,F2,0)
plt.gca().set_aspect('equal')
plt.axis([-4*r, 4*r, -4*r,4*r])
# plt.axis('off')
plt.show()
The output:
Sine, cosine and an angle evenly divided over the range 0, 2picould be used:
import numpy as np
import matplotlib.pyplot as plt
num_circ = 7
rad_large = 7
rad_small = 6
thetas = np.linspace(0, 2 * np.pi, num_circ, endpoint=False)
fig, ax = plt.subplots()
ax.add_patch(plt.Circle((0, 0), rad_large, fc='none', ec='navy'))
for theta in thetas:
ax.add_patch(plt.Circle((rad_large * np.cos(theta), rad_large * np.sin(theta),), rad_small, fc='none', ec='crimson'))
ax.autoscale_view() # calculate the limits for the x and y axis
ax.set_aspect('equal') # show circles as circles
plt.show()

python - Plotting implicit function f(x,y) = 0, where x,y undergo matrix multiplication

As the implicit function where 'A' is an n*2 matrix
0 = np.dot((x,y),A)
0 = xA11 yA12
0 = xA21 yA22
...
0 = xAn1 yAn2
Is it possible, via matplotlib or other means, to plot all the lines on the same plot without a large loop?
Given a n*2 matrix A, for each row i a line is defined by A[i,0]*x + A[i,1]*y == 0. This means 0,0 always lies on the line, as well as the point x=A[i,1],y=-A[i,0]. Multiplying with any value, e.g. by normalizing will again give points on the line.
The following code shows 3 ways to visualize these lines:
Some line segments cut by a circle, together with x=A[i,1],y=-A[i,0] and x=-A[i,1],y=A[i,0].
The same segments extended till the plot's border.
Just some end points on a circle.
import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import norm
from matplotlib.collections import LineCollection
n = 10
radius = 20
A = np.random.uniform(-10, 10, (n, 2))
B = A / norm(A, axis=1, keepdims=True) * radius # normalize and put on a circle with given radius
lines = np.dstack([B[:, 1], -B[:, 0], -B[:, 1], B[:, 0]]).reshape(-1, 2, 2)
fig, axes = plt.subplots(ncols=3, figsize=(14, 4))
for ax in axes:
ax.set_aspect('equal')
for ax in axes[:2]:
lc = LineCollection(lines, colors='blue', linewidths=2)
ax.add_collection(lc)
if ax == axes[0]:
ax.scatter(A[:, 1], -A[:, 0], color='crimson')
ax.scatter(-A[:, 1], A[:, 0], color='crimson')
elif ax == axes[1]:
ax.set_xlim(-radius / 2, radius / 2)
ax.set_ylim(-radius / 2, radius / 2)
for k in range(2):
axes[2].scatter(lines[:, k, 0], lines[:, k, 1], color='crimson')
axes[0].set_title('lines in circle and dots')
axes[1].set_title('lines till border')
axes[2].set_title('dots on circle')
plt.show()

Facecolor changing edgecolor in matplotlib

I am trying to remove the edge color in the plot of a cylinder where I have set an alpha and facecolors. However, if I also set the facecolors, I can still see the edge colors. If I remove the alpha = 0.5 statement then the problem is resolved, however I need the alpha to be <1 . Here is an example:
You can still see the blue edgecolors even tough I have set the edgecolor to None.
This is the code where I use plot_surface()
ax.plot_surface(X, Y,Z, edgecolor = "None", facecolors = col1, alpha = 0.5)
Yet the edge colors are still there? However, if I remove the facecolors statement inside plot_surface() then the edge colors are no longer there. Here is the complete code:
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.linalg import norm
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import random
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
origin = np.array([0, 0, 0])
#axis and radius
p0 = np.array([0, 0, 0])
p1 = np.array([8, 8, 8])
R = 4
#vector in direction of axis
v = p1 - p0
#find magnitude of vector
mag = norm(v)
#unit vector in direction of axis
v = v / mag
#make some vector not in the same direction as v
not_v = np.array([1, 0, 0])
if (v == not_v).all():
not_v = np.array([0, 1, 0])
#make vector perpendicular to v
n1 = np.cross(v, not_v)
#normalize n1
n1 /= norm(n1)
#make unit vector perpendicular to v and n1
n2 = np.cross(v, n1)
#surface ranges over t from 0 to length of axis and 0 to 2*pi
t = np.linspace(0, mag, 200)
theta = np.linspace(0, 2 * np.pi, 100)
#use meshgrid to make 2d arrays
t, theta = np.meshgrid(t, theta)
#generate coordinates for surface
X, Y, Z = [p0[i] + v[i] * t + R * np.sin(theta) * n1[i] + R * np.cos(theta) * n2[i] for i in [0, 1, 2]]
col1 = plt.cm.Blues(np.linspace(0,1,200)) # linear gradient along the t-axis
col1 = np.repeat(col1[np.newaxis,:, :], 100, axis=0) # expand over the theta- axis
ax.plot_surface(X, Y,Z, edgecolor = None, facecolors = col1, alpha = 0.5)
#plot axis
ax.plot(*zip(p0, p1), color = 'red')
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_zlim(0, 10)
plt.axis('off')
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
plt.show()
Setting linewidth=0 in plot_surface() solves this problem:
ax.plot_surface(X, Y, Z, edgecolor=None, facecolors=col1, alpha=0.5, linewidth=0)
p.s.: I didn't find this worth an answer, but per: Question with no answers, but issue solved in the comments (or extended in chat), I added it as a quick answer so the question can be marked as solved

Generator of evenly spaced points in a circle in python

I am tasked with generating evenly (more or less) spaced points on concentric rings of an invisible circle. The function should take a list of radii, and number of points to plot for a given radius as arguments. For example for a radius of 0 it should plot 1 point at (0,0). For a circle of radius of 1, it should plot 10 points along the circumference of the circle, spaced out by an angle of 2pi/10. For a circle of radius 2, 20 points along the circumference, spaced out by an angle of 2pi/20.
The generator should take the following parameters:
n, r_max, m
and should generate rings of coordinate pairs at radii
r_i = i*r_max/n for i = 0,1,..,n.
Each ring should have n*i points uniformly distributed in θ where
n_i=1 for i=0; n_i = mi for i>0
When the function is called like this:
for r, t in genpolar.rtuniform(n=10, rmax=0.1, m=6):
plot(r * cos(t), r * sin(t), 'bo')
it should return a plot that looks like:
Here is what I've come up with so far:
def rtpairs(R, N):
R=[0.0,0.1,0.2]
N=[1,10,20]
r=[]
t=[]
for i in N:
theta=2*np.pi/i
t.append(theta)
for j in R:
j=j
r.append(j)
plt.plot(r*np.cos(t),r*np.sin(t), 'bo')
plt.show()
but I'm pretty sure there is a more efficient method using two for loops.
Many thanks
I figured it out. The code goes like this:
import numpy as np
import matplotlib.pyplot as plt
T = [1, 10, 20, 30, 40, 50, 60]
R = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
def rtpairs(r, n):
for i in range(len(r)):
for j in range(n[i]):
yield r[i], j*(2 * np.pi / n[i])
for r, t in rtpairs(R, T):
plt.plot(r * np.cos(t), r * np.sin(t), 'bo')
plt.show()
Here is one way to do this.
import numpy as np
import matplotlib.pyplot as plt
def circle_points(r, n):
circles = []
for r, n in zip(r, n):
t = np.linspace(0, 2*np.pi, n, endpoint=False)
x = r * np.cos(t)
y = r * np.sin(t)
circles.append(np.c_[x, y])
return circles
When you pass this function in the appropriate lists, one with the radius for each circle and the other with the desired number of points it returns a list of coordinate arrays, one for each circle.
r = [0, 0.1, 0.2]
n = [1, 10, 20]
circles = circle_points(r, n)
These can be plotted as follows.
fig, ax = plt.subplots()
for circle in circles:
ax.scatter(circle[:, 0], circle[:, 1])
ax.set_aspect('equal')
plt.show()
Here we see the result for more circles.
n = [1, 10, 20, 30, 40, 50, 60]
r = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
circles = circle_points(r, n)
fig, ax = plt.subplots()
for circle in circles:
ax.scatter(circle[:, 0], circle[:, 1])
ax.set_aspect('equal')
plt.show()
Here is a way you could achieve this.
def circles(c_list: List[int]):
g_d_list = [] # graph data list
for g in c_list:
# create length of circle list. In this instance
# i'm multiplying by 8 each time but could be any number.
lg = [g] * (8*g)
ang = 360/len(lg) # calculate the angle of each entry in circle list.
ang_list = []
for i in range(len(lg)+1):
ang_list.append(ang*i)
for i, c in enumerate(lg):
# calculate the x and y axis points or each circle. in this instance
# i'm expanding circles by multiples of ten but could be any number.
x_axis = 0 + (10*g) * math.cos(math.radians(ang_list[i+1]))
y_axis = 0 + (10*g) * math.sin(math.radians(ang_list[i+1]))
# tuple structure ((axis tuple), circle size, circle colour)
g_d_list.append(((x_axis, y_axis), 1, 'r'))
fig, ax = plt.subplots()
for c in range(len(g_d_list)):
circle = plt.Circle(g_d_list[c][0], radius=g_d_list[c][1], fc=g_d_list[c][2])
ax.add_patch(circle)
plt.axis('scaled')
plt.axis('off') # optional if you don't want to show axis
plt.show()
It produces a graph when provided with a list containing the number of circles you require. For example circles([1,2,3]) returns.
I don't know python but this formula should help.
int ringNumber = 0
int n = ringNumber-1
((n/n+1)*60)-60 = degrees between points(except for ring zero, which the point is the center
You do have to cycle through the entire circle for all radii, so your plot call is pretty much stuck with some M*N process.
The code details can be slightly improved. For starters, your R list already holds the values you want; there's no need to construct a new list with the same values. You can build the t list with a straightforward list comprehension.
Is this what you wanted?
N=[1,10,20]
t = [2*np.pi/i for i in N]
for R in N:
plt.plot(R*np.cos(t),R*np.sin(t), 'bo')
plt.show()

Categories

Resources