I calculate ellipse-like polygons which are drawn over another image. The polygons together form one shape and must have a single transparency. Unfortunately, if I draw one polygon after another, the stack gets different transparencies. In the example below I have 3 polygons, creating 3 transparencies, resulting in different grey areas instead of one. Does anyone know how to obtain a single transparency?
Thank you
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle
def get_ellipse_coords(a=0.0, b=0.0, x=0.0, y=0.0, angle=0.0, k=2):
pts = np.zeros((360*k+1, 2))
beta = -angle * np.pi/180.0
sin_beta = np.sin(beta)
cos_beta = np.cos(beta)
alpha = np.radians(np.r_[0.:360.:1j*(360*k+1)])
sin_alpha = np.sin(alpha)
cos_alpha = np.cos(alpha)
pts[:, 0] = x + (a * cos_alpha * cos_beta - b * sin_alpha * sin_beta)
pts[:, 1] = y + (a * cos_alpha * sin_beta + b * sin_alpha * cos_beta)
return pts
if __name__ == '__main__':
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
plt.xlim([-5,5])
plt.ylim([-5,5])
c='0.5'
alp=0.5
ax.add_patch(Rectangle((1.5, -4.0),0.5,8.0,
color='lightblue', zorder=1))
pts = get_ellipse_coords(a=4.0, b=1.0)
ax.add_patch(Polygon(pts, closed=True, lw=0.5,
color=c, alpha=alp,zorder=2))
pts = get_ellipse_coords(a=4.0, b=1.0, x=1.0, angle=30)
ax.add_patch(Polygon(pts, closed=True, lw=0.5,
color=c, alpha=alp, zorder=2))
pts = get_ellipse_coords(a=2.0, b=0.25, x=2.0, y=-2.0, angle=250)
ax.add_patch(Polygon(pts, closed=True, lw=0.5,
color=c, alpha=alp, zorder=2))
plt.show()
np.concatenate, suggested in the first answer, does the trick I need:
[...]
ax.add_patch(Rectangle((1.5, -4.0),0.5,8.0,
color='lightblue', zorder=1))
pts1 = get_ellipse_coords(a=4.0, b=1.0)
pts2 = get_ellipse_coords(a=4.0, b=1.0, x=1.0, angle=30)
pts3 = get_ellipse_coords(a=2.0, b=0.25, x=2.0, y=-2.0, angle=250)
stoppoint = np.array([[np.nan,np.nan]])
pts = np.concatenate((pts1, stoppoint, pts2))
pts = np.concatenate((pts, stoppoint, pts3))
ax.add_patch(Polygon(pts, closed=True, lw=0.0,
color=c, alpha=alp, zorder=2))
Like this:
pts1 = get_ellipse_coords(a=4.0, b=1.0)
pts2 = get_ellipse_coords(a=4.0, b=1.0, x=1.0, angle=30)
stoppoint = np.array([[nan,nan]])
pts = np.concatenate((pts1, stoppoint, pts2))
ax.add_patch(Polygon(pts, closed=True, lw=0.5,
color=c, alpha=alp,zorder=2))
The stoppoint prevents a line connecting the ellipses.
The accepted solution did not work for me, I had to build my compound path with matplotlib.path.Path.make_compound_path_from_polys and then pass its vertices to my matplotlib.patches.Polygon class instance.
The gist of it as documented is as follows:
data = np.random.randn(1000)
n, bins = np.histogram(data, 50)
# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n
# we need a (numrects x numsides x 2) numpy array for the path helper
# function to build a compound path
XY = np.array([[left, left, right, right], [bottom, top, top, bottom]]).T
# get the Path object
barpath = path.Path.make_compound_path_from_polys(XY)
# make a patch out of it
patch = patches.PathPatch(barpath)
ax.add_patch(patch)
Related
The following code calculates the following vectors:
the orientation Vector (red) and two vectors (blues) which result by rotating the red vector 60 degree clock -und counterclockwise.
import matplotlib.pyplot as plt
import numpy as np
def Visualize(orienVector,vector1,vector2):
# Create figure and subplot
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Plot data points
#ax.scatter(vector1[0], vector1[1], color='blue')
#ax.scatter(vector2[0], vector2[1], color='orange')
# Set limits for x and y axes
plt.xlim(-1, 1)
plt.ylim(-1, 1)
# ===== Important bits start here =====
# Set properties of spines
ax.spines['top'].set_color('none')
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
# Set axis tick positions
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# Set specific tick locations
ax.set_xticks([-10,-5, 0, 5 , 10])
ax.set_yticks([-10,-5, 0, 5 , 10])
# ===== End Of Important Bits =====
# Draw arrows
ax.arrow(0, 0 , vector1[0][0], vector1[1][0],
head_width=0.03,
head_length=0.1,
lw=1,
fc='blue',
ec='blue',
length_includes_head=True)
ax.arrow(0, 0 , vector2[0][0], vector2[1][0],
head_width=0.03,
head_length=0.1,
lw=1,
fc='blue',
ec='blue',
length_includes_head=True)
ax.arrow(0, 0 , orienVector[0][0], orienVector[1][0],
head_width=0.03,
head_length=0.1,
lw=2,
fc='red',
ec='red',
length_includes_head=True)
plt.show()
# rotation matrix clockwise
def rotMatrixClockWise(angle):
c, s = np.cos(angle), np.sin(angle)
R = np.array([[c, -s], [s, c]])
return R
# rotation matrix clockwise
def rotMatrixCounterClockWise(angle):
c, s = np.cos(angle), np.sin(angle)
R = np.array([[c, s], [-s, c]])
return R
# center of the poit of interests POIS
POI_X = [10,12,15,17,20,50]
POI_Y = [20,30,25,22,19,35]
# position of the pedestrian
pedPostion = np.array([[3],[4]])
# range of the horisontal angel view
spanningAngle = np.radians(60)
# calculate the cone
for angle in range(0,360,5):
# calculating the component of orientation vector V0, where the length |V0| = 1
x0 = 5*np.cos(np.radians(angle))
y0 = 5*np.sin(np.radians(angle))
v0 = np.array([[x0],[y0]])
v1 = rotMatrixCounterClockWise(spanningAngle).dot(v0)
v2 = rotMatrixClockWise(spanningAngle).dot(v0)
Visualize(v0,v1,v2)
The output of this vector looks like
I'm trying to fill the area between the blue vectors to obtain a cone like the following:
The distance between the head of the cone (0,0) and the arc is always 5
However, I can't get it to work. I'm new to Matlibplot
Maybe not completely what you are looking for, but you could create a wedge between the vectors. It would be more appropriate to fit an ellipse and fill the coordinates in between. Here is an example for the wedge.
import matplotlib.pyplot as plt, numpy as np
from matplotlib import patches
v1 = np.array((1, 2))
v2 = np.array((1, -2))
base = np.array((0, 0))
theta1 = np.rad2deg(np.arctan(v1[1] / v1[0]))
theta2 = np.rad2deg(np.arctan(v2[1] / v2[0]))
fig, ax = plt.subplots()
a, b = theta1, theta2
if b > a:
a, b = b, a
artist = patches.Arc(base,
width = 1,
height = 1,
theta1 = a,
theta2 = b,
color = 'green')
ax.arrow(*base, *v1, color = 'black')
ax.arrow(*base, *v2, color = 'black')
ax.arrow(*base, *(v2 + v1), color = 'red')
wedge = patches.Wedge(base,
r = 1,
width = 1,
theta1 = theta2,
theta2 = theta1,
color = 'green')
ax.add_patch(wedge)
fig.show()
You could figure out analytically the points in the arc (a portion of a circle, so y = +- (1-x^2)**0.5), add them to the polygon defined by the origin and extremities of red and yellow vectors, then use https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill.html.
An example of filling a polygon is here: https://matplotlib.org/stable/gallery/lines_bars_and_markers/fill.html#sphx-glr-gallery-lines-bars-and-markers-fill-py
I'm trying to do something similar to this 3D example from the docs, but with a point cloud instead of a smooth surface. The example projects 2D contours onto each of the three coordinate planes. This shows that I'm able to do that onto the xy-plane.
When I try doing the same onto the other two planes, I get either a weird contour collapsed down to a thin strip,
or an exception way beyond my reach in the bowels of matplotlib.
Traceback (most recent call last):
File ".../matplotlib/backends/backend_qt5.py", line 519, in _draw_idle
self.draw()
File ".../matplotlib/backends/backend_agg.py", line 433, in draw
self.figure.draw(self.renderer)
File ".../matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File ".../matplotlib/figure.py", line 1475, in draw
renderer, self, artists, self.suppressComposite)
File ".../matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File ".../mpl_toolkits/mplot3d/axes3d.py", line 281, in draw
reverse=True)):
File ".../mpl_toolkits/mplot3d/axes3d.py", line 280, in <lambda>
key=lambda col: col.do_3d_projection(renderer),
File ".../mpl_toolkits/mplot3d/art3d.py", line 226, in do_3d_projection
self._segments3d]
File ".../mpl_toolkits/mplot3d/art3d.py", line 225, in <listcomp>
proj3d.proj_trans_points(points, renderer.M) for points in
File ".../mpl_toolkits/mplot3d/proj3d.py", line 188, in proj_trans_points
xs, ys, zs = zip(*points)
ValueError: not enough values to unpack (expected 3, got 0)
Here's an example of the problem. This version works. Uncomment one or both of the calls to ax.contour() in the plot() function to see the weird contours, or more likely, the exception.
import math
import sys
import matplotlib as mpl
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d
import numpy as np
import scipy.spatial
#np.random.seed(4)
numPts = 1000 # number of points in cloud
scatter = False # adds scatter plot to show point cloud
def main():
(pts, f) = mkData() # create the point cloud
tris = mkTris(pts) # triangulate it
plot(pts, tris, f) # plot it
def plot(pts, tris, f):
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
cmap = plt.get_cmap("coolwarm")
collec = ax.plot_trisurf(tris, pts[:, 2], cmap=cmap)
colors = np.mean(f[tris.triangles], axis=1)
collec.set_array(colors)
collec.autoscale()
(xr, yr, zr, xMin, yMin, zMin, zMax) = resample(ax, tris, f)
ax.set_zlim(zMin, zMax) # default always includes zero for some reason
#
# Uncomment one or both of these lines to see the problem.
#
ax.contour(xr, yr, zr, 10, zdir="z", cmap=cmap, offset=zMin)
#ax.contour(xr, yr, zr, 10, zdir="x", cmap=cmap, offset=xMin)
#ax.contour(xr, yr, zr, 10, zdir="y", cmap=cmap, offset=yMin)
if scatter:
ax.scatter(pts[:, 0], pts[:, 1], pts[:, 2], alpha=0.1)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
fig.colorbar(collec, shrink=0.5, aspect=5)
plt.show()
def mkData():
"""
Create a random point cloud near a random plane, and define a function on
the plane for colors and contours.
"""
offset = 1 # generate points near a unit square, xy-plane
pts = 2 * np.random.rand(numPts, 3) - 1
pts[:, 2] = offset * (2 * np.random.rand(numPts) - 1)
x = 2 * np.ravel(pts[:, 0])
y = 2 * np.ravel(pts[:, 1])
f = x * np.exp(-x**2 - y**2) # just some function for colors, contours
width = 100 # scale unit square to a larger rectangle
height = 20
pts[:, 0] *= width
pts[:, 1] *= height
(e1, e2, e3) =[2 * np.pi * np.random.rand() for _ in range(3)]
(c1, s1) = (math.cos(e1), math.sin(e1)) # rotate scaled rectangle
(c2, s2) = (math.cos(e2), math.sin(e2))
(c3, s3) = (math.cos(e3), math.sin(e3))
Ta2b = np.array(( # do not have scipy.spatial.transform
[ c1 *c2, s2, -s1 * c2],
[s1 * s3 - c1 * s2 * c3, c2 * c3, c1 *s3 + s1 * s2 * c3],
[s1 * c3 + c1 * s2 * s3, -c2 * s3, c1 *c3 - s1 * s2 * s3]))
pts = (Ta2b # pts.T).T
dist = 500 # translate away from the origin
Ra2bNb = dist * (2 * np.random.rand(3, 1) - 1)
pts += Ra2bNb.T
return (pts, f)
def mkTris(pts): # triangulate the point cloud
u = np.ravel(pts[:, 0])
v = np.ravel(pts[:, 1])
try:
return mpl.tri.Triangulation(u, v)
except (ValueError, RuntimeError) as ex:
sys.exit(f"Unable to compute triangulation: {ex}.")
def resample(ax, tris, f): # resample triangulation onto a regular grid
(xMin, xMax) = ax.get_xlim()
(yMin, yMax) = ax.get_ylim()
(zMin, zMax) = ax.get_zlim()
x = np.linspace(xMin, xMax, 30)
y = np.linspace(yMin, yMax, 30)
(xm, ym)=np.meshgrid(x, y)
interp = mpl.tri.triinterpolate.LinearTriInterpolator(tris, f)
zm = interp(xm, ym)
return (xm, ym, zm, xMin, yMin, zMin, zMax)
if __name__ == "__main__":
main()
This is with matplotlib 2.2.2 and 3.1.1. Thanks for any help you can provide to get contours on all three planes, like the demo.
Jim
A matplotlib developer pointed out that the resampling was wrong. After fixing that, here's the corrected plot.
For coordinate planes that see the data edge-on, like the yz-plane in this case, the contours can look a little wonky. That's expected, since the data can approach being multi-valued. The xz-plane contours look pretty ragged too. I suspect both problems would improve by triangulating and contouring each plane individually, instead of favoring the xy-plane as done here.
Here's the fixed test script. The only important changes were in plot() and resample().
import math
import sys
import matplotlib as mpl
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d
import numpy as np
import scipy.spatial
#np.random.seed(4)
numPts = 1000 # number of points in cloud
numGrid = 120 # number of points in meshgrid used in resample for contours
scatter = False # adds scatter plot to show point cloud
def main():
(pts, f) = mkData() # create the point cloud
tris = mkTris(pts) # triangulate it
plot(pts, tris, f) # plot it
def plot(pts, tris, f):
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
cmap = plt.get_cmap("coolwarm")
collec = ax.plot_trisurf(tris, pts[:, 2], cmap=cmap)
colors = np.mean(f[tris.triangles], axis=1)
collec.set_array(colors)
collec.autoscale()
(xr, yr, zr, fr, xMin, xMax, yMin, yMax, zMin, zMax) = resample(ax, tris,
pts, f)
ax.set_xlim(xMin, xMax) # default always includes zero for some reason
ax.set_ylim(yMin, yMax)
ax.set_zlim(zMin, zMax)
ax.contour(xr, yr, fr, 10, zdir="z", cmap=cmap, offset=zMin)
ax.contour(fr, yr, zr, 10, zdir="x", cmap=cmap, offset=xMin)
ax.contour(xr, fr, zr, 10, zdir="y", cmap=cmap, offset=yMax)
if scatter:
ax.scatter(pts[:, 0], pts[:, 1], pts[:, 2], alpha=0.1)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
fig.colorbar(collec, shrink=0.5, aspect=5)
plt.show()
def mkData():
"""
Create a random point cloud near a random plane, and define a function on
the plane for colors and contours.
"""
offset = 1 # generate points near a unit square, xy-plane
pts = 2 * np.random.rand(numPts, 3) - 1
pts[:, 2] = offset * (2 * np.random.rand(numPts) - 1)
x = 2 * np.ravel(pts[:, 0])
y = 2 * np.ravel(pts[:, 1])
f = x * np.exp(-x**2 - y**2) # just some function for colors, contours
width = 100 # scale unit square to a larger rectangle
height = 20
pts[:, 0] *= width
pts[:, 1] *= height
(e1, e2, e3) =[2 * np.pi * np.random.rand() for _ in range(3)]
(c1, s1) = (math.cos(e1), math.sin(e1)) # rotate scaled rectangle
(c2, s2) = (math.cos(e2), math.sin(e2))
(c3, s3) = (math.cos(e3), math.sin(e3))
Ta2b = np.array(( # do not have scipy.spatial.transform
[ c1 *c2, s2, -s1 * c2],
[s1 * s3 - c1 * s2 * c3, c2 * c3, c1 *s3 + s1 * s2 * c3],
[s1 * c3 + c1 * s2 * s3, -c2 * s3, c1 *c3 - s1 * s2 * s3]))
pts = (Ta2b # pts.T).T
dist = 500 # translate away from the origin
Ra2bNb = dist * (2 * np.random.rand(3, 1) - 1)
pts += Ra2bNb.T
return (pts, f)
def mkTris(pts):
"Triangulate the point cloud."
u = np.ravel(pts[:, 0])
v = np.ravel(pts[:, 1])
try:
return mpl.tri.Triangulation(u, v)
except (ValueError, RuntimeError) as ex:
sys.exit(f"Unable to compute triangulation: {ex}.")
def resample(ax, tris, pts, f):
"Resample the triangulation onto a regular grid for contours."
(xMin, xMax) = ax.get_xlim()
(yMin, yMax) = ax.get_ylim()
(zMin, zMax) = ax.get_zlim()
x = np.linspace(xMin, xMax, numGrid)
y = np.linspace(yMin, yMax, numGrid)
(xm, ym)=np.meshgrid(x, y)
fInterp = mpl.tri.CubicTriInterpolator(tris, f)
fm = fInterp(xm, ym)
zInterp = mpl.tri.CubicTriInterpolator(tris, pts[:,2])
zm = zInterp(xm, ym)
return (xm, ym, zm, fm, xMin, xMax, yMin, yMax, zMin, zMax)
if __name__ == "__main__":
main()
I wanted to rotate Rectangles with matplotlib, but the normal patch always rotates around the lower left corner of the Rectangle. Is there a way to describe more general transformations? For Example I want to rotate about the mid point on the shorter side of the rectangle, so that the result looks like the motion of a clock hand.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
height = 0.1
width = 1
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim([-width * 1.2, width * 1.2])
ax.set_ylim([-width * 1.2, width * 1.2])
ax.plot(0, 0, color='r', marker='o', markersize=10)
for deg in range(0, 360, 45):
rec = Rectangle((0, 0), width=width, height=height,
angle=deg, color=str(deg / 360), alpha=0.9)
ax.add_patch(rec)
Same as the other answer, just without subclassing and using private attributes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
height = 0.1
width = 1
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim([-width * 1.2, width * 1.2])
ax.set_ylim([-width * 1.2, width * 1.2])
ax.plot(0, 0, color='r', marker='o', markersize=10)
point_of_rotation = np.array([0, height/2]) # A
# point_of_rotation = np.array([width/2, height/2]) # B
# point_of_rotation = np.array([width/3, height/2]) # C
# point_of_rotation = np.array([width/3, 2*height]) # D
for deg in range(0, 360, 45):
rec = plt.Rectangle(-point_of_rotation, width=width, height=height,
color=str(deg / 360), alpha=0.9,
transform=Affine2D().rotate_deg_around(*(0,0), deg)+ax.transData)
ax.add_patch(rec)
plt.show()
I created a custom Rectangle class which has as additional argument the relative point of rotation - relative means measured in the frame defined by the original lower left coordinates of the rectangle (x0, y0) and the angle.
rec = RotatingRectangle((0, 0), width=1, height=0.1, rel_point_of_rot=(0, 0.05), angle=45)
creates an rectangle where the the relative point (0, 0.05) [the midpoint of the left side] is shifted to the center (0, 0) and than a rotation of 45 deg is performed around this point.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
class RotatingRectangle(Rectangle):
def __init__(self, xy, width, height, rel_point_of_rot, **kwargs):
super().__init__(xy, width, height, **kwargs)
self.rel_point_of_rot = rel_point_of_rot
self.xy_center = self.get_xy()
self.set_angle(self.angle)
def _apply_rotation(self):
angle_rad = self.angle * np.pi / 180
m_trans = np.array([[np.cos(angle_rad), -np.sin(angle_rad)],
[np.sin(angle_rad), np.cos(angle_rad)]])
shift = -m_trans # self.rel_point_of_rot
self.set_xy(self.xy_center + shift)
def set_angle(self, angle):
self.angle = angle
self._apply_rotation()
def set_rel_point_of_rot(self, rel_point_of_rot):
self.rel_point_of_rot = rel_point_of_rot
self._apply_rotation()
def set_xy_center(self, xy):
self.xy_center = xy
self._apply_rotation()
height = 0.1
width = 1
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim([-width * 1.2, width * 1.2])
ax.set_ylim([-width * 1.2, width * 1.2])
ax.plot(0, 0, color='r', marker='o', markersize=10)
point_of_rotation = np.array([0, height/2]) # A
# point_of_rotation = np.array([width/2, height/2]) # B
# point_of_rotation = np.array([width/3, height/2]) # C
# point_of_rotation = np.array([width/3, 2*height]) # D
for deg in range(0, 360, 45):
rec = RotatingRectangle((0, 0), width=width, height=height,
rel_point_of_rot=point_of_rotation,
angle=deg, color=str(deg / 360), alpha=0.9)
ax.add_patch(rec)
I'm just starting with pyqtgraph and I want to make 3d surface plots in spherical coordinates. I've taken a look at the example GLSurfacePlot.py from the documentation but there are only plots in cartesian coordinates.
This is the plot I want to make (it's a half wave dipole radiation pattern):
How to plot r(theta, phi) with pyqtgraph?
EDIT: I could do it with matplotlib mplot3d, here is the script:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
k = 2*np.pi
long = 0.5
theta = np.linspace(0, np.pi, 361)
phi = np.linspace(0, 2*np.pi, 361)
PHI, THETA = np.meshgrid(phi, theta)
R = np.absolute((np.cos(k*long/2*np.cos(THETA))-np.cos(k*long/2))/np.sin(THETA))
R = np.nan_to_num(R)
X = R * np.sin(THETA) * np.cos(PHI)
Y = R * np.sin(THETA) * np.sin(PHI)
Z = R * np.cos(THETA)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
cmap = plt.get_cmap('jet')
plot = ax.plot_surface(X, Y, Z, rstride=10, cstride=10, facecolors=cmap(R),linewidth=0, antialiased=False, alpha=1)
plt.show()
The problem is that it's too slow when rotating and zooming it, and I definitely need that feature for my application, that's why I'm trying to do it with pyqtgraph.
Drawing this type of equations is not possible through GLSurfacePlotItem, in this case you must use GLMeshItem, but for this you must create an appropriate MeshData, so it takes as a reference sphere obtaining the following function:
def DipoleData(rows, cols, func, args=None):
verts = np.empty((rows+1, cols, 3), dtype=float)
phi = (np.arange(rows+1) * 2*np.pi *(1+2/rows)/ rows).reshape(rows+1, 1)
th = ((np.arange(cols) * np.pi / cols).reshape(1, cols))
if args is not None:
r = func(th, phi, *args)
else:
r = func(th, phi)
s = r* np.sin(th)
verts[...,2] = r * np.cos(th)
verts[...,0] = s * np.cos(phi)
verts[...,1] = s * np.sin(phi)
verts = verts.reshape((rows+1)*cols, 3)[cols-1:-(cols-1)] ## remove redundant vertexes from top and bottom
faces = np.empty((rows*cols*2, 3), dtype=np.uint)
rowtemplate1 = ((np.arange(cols).reshape(cols, 1) + np.array([[0, 1, 0]])) % cols) + np.array([[0, 0, cols]])
rowtemplate2 = ((np.arange(cols).reshape(cols, 1) + np.array([[0, 1, 1]])) % cols) + np.array([[cols, 0, cols]])
for row in range(rows):
start = row * cols * 2
faces[start:start+cols] = rowtemplate1 + row * cols
faces[start+cols:start+(cols*2)] = rowtemplate2 + row * cols
faces = faces[cols:-cols] ## cut off zero-area triangles at top and bottom
## adjust for redundant vertexes that were removed from top and bottom
vmin = cols-1
faces[faces<vmin] = vmin
faces -= vmin
vmax = verts.shape[0]-1
faces[faces>vmax] = vmax
return gl.MeshData(vertexes=verts, faces=faces)
It is then used in the following example:
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 3
w.show()
w.setWindowTitle('Half Wave Dipole Radiation Pattern')
def r_theta_phi(theta, phi, k, l):
return np.absolute((np.cos((k*l/2)*np.cos(theta)) -np.cos(k*l/2))/np.sin(theta))
p = 2*np.pi
q = 0.5
md = DipoleData(100, 100, r_theta_phi, args=(p, q))
colors = np.ones((md.faceCount(), 4), dtype=float)
colors[:,0] = np.linspace(0.1, 0.2, colors.shape[0])
colors[:,1] = np.linspace(0.2, 0.9, colors.shape[0])
colors[:,2] = np.linspace(0.0, 0.1, colors.shape[0])
md.setFaceColors(colors)
m = gl.GLMeshItem(meshdata=md, smooth=False)
w.addItem(m)
ax = gl.GLAxisItem()
ax.setSize(100,100,100)
w.addItem(ax)
g = gl.GLGridItem()
g.scale(0.2, 0.2, 0.2)
w.addItem(g)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Obtaining what is shown in the following image:
I've currently constructed a plot using rectangle Patches to display a sequence of positions.
EDIT: Code used to generate this (built off of the RLPy library)-
def visualize_trajectory(self, trajectory=[[0,0,0,0], [0.1,0.1,0,0]]):
domain_fig = plt.figure()
for i, s in enumerate(trajectory):
x, y, speed, heading = s[:4]
car_xmin = x - self.REAR_WHEEL_RELATIVE_LOC
car_ymin = y - self.CAR_WIDTH / 2.
car_fig = matplotlib.patches.Rectangle(
[car_xmin,
car_ymin],
self.CAR_LENGTH,
self.CAR_WIDTH,
alpha=(0.8 * i) / len(trajectory) )
rotation = Affine2D().rotate_deg_around(
x, y, heading * 180 / np.pi) + plt.gca().transData
car_fig.set_transform(rotation)
plt.gca().add_patch(car_fig)
Is there any way to overlay each of these patches with images? Ideally, there would be a car image instead of a rectangle at each of the positions.
I've played around with AnnotationBbox and TransformedBbox, but both seem to be inflexible when dealing with rotations.
Take a look at
demo_affine_image
from the matplotlib gallery. It shows how
to rotate an image.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
import matplotlib.cbook as cbook
def get_image():
fn = cbook.get_sample_data("necked_tensile_specimen.png")
arr = plt.imread(fn)
# make background transparent
# you won't have to do this if your car image already has a transparent background
mask = (arr == (1,1,1,1)).all(axis=-1)
arr[mask] = 0
return arr
def imshow_affine(ax, z, *args, **kwargs):
im = ax.imshow(z, *args, **kwargs)
x1, x2, y1, y2 = im.get_extent()
im._image_skew_coordinate = (x2, y1)
return im
N = 7
x = np.linspace(0, 1, N)
y = x**1.1
heading = np.linspace(10, 90, N)
trajectory = list(zip(x, y, heading))
width, height = 0.3, 0.3
car = get_image()
fig, ax = plt.subplots()
for i, t in enumerate(trajectory, start=1):
xi, yi, deg = t
im = imshow_affine(ax, car, interpolation='none',
extent=[0, width, 0, height], clip_on=True,
alpha=0.8*i/len(trajectory))
center_x, center_y = width//2, height//2
im_trans = (mtransforms.Affine2D()
.rotate_deg_around(center_x, center_y, deg)
.translate(xi, yi)
+ ax.transData)
im.set_transform(im_trans)
ax.set_xlim(-0.5, 1.5)
ax.set_ylim(-0.5, 1.7)
plt.show()