Using a linear equation to create a radially interpolated circle - python

Given a linear equation, I want to use the slope to create a circle of values around a given point, defined by the slope of the linear equation if possible
Im currently a bit far away - can only make the radial plot but do not know how to connect this with an input equation. My first thought would be to change the opacity using import matplotlib.animation as animation and looping matplotlib's alpha argument to become gradually more and more opaque. However the alpha doesnt seem to change opacity.
Code:
# lenth of radius
distance = 200
# create radius
radialVals = np.linspace(0,distance)
# 2 pi radians = full circle
azm = np.linspace(0, 2 * np.pi)
r, th = np.meshgrid(radialVals, azm)
z = (r ** 2.0) / 4.0
# creates circle
plt.subplot(projection="polar")
# add color gradient
plt.pcolormesh(th, r, z)
plt.plot(azm, r,alpha=1, ls='', drawstyle = 'steps')
#gridlines
# plt.grid()
plt.show()

Here is one way to solve it, the idea is to create a mesh, calculate the colors with a function then use imshow to visualize the mesh.
from matplotlib import pyplot as plt
import numpy as np
def create_mesh(slope,center,radius,t_x,t_y,ax,xlim,ylim):
"""
slope: the slope of the linear function
center: the center of the circle
raadius: the radius of the circle
t_x: the number of grids in x direction
t_y: the number of grids in y direction
ax: the canvas
xlim,ylim: the lims of the ax
"""
def cart2pol(x,y):
rho = np.sqrt(x**2 + y**2)
phi = np.arctan2(y,x)
return rho,phi
def linear_func(slope):
# initialize a patch and grids
patch = np.empty((t_x,t_y))
patch[:,:] = np.nan
x = np.linspace(xlim[0],xlim[1],t_x)
y = np.linspace(ylim[0],ylim[1],t_y)
x_grid,y_grid = np.meshgrid(x, y)
# centered grid
xc = np.linspace(xlim[0]-center[0],xlim[1]-center[0],t_x)
yc = np.linspace(ylim[0]-center[1],ylim[1]-center[1],t_y)
xc_grid,yc_grid = np.meshgrid(xc, yc)
rho,phi = cart2pol(xc_grid,yc_grid)
linear_values = slope * rho
# threshold controls the size of the gaussian
circle_mask = (x_grid-center[0])**2 + (y_grid-center[1])**2 < radius
patch[circle_mask] = linear_values[circle_mask]
return patch
# modify the patch
patch = linear_func(slope)
extent = xlim[0],xlim[1],ylim[0],ylim[1]
ax.imshow(patch,alpha=.6,interpolation='bilinear',extent=extent,
cmap=plt.cm.YlGn,vmin=v_min,vmax=v_max)
fig,ax = plt.subplots(nrows=1,ncols=2,figsize=(12,6))
slopes = [40,30]
centroids = [[2,2],[4,3]]
radii = [1,4]
for item in ax:item.set_xlim(0,8);item.set_ylim(0,8)
v_max,v_min = max(slopes),0
create_mesh(slopes[0],centroids[0],radii[0],t_x=300,t_y=300,ax=ax[0],xlim=(0,8),ylim=(0,8))
create_mesh(slopes[1],centroids[1],radii[1],t_x=300,t_y=300,ax=ax[1],xlim=(0,8),ylim=(0,8))
plt.show()
The output of this code is
As you can see, the color gradient of the figure on the left is not as sharp as the figure on the right because of the different slopes ([40,30]).
Also note that, these two lines of code
v_max,v_min = max(slopes),0
ax.imshow(patch,alpha=.6,interpolation='bilinear',extent=extent,
cmap=plt.cm.YlGn,vmin=v_min,vmax=v_max)
are added in order to let the two subplots share the same colormap.

Related

How to draw tangential circles to the inside of a curve in Python using numpy and matplotlib in Python?

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

How to tell is a plotted point is within a circle matplotlib

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.

How can I rotate a graph that contains an imshow in matplotlib?

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()

Changing aspect ratio for 3D plots on Matplotlib

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.

How to Generate a Homogeneous Poisson Point Process in a circle?

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

Categories