matplotlib : How can I draw a circle with random x deformations? - python

I need to draw a circle that's not perfect, I mean at some points on the circle the radius needs to change (be greater or lower) in order to cause the desired deformation.
This image for instance shows a circle with 1 single deformation:
The number of the deformations is random and the positions also.
I am using the code below to draw a normal circle :
import numpy as np
import matplotlib.pyplot as plt
theta = np.linspace(0, 2*np.pi, 200)
radius = 0.4
a = radius * np.cos(theta)
b = radius * np.sin(theta)
figure, axes = plt.subplots(1)
axes.plot(a, b)
axes.set_aspect(1)
plt.show()
Do you have any ideas how can I achieve this?

Making the radius depend on theta, we could have a function f such that:
When further than 2 steps away from an angle theta1: f(t) = 1
At 1 and 2 steps away f(1) = f(2) = 1
At theta1 there is a deformation such that f(0) = k, for some k
At 0 and 2, the tangent to the deformation should be zero
For negative t, we can use f on the absolute value
If f would be a polynomial, it could be of degree 4, so f = a*t**4 + b*t**3 + c*t**2 + d*t + e. The symbolic math library, sympy, can find suitable values for these parameters with the given constraints.
from sympy import Eq, solve
from sympy.abc import a, b, c, d, e, t, k
f = a * t ** 4 + b * t ** 3 + c * t ** 2 + d * t + e
eq1 = Eq(f.subs(t, 0), k)
eq2 = Eq(f.subs(t, 1), 1)
eq3 = Eq(f.subs(t, 2), 1)
eq4 = Eq(f.diff(t).subs(t, 0), 0)
eq5 = Eq(f.diff(t).subs(t, 2), 0)
sol = solve([eq1, eq2, eq3, eq4, eq5], (a, b, c, d, e))
This generates (after some rewriting), the following expression for f:
k + (2 * t ** 2 - 9 * t + 11) * t ** 2 * (1 - k) / 4
Now, use this function to draw the deformed circle:
import matplotlib.pyplot as plt
import numpy as np
theta = np.linspace(0, 2 * np.pi)
k = 0.8
theta1 = 80 * np.pi / 180 # a deformation at theta 80 degrees
alpha = 36 * np.pi / 180 # have a special point every 36 degrees (10 on the circle)
th = theta - theta1 # the difference between the angles, still needs to be careful to make this difference symmetrical to zero
t = np.abs(np.where(th < np.pi, th, th - 2 * np.pi)) / alpha # use absolute value and let alpha represent a step of 1
r = np.where(t > 2, 1, k + (2 * t ** 2 - 9 * t + 11) * t ** 2 * (1 - k) / 4) # the deformed radius
plt.plot(np.cos(theta), np.sin(theta), ':r')
plt.plot(r * np.cos(theta), r * np.sin(theta), '-b')
plt.fill(r * np.cos(theta), r * np.sin(theta), color='blue', alpha=0.2)
for i in range(-5, 5):
plt.plot(np.cos(theta1 + i * alpha), np.sin(theta1 + i * alpha), 'xk')
plt.axis('equal')
plt.show()

Related

Draw a circle with a specified tilt angle in three-dimensional space with Python

