I would like to plot circles which are tangents to the interior of a curve in Python as shown below:
I tried the following approach.
I created an inverted exponential curve to get the curve as shown below.
x = np.arange(0, 2, 0.1)
y = [1/np.exp(i) for i in x]
plt.plot(x, y, marker = "o")
To add a circle as tangent to the curve, I am adding a circle manually as follows:
fig, ax = plt.subplots()
#Plot exponential curve
ax.plot(x, y)
#Add tangential circle manually
pi = np.pi
c1 = plt.Circle((x[1]+ np.sin(pi/4) * 0.1, y[1] + np.sin(pi/4) * 0.1),
radius = 0.1, facecolor = "black", ec = "white")
ax.add_patch(c1)
Here my assumption is that the angle denoted by the shaded part in the figure below (between the normal radius and horizontal x-axis is 45 degrees).
However, I think this is an incorrect assumption and incorrect way to do it.
When I add more circles, they are not exactly tangential to the curve as shown below.
What would be the correct approach to draw the circles as tangents to the curve? Is it possible to do it in Python?
Using trigonometry (there is likely a way to simplify but I'm rusty+tired :p).
fig, ax = plt.subplots()
X = np.arange(0, 5, 0.1)
Y = np.sin(X)
#Plot exponential curve
ax.plot(X, Y)
ax.set_aspect('equal')
#Add tangential circles
for i in np.arange(1,len(X),5):
ax.plot(X[i], Y[i], marker='o', color='r')
pi = np.pi
radius = 0.3
# slope of tangent
dydx = np.gradient(Y)[i]/np.gradient(X)[i]
# slope of perpendicular
slope = 1/abs(dydx)
# angle to horizontal
angle = np.arctan(slope)
c1 = plt.Circle((X[i] - np.sign(dydx)*np.cos(angle) * radius,
Y[i] + np.sin(angle) * radius),
radius = radius, facecolor = "black", ec = "white")
ax.add_patch(c1)
output:
Your function is exp(-x)
It's derivative is -exp(-x), so tangent vector at point x, exp(-x) is (1, -exp(-x)) and normal vector is (exp(-x), 1) (note sign change).
Normalize it
n_len = np.hypot((y)[i], 1)
nx = y[i] / n_len
ny = 1 / n_len
center for radius R is
c1 = plt.Circle((x[i] + R * nx, y[i] + R * ny, ...
For the circle to be tangent, its center must be located along the normal to the curve (this is a straight-line with slope -δx/δy).
You did not specify how the radius varies, so there are two options:
the radius is a function of x or y or s (curvilinear abscissa) that you supply; then place the center at the desired distance from the contact point, on the normal;
the radius is such that it matches the local curvature; then what you want is the osculating circle; there is a formula to compute the radius of curvature.
https://en.wikipedia.org/wiki/Osculating_circle
I have a circle plotted on a graph using matplotlib
angle = np.linspace(0, 4 * np.pi, 150)
radius = 0.2
x = radius * np.cos(angle) + 0.5
y = radius * np.sin(angle) + 0.5
fig, ax = plt.subplots()
ax.set_aspect(1)
ax.plot(x, y)
I also have some code that randomly plots scatter points on the graph:
q = [random.uniform(0.3, 0.7) for n in range(900)]
b = [random.uniform(0.3, 0.7) for n in range(900)]
ax.scatter(q, b, color='black', marker='.', s=1)
I want to count how many of the randomly plotted points fall within the circle. Is there a way I can do this?
To do this you have to find the points that have a distance between them and the center of the circle inferior to the radius. The center of the circle is (0.5, 0.5).
To do this you can do the following:
import numpy as np
q, b = np.array(q), np.array(b)
# The mask is a boolean array that tells if a point is inside
# of the circle or outside of the circle.
mask = np.sqrt((q-0.5)**2 + (b-0.5)**2) <= radius
#np.count_nonzero counts the number of True elements in an array.
number_inside = np.count_nonzero(mask)
number_inside is the number of points inside the circle.
I have some microscopic scan data in a rectangular grid that was scanned by an X/Y scanner. The object I'm scanning contains markers in a nice, rectangular, orthogonal pattern on its surface.
Due to practicalities, we can only align the object to the X/Y scanner to within a few degrees when fixing it to the X/Y scanner table. We compensate for this by some calibration measurement, and then performing scans that are nicely aligned with the object we're trying to scan (by moving the X and Y axes simultaneously).
This works well, but now I want to display this data in a coordinate system that corresponds to the X/Y scanner's axes. So the scanned, rectangular dataset will be rotated a bit in that coordinate system. (See the second image below for what I mean).
For presentation purposes however, I would prefer to have the scanned image nicely horizontal, and the axes rotated. I am trying to accomplish this in Matplotlib but I'm failing miserably.
I can get a rotated Axes (see the third image), but I am unable to figure out what kind of transforms I should do to get the ax3.imshow() to show its data at its intended position.
Here's where I am so far:
The program to generate the test data and the image I have so far is shown below.
I'd appreciate any help I can get. I'm afraid to say the documentation of matplotlib leaves a lot to be desired, so I hope somebody who knows matplotlib inside out may chime in.
#! /usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
import random
angle_deg = 5.0 # degrees CCW.
# Test data
NX = 5
NY = 3
x = np.zeros(shape=(NY, NX))
y = np.zeros(shape=(NY, NX))
c = np.zeros(shape=(NY, NX))
angle_rad = np.deg2rad(angle_deg)
for iy in range(NY):
for ix in range(NX):
icx = 0.5 * (NX - 1)
icy = 0.5 * (NY - 1)
# To centered coordinates, in physical units
xcp = (ix - icx) * 0.1
ycp = (iy - icy) * 0.1
# Rotate
xcpr = xcp * np.cos(angle_rad) - ycp * np.sin(angle_rad)
ycpr = xcp * np.sin(angle_rad) + ycp * np.cos(angle_rad)
# Rotate, and translated to the physical center
x[iy, ix] = xcpr + 40.0
y[iy, ix] = ycpr + 30.0
c[iy, ix] = 100 + ix + iy
fig = plt.figure()
fig.set_size_inches(15, 9)
# Add axis #1.
ax1 = fig.add_subplot(131)
ax1.set_title("imshow()")
ax1.imshow(c, origin='lower')
ax1.grid()
# Add axis #2.
ax2 = fig.add_subplot(132)
ax2.set_title("pcolormesh()")
ax2.pcolormesh(x, y, c, shading='nearest')
ax2.axis('equal')
ax2.grid()
# Add axis #3.
min_x = x.min()
max_x = x.max()
min_y = y.min()
max_y = y.max()
cx = 0.5 * (min_x + max_x)
cy = 0.5 * (min_y + max_y)
transform = Affine2D().rotate_deg(-angle_deg)
grid_helper = floating_axes.GridHelperCurveLinear(transform, extremes=(min_x, max_x, min_y, max_y))
ax3 = floating_axes.FloatingSubplot(fig, 133, grid_helper=grid_helper)
ax3.grid()
ax3.set_title("rotate the entire graph.\nHow do I do get the data to show up here,\nhorizontally like in the leftmost image!?\n")
ax3.imshow(c, origin='lower')
fig.add_subplot(ax3)
plt.show()
I am trying to set up the aspect ratio for 3D plots using Matplotlib.
Following the answer for this question: Setting aspect ratio of 3D plot
I kind of applied the solution as:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d import proj3d
def get_proj(self):
"""
Create the projection matrix from the current viewing position.
elev stores the elevation angle in the z plane
azim stores the azimuth angle in the (x, y) plane
dist is the distance of the eye viewing point from the object point.
"""
# chosen for similarity with the initial view before gh-8896
relev, razim = np.pi * self.elev/180, np.pi * self.azim/180
#EDITED TO HAVE SCALED AXIS
xmin, xmax = np.divide(self.get_xlim3d(), self.pbaspect[0])
ymin, ymax = np.divide(self.get_ylim3d(), self.pbaspect[1])
zmin, zmax = np.divide(self.get_zlim3d(), self.pbaspect[2])
# transform to uniform world coordinates 0-1, 0-1, 0-1
worldM = proj3d.world_transformation(xmin, xmax,
ymin, ymax,
zmin, zmax)
# look into the middle of the new coordinates
R = self.pbaspect / 2
xp = R[0] + np.cos(razim) * np.cos(relev) * self.dist
yp = R[1] + np.sin(razim) * np.cos(relev) * self.dist
zp = R[2] + np.sin(relev) * self.dist
E = np.array((xp, yp, zp))
self.eye = E
self.vvec = R - E
self.vvec = self.vvec / np.linalg.norm(self.vvec)
if abs(relev) > np.pi/2:
# upside down
V = np.array((0, 0, -1))
else:
V = np.array((0, 0, 1))
zfront, zback = -self.dist, self.dist
viewM = proj3d.view_transformation(E, R, V)
projM = self._projection(zfront, zback)
M0 = np.dot(viewM, worldM)
M = np.dot(projM, M0)
return M
Axes3D.get_proj = get_proj
Then I'm creating a sample data and plotting as:
y,z,x = np.meshgrid(range(10),-np.arange(5)[::-1],range(20))
d = np.sin(x)+np.sin(y)+np.sin(z)
iy,ix = 0,-1
iz = -1
fig,ax = plt.subplots(figsize=(5,5),subplot_kw={'projection':'3d'})
ax.pbaspect = np.array([1, 1, 1])#np.array([2.0, 1.0, 0.5])
ax.contourf(x[iz], y[iz], d[iz],zdir='z',offset=0)
ax.contourf(x[:,iy,:],d[:,iy,:],z[:,iy,:],zdir='y',offset=y.min())
ax.contourf(d[:,:,ix],y[:,:,ix],z[:,:,ix],zdir='x',offset=x.max())
color = '0.3'
ax.plot(x[0,iy,:],y[0,iy,:],y[0,iy,:]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,iy,0]*0+x.max(),y[:,iy,0],z[:,iy,0],color,linewidth=1,zorder=1e4)
ax.plot(x[0,:,ix],y[0,:,ix],y[0,:,ix]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,0,ix],y[:,0,ix]*0+y.min(),z[:,0,ix],color,linewidth=1,zorder=1e4)
ax.set(xlim=[x.min(),x.max()],ylim=[y.min(),y.max()],zlim=[z.min(),z.max()])
fig.tight_layout()
If I set the pbaspect parameter as (1,1,1) I obtain:
But If I set it for (2,1,0.5) the axis seems to be correct, but it crops the data somehow:
Even if I let the xlim,ylim and zlim automatic. There is something strange with the aspect too.
Something tells me that the the axis as not orthogonal.
Does someone know how to correct the aspect ratio for this?
I would also like to know how to avoid the axis being cropped by the figure limits.
I searched on the web so long, but I could not find a better solution for this.
Update:
I tried using less than 1 values for pbaspect as suggested and it gets beter, but still crops the data:
You can try to change figsize and adjust subplots margins like below:
fig = plt.figure(figsize=(10,6))
fig.subplots_adjust(left=0.2, right=0.8, top=0.8, bottom=0.2)
ax = fig.gca(projection='3d')
ax.pbaspect = np.array([2, 1, 0.5])
ax.contourf(x[iz], y[iz], d[iz],zdir='z',offset=0)
ax.contourf(x[:,iy,:],d[:,iy,:],z[:,iy,:],zdir='y',offset=y.min())
ax.contourf(d[:,:,ix],y[:,:,ix],z[:,:,ix],zdir='x',offset=x.max())
ax.plot(x[0,iy,:],y[0,iy,:],y[0,iy,:]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,iy,0]*0+x.max(),y[:,iy,0],z[:,iy,0],color,linewidth=1,zorder=1e4)
ax.plot(x[0,:,ix],y[0,:,ix],y[0,:,ix]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,0,ix],y[:,0,ix]*0+y.min(),z[:,0,ix],color,linewidth=1,zorder=1e4)
plt.show()
Output for (2,1,0.5):
Regarding disappearing graphics there is Matplotlib issue:
The problem occurs due to the reduction of 3D data down to 2D + z-order scalar. A single value represents the 3rd dimension for all parts of 3D objects in a collection. Therefore, when the bounding boxes of two collections intersect, it becomes possible for this artifact to occur. Furthermore, the intersection of two 3D objects (such as polygons or patches) can not be rendered properly in matplotlib’s 2D rendering engine.
I would like to generate N points in a circle C of center (0,0) and of radius R=200. The points follow Poisson distribution. In other words, I would like to generate N homogeneous Poisson point process (HPPP) inside C.
I found this paper Generating Homogeneous Poisson Processes . In Section 2 there is exactly what I want. Specifically, in page 4, Algorithm 3 generates the points HPPP inside C.
I implemented this code in Python as follow:
""" Main Codes """
import matplotlib.pyplot as plt
import numpy as np
lamb = 0.0005 # the rate
pi = np.pi # pi = 3.14...
r = 200 # the radius of the circle C
mean = lamb * pi * r ** 2 # the mean of the Poisson random variable n
n = np.random.poisson(mean) # the Poisson random variable (i.e., the number of points inside C)
u_1 = np.random.uniform(0.0, 1.0, n) # generate n uniformly distributed points
radii = np.zeros(n) # the radial coordinate of the points
for i in range(n):
radii[i] = r * (np.sqrt(u_1[i]))
u_2 = np.random.uniform(0.0, 1.0, n) # generate another n uniformly distributed points
angle = np.zeros(n) # the angular coordinate of the points
for i in range(n):
angle[i] = 2 * pi * u_2[i]
""" Plots """
fig = plt.gcf()
ax = fig.gca()
plt.xlim(-300, 300)
plt.ylim(-300, 300)
circ = plt.Circle((0, 0), radius=200, color='r', linewidth=2, fill=False)
plt.polar(angle, radii, 'bo')
ax.add_artist(circ)
plt.show()
First, I cannot see the points inside the circle. Second, I don't know why the points do not generate inside the circle properly. Is there a problem in my code?
The output is given below: The circle C is in red.
I found the answer. I just convert the polar coordinates to the Cartesian coordinates and then I plot with plt.plot() not with plt.polar().
# Cartesian Coordinates
x = np.zeros(n)
y = np.zeros(n)
for i in range(n):
x[i] = radii[i] * np.cos(angle[i])
y[i] = radii[i] * np.sin(angle[i])
plt.plot(x,y,'bo')
So I get the desired output.
A few years late, but I wrote about this problem a few months ago; see this post.
For future readers, here's my code:
import numpy as np
import scipy.stats
import matplotlib.pyplot as plt
#Simulation window parameters
r=1;
xx0=0; yy0=0; #centre of disk
areaTotal=np.pi*r**2; #area of disk
#Point process parameters
lambda0=100; #intensity (ie mean density) of the Poisson process
#Simulate Poisson point process
numbPoints = scipy.stats.poisson( lambda0*areaTotal ).rvs()#Poisson number of points
theta = 2*np.pi*scipy.stats.uniform.rvs(0,1,((numbPoints,1)))#angular coordinates of Poisson points
rho = r*np.sqrt(scipy.stats.uniform.rvs(0,1,((numbPoints,1))))#radial coordinates of Poisson points
#Convert from polar to Cartesian coordinates
xx = rho * np.cos(theta)
yy = rho * np.sin(theta)
#Shift centre of disk to (xx0,yy0)
xx=xx+xx0; yy=yy+yy0;
#Plotting
plt.scatter(xx,yy, edgecolor='b', facecolor='none', alpha=0.5 )
plt.xlabel("x"); plt.ylabel("y")
plt.axis('equal')
A sample:
A realization of a Poisson point process on a disk