I am creating a circle and want to then develop a right-angle triangle, or any isometric form of a triangle. Whereby, I can take any line distance between two edges of the circle and draw arrows toward the peak point.
For example:
import numpy as np
import matplotlib.pyplot as plt
import math
theta = np.linspace(0, 2*np.pi, 100)
x1 = np.cos(theta)
y1 = np.sin(theta)
plt.plot(x1, y1)
for i in [min(y1), max(y1)]:
plt.plot(0, i, '-ok', mfc='C1', mec='C1')
plt.arrow(0,min(y1),0,2*max(y1),width=0.001,color='red',head_starts_at_zero=False)
plt.arrow(min(x1), (1/2)*min(y1), 2*max(x1), (1/2)*max(y1),width=0.001,color='red',head_starts_at_zero=False)
However, I cannot accurately get the distance between two points correct when i aim for any form of a triangle.
However, I can easily achieve it when setting y to 0 in the second arrow. Perhaps there is a general equation to do this?
Like this:
import numpy as np
import matplotlib.pyplot as plt
import math
theta = np.linspace(0, 2*np.pi, 100)
x1 = np.cos(theta)
y1 = np.sin(theta)
plt.plot(x1, y1)
x = np.array([0,120,240,0])
y = np.array([0,120,240,0])
x = np.cos( x * np.pi / 180 )
y = np.sin( y * np.pi / 180 )
plt.plot( x, y, color='red' )
plt.show()
Output:
In fact, if you choose ANY three angles, you'll get an inscribed triangle.
Related
The normal vector is calculated with the cross product of two vectors on the plane, so it shoud be perpendicular to the plane. But as you can seein the plot the normal vector produced with quiver isn't perpendicular.
Is the calculation of the plane wrong, my normal vector or the way i plot the normal vector?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
points = [[3.2342, 1.8487, -1.8186],
[2.9829, 1.6434, -1.8019],
[3.4247, 1.5550, -1.8093]]
p0, p1, p2 = points
x0, y0, z0 = p0
x1, y1, z1 = p1
x2, y2, z2 = p2
ux, uy, uz = u = [x1-x0, y1-y0, z1-z0] #first vector
vx, vy, vz = v = [x2-x0, y2-y0, z2-z0] #sec vector
u_cross_v = [uy*vz-uz*vy, uz*vx-ux*vz, ux*vy-uy*vx] #cross product
point = np.array(p1)
normal = np.array(u_cross_v)
d = -point.dot(normal)
print('plane equation:\n{:1.4f}x + {:1.4f}y + {:1.4f}z + {:1.4f} = 0'.format(normal[0], normal[1], normal[2], d))
xx, yy = np.meshgrid(range(10), range(10))
z = (-normal[0] * xx - normal[1] * yy - d) * 1. / normal[2]
# plot the surface
plt3d = plt.figure().gca(projection='3d')
plt3d.quiver(x0, y0, z0, normal[0], normal[1], normal[2], color="m")
plt3d.plot_surface(xx, yy, z)
plt3d.set_xlabel("X", color='red', size=18)
plt3d.set_ylabel("Y", color='green', size=18)
plt3d.set_zlabel("Z", color='b', size=18)
plt.show()
Actually, your plot is 100% correct. The scale of your Z axis does not correspond to the same scale on X & Y axis. If you use a function to set the scale correct, you can see that:
...
plt3d.set_zlabel("Z", color='b', size=18)
# insert these lines
ax = plt.gca()
set_axis_equal(ax)
plt.show()
and the corresponding function from this post:
def set_axes_radius(ax, origin, radius):
'''
From StackOverflow question:
https://stackoverflow.com/questions/13685386/
'''
ax.set_xlim3d([origin[0] - radius, origin[0] + radius])
ax.set_ylim3d([origin[1] - radius, origin[1] + radius])
ax.set_zlim3d([origin[2] - radius, origin[2] + radius])
def set_axes_equal(ax, zoom=1.):
'''
Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect("equal") and ax.axis("equal") not working for 3D.
input:
ax: a matplotlib axis, e.g., as output from plt.gca().
'''
limits = np.array([
ax.get_xlim3d(),
ax.get_ylim3d(),
ax.get_zlim3d(),
])
origin = np.mean(limits, axis=1)
radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0])) / zoom
set_axes_radius(ax, origin, radius)
I want to scatter a lot of datapoints around a centre one (2.5,2.5) based on a given distance for each datapoint to the centre.
How do I do that and also evade duplicates/scatter them evenly around the centre?
Thanks in advance
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(6, 6))
N = 120
angles = np.linspace(0, 2 * np.pi, N)
c_x, c_y = (2.5, 2.5)
x_s, y_s = [], []
distances = list(np.arange(0, 5.5, 0.5))
for distance in distances:
for angle in angles:
x_s.append(c_x + distance * np.cos(angle))
y_s.append(c_y + distance * np.sin(angle))
plt.scatter(x_s, y_s, c="b", s=4)
plt.show()
To clarify, I wanted one point for each distance, and then the next one offset by 180 or 90 degrees. But I managed to complete it based on the code provided by Gustav Rasmussen:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(6, 6))
#default
N = 50
angles = np.linspace(0, 2 * np.pi, N)
c_x, c_y = (2.5, 2.5)
x_s, y_s = [], []
distances = list(np.arange(0, 5.5, 0.01))
i = angles.size/4
for distance in distances:
x_s.append(c_x + distance * np.cos(i))
y_s.append(c_y + distance * np.sin(i))
i += i
plt.scatter(x_s, y_s, c="b", s=4)
plt.show()
Here we can see 550 distances, displayed with the next one being displayed offset by approximately 90 degrees.
Last mention: When dealing with a dataset of bigger deviations it is better to do i = angles.size/2 as to keep the output somewhat circled
import cmath
import numpy as np
from matplotlib import pyplot as plt
from itertools import starmap
c = np.array(list(starmap(cmath.rect, [(v//40+1, v*np.pi/20) for v in range(120)])))
x = c.real+2.5
y = c.imag+2.5
plt.scatter(x, y)
I want to plot a quantity which is given on a parametric surface in 3d space (for example the temperature distribution on a sphere). I can plot a parametric 3D plot of the sphere (as a function of the two parameters phi and theta) but I don't know how to make the colors of the polygons making up the sphere depend on the parameters theta and phi (normally, the color of a polygon is simply determined by the z-Position of the polygon).
Here's a basic example which plots a torus with colormap:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
angle = np.linspace(0, 2 * np.pi, 32)
theta, phi = np.meshgrid(angle, angle)
r, R = .25, 1.
X = (R + r * np.cos(phi)) * np.cos(theta)
Y = (R + r * np.cos(phi)) * np.sin(theta)
Z = r * np.sin(phi)
# Display the mesh
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.set_xlim3d(-1, 1)
ax.set_ylim3d(-1, 1)
ax.set_zlim3d(-1, 1)
ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1,cmap="hot")
plt.show()
However, the colors of the files are given by the z position of the tile, I want the color to be given by a function f(x,y).
Does anyone know how I can achieve this dependency in Matplotlib?
Thanks very much!
Ok, if anyone else is looking for a solution to this problem here's a possible solution:
The colors of the individual faces making up the surface plot can be set using the keyword argument facecolors. The following code will use the function X**2+Y**2 for coloring the faces of the parametric surface:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors as mcolors
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
# Generate torus mesh
angle = np.linspace(0, 2 * np.pi, 32)
theta, phi = np.meshgrid(angle, angle)
r, R = .25, 1.
X = (R + r * np.cos(phi)) * np.cos(theta)
Y = (R + r * np.cos(phi)) * np.sin(theta)
Z = r * np.sin(phi)
colorfunction=(X**2+Y**2)
norm=mcolors.Normalize(colorfunction.min(),colorfunction.max())
# Display the mesh
fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(projection='3d')
ax.set_xlim3d(-1, 1)
ax.set_ylim3d(-1, 1)
ax.set_zlim3d(-1, 1)
ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1, facecolors=cm.jet(norm(colorfunction)))
plt.show()
The following formula is used to classify points from a 2-dimensional space:
f(x1,x2) = np.sign(x1^2+x2^2-.6)
All points are in space X = [-1,1] x [-1,1] with a uniform probability of picking each x.
Now I would like to visualize the circle that equals:
0 = x1^2+x2^2-.6
The values of x1 should be on the x-axis and values of x2 on the y-axis.
It must be possible but I have difficulty transforming the equation to a plot.
You can use a contour plot, as follows (based on the examples at http://matplotlib.org/examples/pylab_examples/contour_demo.html):
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1.0, 1.0, 100)
y = np.linspace(-1.0, 1.0, 100)
X, Y = np.meshgrid(x,y)
F = X**2 + Y**2 - 0.6
plt.contour(X,Y,F,[0])
plt.show()
This yields the following graph
Lastly, some general statements:
x^2 does not mean what you think it does in python, you have to use x**2.
x1 and x2 are terribly misleading (to me), especially if you state that x2 has to be on the y-axis.
(Thanks to Dux) You can add plt.gca().set_aspect('equal') to make the figure actually look circular, by making the axis equal.
The solution of #BasJansen certainly gets you there, it's either very inefficient (if you use many grid points) or inaccurate (if you use only few grid points).
You can easily draw the circle directly. Given 0 = x1**2 + x**2 - 0.6 it follows that x2 = sqrt(0.6 - x1**2) (as Dux stated).
But what you really want to do is to transform your cartesian coordinates to polar ones.
x1 = r*cos(theta)
x2 = r*sin(theta)
if you use these substitions in the circle equation you will see that r=sqrt(0.6).
So now you can use that for your plot:
import numpy as np
import matplotlib.pyplot as plt
# theta goes from 0 to 2pi
theta = np.linspace(0, 2*np.pi, 100)
# the radius of the circle
r = np.sqrt(0.6)
# compute x1 and x2
x1 = r*np.cos(theta)
x2 = r*np.sin(theta)
# create the figure
fig, ax = plt.subplots(1)
ax.plot(x1, x2)
ax.set_aspect(1)
plt.show()
Result:
How about drawing x-values and calculating the corresponding y-values?
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100, endpoint=True)
y = np.sqrt(-x**2. + 0.6)
plt.plot(x, y)
plt.plot(x, -y)
produces
This can obviously be made much nicer, but this is only for demonstration...
# x**2 + y**2 = r**2
r = 6
x = np.linspace(-r,r,1000)
y = np.sqrt(-x**2+r**2)
plt.plot(x, y,'b')
plt.plot(x,-y,'b')
plt.gca().set_aspect('equal')
plt.show()
produces:
Plotting a circle using complex numbers
The idea: multiplying a point by complex exponential () rotates the point on a circle
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
num_pts=20 # number of points on the circle
ps = np.arange(num_pts+1)
# j = np.sqrt(-1)
pts = np.exp(2j*np.pi/num_pts *(ps))
fig, ax = plt.subplots(1)
ax.plot(pts.real, pts.imag , '-o')
ax.set_aspect(1)
plt.show()
I have the following problem:
a have N points on a sphere specified by a array x, with x.shape=(N,3). This array contains their cartesian coordinates. Furthermore, at each point, I have a specified temperature. This quantity is saved in an array T, with T.shape=(N,).
Is there any straight forward way to map this temperature distribution into the plane using different colors?
If it simplifies the task, the position can also be given in polar coordinates (\theta,\phi).
To plot your data, you can use Basemap. The only problem is, that both contour and contourf routines needs gridded data. Here is example with naive (and slow) IDW-like interpolation on sphere. Any comments are welcome.
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
def cart2sph(x, y, z):
dxy = np.sqrt(x**2 + y**2)
r = np.sqrt(dxy**2 + z**2)
theta = np.arctan2(y, x)
phi = np.arctan2(z, dxy)
theta, phi = np.rad2deg([theta, phi])
return theta % 360, phi, r
def sph2cart(theta, phi, r=1):
theta, phi = np.deg2rad([theta, phi])
z = r * np.sin(phi)
rcosphi = r * np.cos(phi)
x = rcosphi * np.cos(theta)
y = rcosphi * np.sin(theta)
return x, y, z
# random data
pts = 1 - 2 * np.random.rand(500, 3)
l = np.sqrt(np.sum(pts**2, axis=1))
pts = pts / l[:, np.newaxis]
T = 150 * np.random.rand(500)
# naive IDW-like interpolation on regular grid
theta, phi, r = cart2sph(*pts.T)
nrows, ncols = (90,180)
lon, lat = np.meshgrid(np.linspace(0,360,ncols), np.linspace(-90,90,nrows))
xg,yg,zg = sph2cart(lon,lat)
Ti = np.zeros_like(lon)
for r in range(nrows):
for c in range(ncols):
v = np.array([xg[r,c], yg[r,c], zg[r,c]])
angs = np.arccos(np.dot(pts, v))
idx = np.where(angs == 0)[0]
if idx.any():
Ti[r,c] = T[idx[0]]
else:
idw = 1 / angs**2 / sum(1 / angs**2)
Ti[r,c] = np.sum(T * idw)
# set up map projection
map = Basemap(projection='ortho', lat_0=45, lon_0=15)
# draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
# compute native map projection coordinates of lat/lon grid.
x, y = map(lon, lat)
# contour data over the map.
cs = map.contourf(x, y, Ti, 15)
plt.title('Contours of T')
plt.show()
One way to do this is to set facecolors by mapping your heat data through the colormap.
Here's an example:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
u = np.linspace(0, 2 * np.pi, 80)
v = np.linspace(0, np.pi, 80)
# create the sphere surface
x=10 * np.outer(np.cos(u), np.sin(v))
y=10 * np.outer(np.sin(u), np.sin(v))
z=10 * np.outer(np.ones(np.size(u)), np.cos(v))
# simulate heat pattern (striped)
myheatmap = np.abs(np.sin(y))
ax.plot_surface(x, y, z, cstride=1, rstride=1, facecolors=cm.hot(myheatmap))
plt.show()
Here, my "heatmap" is just stripes along the y-axis, which I made using the function np.abs(np.sin(y)), but anything that goes form 0 to 1 will work (and, of course, it needs to match the shapes on x, etc.