I want to draw a circle with a specified angle of inclination in 3D space using Python. Similar to the image below:
Image
I can already draw circles in 2D. I modified my program by referring to the link below:
Masking a 3D numpy array with a tilted disc
import numpy as np
import matplotlib.pyplot as plt
r = 5.0
a, b, c = (0.0, 0.0, 0.0)
angle = np.pi / 6 # "tilt" of the circle
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_xlim(-10,10)
ax.set_ylim(-10,10)
ax.set_zlim(-10,10)
phirange = np.linspace(0, 2 * np.pi, 300) #to make a full circle
x = a + r * np.cos(phirange)
y = b + r * np.sin(phirange)
z= c
ax.plot(x, y, z )
plt.show()
Now I can draw the circle in 3D space, but I can't get the circle to tilt at the angle I want.
I tried to modify the code in the Z part, the circle can be tilted, but not the result I want.
z = c + r * np.cos(phirange) * np.sin(angle)
Result image:
Do the X and Y parts also need to be modified? What should I do?
update: the circle tilt with other axis
Let i = (1, 0, 0), j = (0, 1, 0). Those are the direction vectors of the x-axis and y-axis, respectively. Those two vectors form an orthonormal basis of the horizontal plane. Here "orthonormal" means the two vectors are orthogonal and both have length 1.
A circle on the horizontal plane with centre C and radius r consists in all points that can be written as C + r * (cos(theta) * i + sin(theta) * j), for all values of theta in range [0, 2 pi]. Note that this works with i and j, but it would have worked equally with any other orthonormal basis of the horizontal plane.
A circle in any other plane can be described exactly the same way, by replacing i and j with two vectors that form an orthonormal basis of that plane.
According to your image, the "tilted plane at angle tilt" has the following orthonormal basis:
a = (cos(tilt), 0, sin(tilt))
b = (0, 1, 0)
You can check that these are two vectors in your plane, that they are orthogonal and that they both have norm 1. Thus they are indeed an orthonormal basis of your plane.
Therefore a circle in your plane, with centre C and radius r, can be described as all the points C + r * (cos(theta) * a + sin(theta) * b), where theta is in range [0, 2 pi].
In terms of x,y,z, this translates into the following system of three parametric equations:
x = x_C + r * cos(theta) * x_a + r * sin(theta) * x_b
y = y_C + r * cos(theta) * y_a + r * sin(theta) * y_b
z = z_C + r * cos(theta) * z_a + r * sin(theta) * z_b
This simplifies a lot, because x_b, y_a, z_b are all equal to 0:
x = x_C + r * cos(theta) * x_a # + sin(theta) * x_b, but x_b == 0
y = y_C + r * sin(theta) * y_b # + cos(theta) * y_a, but y_a == 0
z = z_C + r * cos(theta) * z_a # + sin(theta) * z_b, but z_b == 0
Replacing x_a, y_b and z_a by their values:
x = x_C + r * cos(tilt) * cos(theta)
y = y_C + r * sin(theta)
z = z_C + r * sin(tilt) * cos(theta)
In python:
import numpy as np
import matplotlib.pyplot as plt
# parameters of circle
r = 5.0 # radius
x_C, y_C, z_C = (0.0, 0.0, 0.0) # centre
tilt = np.pi / 6 # tilt of plane around y-axis
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_xlim(-10,10)
ax.set_ylim(-10,10)
ax.set_zlim(-10,10)
theta = np.linspace(0, 2 * np.pi, 300) #to make a full circle
x = x_C + r * np.cos(tilt) * np.cos(theta)
y = y_C + r * np.sin(theta)
z = z_C + r * np.sin(tilt) * np.cos(theta)
ax.plot(x, y, z )
plt.show()

I would like to make a colormap from a matrix, with making difference, which way we have got the negative values

