How to fill a circle with a gradient? - python

Is there a way to fill a circle created by :
ax.add_patch(ptc.Circle(.....)
with a vertical gradient from a colormap :
grad = cm.get_cmap('plasma', 100)
The expected output:
I don't know how to do this, but according to the picture someone got it with imshow ().
Thanks

You can draw an image and set a clip path:
import numpy as np
import matplotlib.pyplot as plt
x, y, r = 0, 35, 25
fig, ax = plt.subplots()
img = ax.imshow(np.linspace(0, 1, 256).reshape(-1, 1), cmap='plasma',
extent=[x - r, x + r, y - r, y + r], origin='lower')
circle = plt.Circle((x, y), r, transform=ax.transData)
img.set_clip_path(circle)
ax.use_sticky_edges = False
ax.margins(x=0.05, y=0.05)
plt.show()

Related

Anti-aliased image of a spline shape

My goal is to create 3 images, one is an anti-aliased image of a random spline and then two others that scaled between 0 and 1 for how "horizontal" or "vertical" the spline is at each point.
from scipy.interpolate import CubicSpline, griddata
import numpy as np
import matplotlib.pyplot as plt
def create_random_line():
# Create random spline
x = np.array([1, 15, 30, 49])
y = np.random.uniform(1, 50, 4)
f = CubicSpline(x, y, bc_type='natural')
x_new = np.linspace(0, 49, 100)
y_new = f(x_new)
y_new_deriv = f(x_new, 1)
y_angles = np.array([math.atan2(tt, 1) for tt in y_new_deriv])
# Plot the spline, derivative and angle
plt.figure(2)
plt.clf()
plt.subplot(3,1,1)
plt.plot(x, y, 'x')
plt.xlim((0, 50))
plt.ylim((0, 50))
plt.plot(x_new, y_new)
plt.subplot(3,1,2)
plt.plot(x_new, y_new_deriv)
plt.subplot(3,1,3)
plt.plot(x_new, np.rad2deg(y_angles))
plt.ylim((-90, 90))
plt.show()
# Create image of spline
image = np.zeros((50, 50))
scaled_angle_maps = np.zeros((50, 50, 2))
for xx, yy, rr in zip(y_new, x_new, np.rad2deg(y_angles)):
image[int(np.round(xx)), int(np.round(yy))] = 1
scaled_angle_maps[int(np.round(xx)), int(np.round(yy)), 0] = np.clip(1 - (np.abs(rr)/90), 0, 1)
scaled_angle_maps[int(np.round(xx)), int(np.round(yy)), 1] = np.clip(np.mod(np.abs(rr),90)/90, 0, 1)
return image, scaled_angle_maps
# Create random spline image
image, scaled_angle_maps = create_random_line()
# Plot
plt.figure(1)
plt.clf()
plt.subplot(2,2,1)
plt.imshow(image)
plt.gray()
plt.colorbar()
plt.ylim((0,50))
plt.subplot(2,2,3)
plt.imshow(scaled_angle_maps[:,:,0])
plt.ylim((0,50))
plt.colorbar()
plt.title('horizontal')
plt.subplot(2,2,4)
plt.imshow(scaled_angle_maps[:,:,1])
plt.ylim((0,50))
plt.colorbar()
plt.title('vertical')
plt.show()
But, I would like this anti-aliased. I have been reading about Wu's algorithm, but most implementations appear to be for straight lines. I tried creating it from the matplotlib canvas, but that did not work out well.
Then, second, I would like to have an arbitrary thickness to the spline in the image, though I suppose I could just scipy.ndimage.grey_dilation after the image is created.
So, am I missing an easy method of creating a random spline image? It feels like there should be a simpler method to do this.

How to identify certain coordinates (x,y) for its color after matplotlib.pyplot fill function?

Is there any way to input a certain coordinates (x,y) to pyplot and output its filling color in return?
ex. (0,0) -> Red , (0.75,0) -> blue , (1,1) ->white
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(15,15))
x,y,x2,y2=[],[],[],[]
for i in np.linspace(0,2,300):
x.append(np.cos(np.pi*i))
y.append(np.sin(np.pi*i))
x2.append(0.5*np.cos(np.pi*i))
y2.append(0.5*np.sin(np.pi*i))
plt.fill(x,y,'b')
plt.fill(x2,y2,'r')
Color Image
Here is one possible method. Let's begin with a modified code (based on yours):
# Modified code
import numpy as np
import matplotlib.pyplot as plt
from shapely.geometry import Point, Polygon # additional code
plt.figure(figsize=(15,15))
x,y,x2,y2=[],[],[],[]
for i in np.linspace(0,2,300):
x.append(np.cos(np.pi*i))
y.append(np.sin(np.pi*i))
x2.append(0.5*np.cos(np.pi*i))
y2.append(0.5*np.sin(np.pi*i))
# grab the fill objects for use later
# .fill return: matplotlib.patches.Polygon object
blue_pgn = plt.fill(x,y,'b') #Plot filled polygons
red_pgn = plt.fill(x2,y2,'r')
plt.show()
It produces the same plot as your code does, but also exposes 2 useful objects, blue_pgn and red_pgn .
This is the second part:
# Create geometries from objects in the plot
blue_geom = Polygon(blue_pgn[0].get_xy())
red_geom = Polygon(red_pgn[0].get_xy())
# create a function
def from_xy_to_color(x,y):
point_xy = Point(x,y)
if red_geom.contains(point_xy):
print("xy:",x,y,", color:","Red")
elif blue_geom.contains(point_xy):
print("xy:",x,y,", color:","Blue")
else:
print("xy:",x,y,", color:","White")
# test all the points
xys = [[0,0], [.75,0], [1,1]]
for xy in xys:
#print(xy)
from_xy_to_color(*xy)
The output from this part:
xy: 0 0 , color: Red
xy: 0.75 0 , color: Blue
xy: 1 1 , color: White
from matplotlib.backends.backend_agg import FigureCanvasAgg
import numpy as np
import matplotlib.pyplot as plt
def circle_coords():
t = np.linspace(0, 2, 300) * np.pi
x = np.cos(t)
y = np.sin(t)
return x, y
def plot(fig):
ax = fig.gca()
x, y = circle_coords()
ax.fill(x, y, 'b')
ax.fill(x / 2, y / 2, 'r')
def capture():
plt.Figure((4, 4), dpi=20)
fig = plt.gcf()
canvas = FigureCanvasAgg(fig)
plot(fig)
canvas.draw()
r = canvas_to_numpy(canvas)
plt.close()
return r
def canvas_to_numpy(canvas):
s, (width, height) = canvas.print_to_buffer()
x = np.frombuffer(s, np.uint8)
return x.reshape((height, width, 4))
def random_points(shape, n_points=4):
height, width = shape
x = np.random.uniform(0, width, size=n_points)
y = np.random.uniform(0, height, size=n_points)
return np.vstack([x, y]).T
def main():
arr = capture()
p = [[360, 274],
[379, 48],
[117, 216]]
fig = plt.gcf()
ax = fig.gca()
np.set_printoptions(precision=2)
print(p)
for x, y in p:
r, g, b, a = arr[y, x] / 255
c = f"{r, g, b}"
print(arr[y, x])
ax.text(x, y, c)
ax.scatter(x, y, c="yellow")
plt.imshow(arr)
plt.show()
plt.close()
main()
See: Matplotlib figure to image as a numpy array

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

Difficulty plotting a two dimensional lognorm surface

here is the code im using and I've also attached the output. I'd like to plot a two dimensional lognorm function as a 3d surface, the above code is supposed to do this however the output results in the entire plane being skewed rather than just the z values. any help or suggestions would be greatly appreciated.
dx = 90 - (-90)
dy = 90 - (-90)
c = [dx + dx/2.0, dy+dy/2.0]
z = np.zeros((400, 400))
x = np.linspace(-90, 90, 400)
y = x.copy()
for i in range(len(x)):
for j in range(len(y)):
p =[x[i], y[j]]
d = math.sqrt((p[0]-c[0])**2 + (p[1]-c[1])**2)
t = d
z[i][j] = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(x,y, z, cmap = 'viridis')
plt.show()
output of the provided code
ideally I'd like for it to look something like this.
this is the image here
I think you wanted to plot a 3D surface and here is an example:
#!/usr/bin/python3
# 2018/10/25 14:44 (+0800)
# Plot a 3D surface
from scipy.stats import norm, lognorm
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
xy = np.linspace(-5, 5, 400)
xx, yy = np.meshgrid(xy)
t = np.sqrt(xx**2 + yy**2)
zz = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(xx,yy, zz, cmap = 'viridis')
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

Categories

Resources