We have two terms, two parameters (h, h0) and a variable T. We can define a certain value of T, where the difference of the two terms divided by L is equal to zero.
With this T value, we can calculate g0, gc and gs operators, and then witness1 and witness2.
Then we make this whole calculation with running parameters (from 0.03 to 3): h0=0.03, h=0.03; h0=0.06, h=0.03; ...; h0=3, h=0.03; h0=0.03, h=0.06; ...; h0=3, h=3
Then we select the witness with the lower value, and put it to the proper place in the matrix.
The values can be both positive and negative. We are interested in where will be the result negative, and from which witness we got the result. I have plotted the results from the matrix, the negative values with yellow, and the positive with white. Now I would like to plot the negative numbers from witness1 with blue, and the negative numbers from witness2 with yellow. Then I would like to norm the graph from 0-100 to 0-3.
How can I make this?
import math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
def g0operator(T, gamma, h, L):
k = -math.pi
d=2*math.pi/L
g0=0
while k <= math.pi:
g0 = g0 + (((math.tanh(math.sqrt(((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2) / T))) * (
1 / (math.sqrt(
((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2))) * ((h - math.cos(k))))
k = k + d
g0 = g0 / L
return g0
def gsoperator(T, gamma, h, L):
gs=0
k = -math.pi
d=2*math.pi/L
while k <= math.pi:
gs = gs + ((math.tanh(math.sqrt(((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2) / T)) * (1 /
(math.sqrt(((gamma * math.sin(k)) ** 2) + (h - math.cos(k)) ** 2))) * (
((math.sin(k)) ** 2)))
k = k + d
gs = -(gs / L) * gamma
return gs
def gcoperator(T, gamma, h, L):
d=2*math.pi/L
gc=0
k = -math.pi
while k <= math.pi:
gc = gc + ((math.tanh(np.sqrt(((gamma * np.sin(k)) ** 2) + (h - np.cos(k)) ** 2) / T)) * (1 /
np.sqrt(((gamma * np.sin(k)) ** 2) + (h - np.cos(k)) ** 2))) * (np.cos(k) * ((np.cos(k) - h)))
k = k + d
gc = gc / L
return gc
def first_term(gamma, h, T, L):
c=-math.pi
d=2*math.pi/L
first=0
while c <= math.pi:
first = first + (math.tanh(math.sqrt(((gamma * math.sin(c)) ** 2) + (h - math.cos(c)) ** 2) / T) * math.sqrt(
((gamma * math.sin(c))
** 2) + (h - math.cos(c)) ** 2))
c=c+d
return first
def second_term(h, h0, gamma, L):
second=0
c=-math.pi
d=2*math.pi/L
while c<=math.pi:
second = second + abs(((math.cos(c) - h0) * (math.cos(c) - h) + ((gamma * math.sin(c)) ** 2)) / (
math.sqrt(((gamma * math.sin(c)) ** 2) + (h0 - math.cos(c)) ** 2)))
c = c + d
return second
def witness1(gs, g0, gc):
W = (-1 / 4) * (2 * abs(gs) + ((g0) ** 2) - ((gc) ** 2) + ((gs) ** 2) - 1)
return W
def witness2(gs, g0, gc):
w=-(1/4)*(-(g0**2)+(gc**2)-(gs**2)-1+2*math.sqrt((g0**2)+(gc**2)))
return w
h=0.03
h0=0.03
T=2.5
gamma=0.6
K=2.5
L=100000
matrix=np.zeros((100,100))
for i in range(100):
for b in range(100):
K = 2.5
T = 2.5
result=1
while abs(result) > 10 ** (-12):
c = -math.pi
first = first_term(gamma, h, T, L)
second = second_term(h, h0, gamma, L)
result = (first - second) / L
if result > 0:
T = T + K / 2
elif result < 0:
T = T - K / 2
K = K / 2
gc = gcoperator(T, gamma, h, L)
gs = gsoperator(T, gamma, h, L)
g0 = g0operator(T, gamma, h, L)
W = witness1(gs, g0, gc)
w = witness2(gs, g0, gc)
if W < w:
matrix[i, b] = W
else:
matrix[i, b] = w
h0=h0+0.03
h=h+0.03
h0=0.03
np.savetxt('matrixgamma=0.6.txt', matrix)
a=np.loadtxt('matrixgamma=0.6.txt')
cmap=colors.ListedColormap(['yellow', 'white'])
bounds=[-2, 0, 2]
norm=colors.BoundaryNorm(bounds, cmap.N)
img=plt.imshow(a, interpolation='nearest', origin='lower', cmap=cmap, norm=norm)
plt.colorbar(img, cmap=cmap, norm=norm, boundaries=bounds)
plt.show()
I'm sorry but I have simplified a little bit your witnessN functions, but I hope that you can adapt my solution, in which the key points are
to save the values for the grid evaluations of witnessN
to use Numpy's logical functions
to use the fact that Booleans can be used as 0 or 1 integers,
With respect to plotting, you can omit the plt.contourf statement, but I like the additional lines subdividing the fields.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import BoundaryNorm, ListedColormap
witness1 = lambda X, Y: np.sin(X-Y/2)
witness2 = lambda X, Y: np.cos(X/2+2*Y)
x = np.linspace(-6, 6, 241)
X, Y = np.meshgrid(x, x)
W = witness1(X, Y)
w = witness2(X, Y)
Wp, wp, Wgtw = W>0, w>0, W>w
values = np.where(Wgtw, W, w)
colors = np.where(np.logical_and(1-Wgtw, 1-wp), 0, 0)
colors += np.where(np.logical_and( Wgtw, 1-Wp), 1, 0)
colors += np.where(np.logical_and( Wgtw, Wp), 2, 0)
colors += np.where(np.logical_and(1-Wgtw, wp), 3, 0)
# change colors to your taste
# w<0, W<0, W>0, w>0
cm = ListedColormap([ 'y', 'b', 'r', 'g'])
norm=BoundaryNorm([-0.5, 0.5, 1.5, 2.5, 3.5], 4)
pc = plt.pcolor(X, Y, colors, norm=norm, cmap=cm)
ct = plt.contour(X, Y, values, [-.9, -0.1, 0.1, 0.9], colors='k')
lb = plt.clabel(ct)
cf = plt.contourf(X, Y, colors, norm=norm, cmap=cm, alpha=0.6)
cb = plt.colorbar(pc)
cb.set_ticks([0, 1, 2, 3])
cb.set_ticklabels(['w<0', 'W<0', 'W>0', 'w>0'])
plt.show()

Scipy odeint. Gravitational motion

I solve the motion in gravitational field around the sun with scipy and mathplotlib and have a problem. My solution is not correct. It is not like in example. Formulas that I used.
from scipy.integrate import odeint
import scipy.constants as constants
import numpy as np
import matplotlib.pyplot as plt
M = 1.989 * (10 ** 30)
G = constants.G
alpha = 30
alpha0 = (alpha / 180) * np.pi
v00 = 0.7
v0 = v00 * 1000
def dqdt(q, t):
x = q[0]
y = q[2]
ax = - G * M * (x / ((x ** 2 + y ** 2) ** 1.5))
ay = - G * M * (y / ((x ** 2 + y ** 2) ** 1.5))
return [q[1], ax, q[3], ay]
vx0 = v0 * np.cos(alpha0)
vy0 = v0 * np.sin(alpha0)
x0 = -150 * (10 ** 11)
y0 = 0 * (10 ** 11)
q0 = [x0, vx0, y0, vy0]
N = 1000000
t = np.linspace(0.0, 100000000000.0, N)
pos = odeint(dqdt, q0, t)
x1 = pos[:, 0]
y1 = pos[:, 2]
plt.plot(x1, y1, 0, 0, 'ro')
plt.ylabel('y')
plt.xlabel('x')
plt.grid(True)
plt.show()
How can I fix this?
Maybe you can tell me solution with another method for example with Euler's formula or with using other library.
I will be very greatful if you help me.

Using functions to create Electric Field array for 2d Density Plot and 3d Surface Plot

Below is my code, I'm supposed to use the electric field equation and the given variables to create a density plot and surface plot of the equation. I'm getting "invalid dimensions for image data" probably because the function E takes multiple variables and is trying to display them all as multiple dimensions. I know the issue is that I have to turn E into an array so that the density plot can be displayed, but I cannot figure out how to do so. Please help.
import numpy as np
from numpy import array,empty,linspace,exp,cos,sqrt,pi
import matplotlib.pyplot as plt
lam = 500 #Nanometers
x = linspace(-10*lam,10*lam,10)
z = linspace(-20*lam,20*lam,10)
w0 = lam
E0 = 5
def E(E0,w0,x,z,lam):
E = np.zeros((len(x),len(z)))
for i in z:
for j in x:
E = ((E0 * w0) / w(z,w0,zR(w0,lam)))
E = E * exp((-r(x)**2) / (w(z,w0,zR(w0,lam)))**2)
E = E * cos((2 * pi / lam) * (z + (r(x)**2 / (2 * Rz(z,zR,lam)))))
return E
def r(x):
r = sqrt(x**2)
return r
def w(z,w0,lam):
w = w0 * sqrt(1 + (z / zR(w0,lam))**2)
return w
def Rz(z,w0,lam):
Rz = z * (1 + (zR(w0,lam) / z)**2)
return Rz
def zR(w0,lam):
zR = pi * lam
return zR
p = E(E0,w0,x,z,lam)
plt.imshow(p)
It took me way too much time and thinking but I finally figured it out after searching for similar examples of codes for similar problems. The correct code looks like:
import numpy as np
from numpy import array,empty,linspace,exp,cos,sqrt,pi
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
lam = 500*10**-9 #Nanometers
x1 = linspace(-10*lam,10*lam,100)
z1 = linspace(-20*lam,20*lam,100)
[x,y] = np.meshgrid(x1,z1)
w0 = lam
E0 = 5
r = sqrt(x**2)
zR = pi * lam
w = w0 * sqrt(1 + (y / zR)**2)
Rz = y * (1 + (zR / y)**2)
E = (E0 * w0) / w
E = E * exp((-r**2 / w**2))
E = E * cos((2 * pi / lam) * (y + (r**2 / (2 * Rz))))
def field(x,y):
lam = 500*10**-9
k = (5 * lam) / lam * sqrt(1 + (y / (pi*lam))**2)
k *= exp(((-sqrt(x**2)**2 / (lam * sqrt(1 + (y / pi * lam)**2))**2)))
k *= cos((2 / lam) * (y + ((sqrt(x**2)**2 / (2 * y * (1 + (pi * lam / y)**2))))))
return k
#Density Plot
f = field(x,y)
plt.imshow(f)
plt.show()
#Surface Plot
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x,y,E,rstride=1,cstride=1)
plt.show

Python: Adding an initial and final position to a trajectory

How can I add a marking along a plot at the position of -20 degrees true anomaly and 103 degrees true anomaly?
What I have is a trajectory of a space craft along a hyperbolic path around Earth where I solved for the time it took to fly between the true anomalies. Unfortunately, I don't know way to mark a position at a specified angle on the path.
I know I can do
initial = pylab.Circle((location), radius = 10, color = '#000000')
final = ....
but what do I put for the location coordinates? Is there a way to do this without the brute force method of solving for the coordinates?
import numpy as np
import pylab
a = -35000 # the semi-major axis of the hyperbolic trajectory
e = 1.2 # the eccentricity
nu0 = -20 * np.pi / 180 # initial anomaly
nuf = 103 * np.pi / 180 # final anomaly
F0 = 2 * np.arctanh(np.sqrt((e - 1) / (e + 1)) * np.tan(nu0 / 2))
Ff = 2 * np.arctanh(np.sqrt((e - 1) / (e + 1)) * np.tan(nuf / 2))
M0 = e * np.sinh(F0) - F0
Mf = e * np.sinh(Ff) - Ff
n = np.sqrt(398600.0 / -a ** 3) # the mean motion
deltat = (Mf - M0) / n # change of time in secs
hours = deltat / 3600
h = np.sqrt(a * 398600 * (1 - e ** 2))
def r(nu):
return h ** 2 / (398600 * (1 + e * np.cos(nu)))
rt = r(nu)
ext = [np.argmin(rt), np.argmax(rt)]
rt[ext] = np.nan
nu = np.linspace(0, 2 * np.pi, 500000)
fig = pylab.figure()
ax = fig.add_subplot(111, aspect = 'equal')
earth = pylab.Circle((0, 0), radius = 6378, color = 'blue')
ax.add_patch(earth)
ax.plot(rt * np.cos(nu), rt * np.sin(nu), 'r')
pylab.axis([-70000, 10000, -40000, 40000])
pylab.show()

Categories