Get coordinates of intersection with Matplotlib - python

I want to get all coordinates where two graphs intersect in Matplotlib. I tried to work with DataFrames but did not come to any results.
In this example i drew a circle and a line (for simplicity). And I would want to have a some form coordinates where the line enters the circle and leaves it.
import numpy as np
from matplotlib import pyplot as plt
import pandas as pd
r = 1
n = 64
t = np.linspace(0, 2 * np.pi, n + 1)
x_circle = r * np.cos(t) + 1
y_circle = r * np.sin(t) + 1
df = pd.DataFrame({'x':x, 'y':y})
plt.plot(df['x'], df['y'])
plt.plot(range(4), np.array(range(4))*0.6)
plt.show()

The essence of this problem is to obtain the point(s) of intersection between 2 geometries. Then to create a plot to visualize the result. There are several python's modules that can handlle this problem. I choose shapely to demonstrate the steps to get the result. Matplotlib is used to plot, not to determine the intersection. Here is the code and the output plot. Note that I avoid using pandas because it is not relevant.
import matplotlib.pyplot as plt
import numpy as np
from shapely import wkt
from shapely.geometry import LineString
from shapely.wkt import loads
# prep data
r = 1
n = 64
t = np.linspace(0, 2 * np.pi, n + 1)
x_circle = r * np.cos(t) + 1
y_circle = r * np.sin(t) + 1
# create the linestring of circle's perimeter
wktcode1 = "LINESTRING ("
for i,(x,y) in enumerate(zip(x_circle, y_circle)):
if i!=len(x_circle)-1:
wktcode1 += str(x)+" "+str(y)+", "
else:
wktcode1 += str(x)+" "+str(y)+")"
#print(wktcode1)
circle_perim = loads(wktcode1) #a geometry object
# create another geometry, for the line
wktcode2 = "LINESTRING ("
xs = range(4)
ys = np.array(range(4))*0.6
for i,(x,y) in enumerate(zip(xs, ys)):
if i!=len(range(4))-1:
wktcode2 += str(x)+" "+str(y)+", "
else:
wktcode2 += str(x)+" "+str(y)+")"
#print(wktcode2)
pass
line_str = loads(wktcode2) #a geometry object
# check if they are intersect
ixx = circle_perim.intersection(line_str)
# visualization of the intersection
# plot circle
plt.plot(x_circle, y_circle)
# plot line
plt.plot(range(4), np.array(range(4))*0.6)
if ixx:
# plot intersection points
for ea in ixx:
plt.scatter(*ea.xy)
print(*ea.xy)
plt.show()
The output plot:

To get points: Subtract x_circle and y_circle and compare it with 0.
To plot: Use plt.scatter() - documentation.
Full code:
import numpy as np
from matplotlib import pyplot as plt
import pandas as pd
r = 1
n = 64
t = np.linspace(0, 2 * np.pi, n + 1)
x_circle = r * np.cos(t) + 1
y_circle = r * np.sin(t) + 1
#get intersecting points
pts = np.reshape(np.where((np.array(x_circle, dtype = 'float16')-np.array(y_circle, dtype = 'float16') )== 0)[0], (1, -1))
pts = np.append(pts, np.array(x_circle)[pts], axis = 0)
print(pts)
plt.plot(x_circle)
plt.plot(y_circle)
plt.scatter(pts[0], pts[1], s = 200, marker='o')
#plt.plot(range(4), np.array(range(4))*0.6)
plt.show()
Note: Since all are float values, while calculating np.sin() and
np.cos(), it rounds off the last digit. Hence, while comparing, I
changed it into float16 values.

Related

Generate a new set of points along a line

I have a Python project where I need to redraw a line many times with the points in random places but keeping the line's shape and point count roughly the same. The final output will be using polygonal points and not Bezier paths (though I wouldn't be opposed to using Bezier as an intermediary step).
This animation is demonstrating how the points could move along the line to different positions while maintaining the general shape.
I also have a working example below where I'm moving along the line and picking random new points between existing points (the red line, below). It works okay, but I'd love to hear some other approaches I might take if someone knows of a better one?
Though this code is using matplotlib to demonstrate the line, the final program will not.
import numpy as np
from matplotlib import pyplot as plt
import random
from random import (randint,uniform)
def move_along_line(p1, p2, scalar):
distX = p2[0] - p1[0]
distY = p2[1] - p1[1]
modX = (distX * scalar) + p1[0]
modY = (distY * scalar) + p1[1]
return [modX, modY]
x_coords = [213.5500031,234.3809357,255.211853,276.0427856,296.8737183,317.7046204,340.1997681,364.3751221,388.5505066,414.8896484,444.5192261,478.5549622,514.5779419,545.4779053,570.3830566,588.0241699,598.2469482,599.772583,596.758728,593.7449341,590.7310791,593.373291,610.0373535,642.1326294,677.4451904,710.0697021,737.6887817,764.4020386,791.1152954,817.8284912,844.541687,871.2550049,897.9682007,924.6813965,951.3945923,978.1078491,1009.909546,1042.689941,1068.179199,1089.543091]
y_coords = [487.3099976,456.8832703,426.4565125,396.0297852,365.6030273,335.1763,306.0349426,278.1913452,250.3477478,224.7166748,203.0908051,191.2358704,197.6810608,217.504303,244.4946136,276.7698364,312.0551453,348.6885986,385.4395447,422.1904297,458.9414063,495.5985413,527.0128479,537.1477661,527.6642456,510.959259,486.6988525,461.2799683,435.8611145,410.4422913,385.023468,359.6045532,334.18573,308.7669067,283.3480835,257.929184,239.4429474,253.6099091,280.1803284,310.158783]
plt.plot(x_coords,y_coords,color='b')
plt.scatter(x_coords,y_coords,s=2)
new_line_x = []
new_line_y = []
for tgt in range(len(x_coords)-1):
#tgt = randint(0, len(x_coords)-1)
next_pt = tgt+1
new_pt = move_along_line([x_coords[tgt],y_coords[tgt]], [x_coords[next_pt],y_coords[next_pt]], uniform(0, 1))
new_line_x.append(new_pt[0])
new_line_y.append(new_pt[1])
plt.plot(new_line_x,new_line_y,color='r')
plt.scatter(new_line_x,new_line_y,s=10)
ax = plt.gca()
ax.set_aspect('equal')
plt.show()
Thank you very much!
I'm not sure if this is the most optimal way to do this but essentially you want to follow these steps:
Calculate the distance of the entire path, and the distance between all the points. Then for each point, tally the distances to that point.
Generate a new set of random points along the path starting with 0, then for each pair of points calculate a random distance: random value between 0 and 1 * total length of the path.
Sort these distances from smallest to largest.
For each random distance loop over the distances find the index where the random distance is > than distance i, and less than distance i+1. Interpolate new x and y values from these points.
from matplotlib import pyplot as plt
from scipy.interpolate import interp1d
import numpy
import random
import math
x_coords = [195.21,212.53,237.39,270.91,314.21,368.43,434.69,514.1,607.8,692.69,746.98,773.8,776.25,757.45,720.52,668.55,604.68,545.37,505.79,487.05,490.27,516.58,567.09,642.93,745.2,851.5,939.53,1010.54,1065.8,1106.58,1134.15,1149.75,1154.68]
y_coords = [195.34,272.27,356.59,438.98,510.14,560.76,581.52,563.13,496.27,404.39,318.83,242.15,176.92,125.69,91.02,75.48,81.62,113.49,168.57,239.59,319.29,400.38,475.6,537.67,579.32,586.78,558.32,504.7,436.69,365.05,300.55,253.95,236.03]
n_points = 100
x_coords = numpy.array(x_coords)
x_min = x_coords.min()
x_max = x_coords.max()
x_range = x_max - x_min
distances = []
tallied_distances = [0]
tallied_distance = 0
for i in range(0, len(x_coords) -1):
xi = x_coords[i]
xf = x_coords[i + 1]
yi= y_coords[i]
yf = y_coords[i+1]
d = math.sqrt((xf-xi)**2 + (yf-yi)**2)
tallied_distance += d
tallied_distances.append(tallied_distance)
random_distances_along_line = [0]
for i in range(0, n_points-2):
random_distances_along_line.append(random.random()*tallied_distance)
random_distances_along_line.sort()
new_x_points = [x_coords[0]]
new_y_points = [y_coords[0]]
for i in range(0, len(random_distances_along_line)):
dt = random_distances_along_line[i]
for j in range(0, len(tallied_distances)-1):
di = tallied_distances[j]
df = tallied_distances[j+1]
if di < dt and dt < df:
difference = dt - di
xi = x_coords[j]
xf = x_coords[j+1]
yi = y_coords[j]
yf = y_coords[j+1]
xt = xi+(xf-xi)*difference/(df-di)
yt = yi+(yf-yi)*difference/(df-di)
new_x_points.append(xt)
new_y_points.append(yt)
new_x_points.append(x_coords[len(x_coords)-1])
new_y_points.append(y_coords[len(y_coords)-1])
plt.plot(new_x_points, new_y_points)
plt.scatter(new_x_points, new_y_points,s=2)
ax = plt.gca()
ax.set_aspect('equal')
plt.show()

Remove Overlapping Circles by Keeping Only Largest

Given a set of circles with random centers and radii, I would like to be able to prune this set so that if overlap between circles occurs, only the largest circle is retained. This is a similar question to the one answered here, but the problem listed there seeks to retain the maximum number of non-overlapping circles, from what I understand. I'd like to be able to adapt the ILP solution given there to my needs, if possible, although a brute-force "search and remove"-type approach would be fine too. The latter is what I've tried so far, but failed to accomplish.
import matplotlib.pyplot as plt
from numpy.random import rand, seed
seed(1)
N = 25 # number of circles
L = 10 # domain size
Rmin = 0.5 # min radius
Rmax = 1 # max radius
cx = rand(N)*(L-2*Rmax) + Rmax
cy = rand(N)*(L-2*Rmax) + Rmax
r = rand(N)*(Rmax-Rmin) + Rmin
# Plotting
for i in range(N):
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='white'))
plt.axis('image')
plt.xlim(0,L)
plt.ylim(0,L)
plt.show()
Desired Result:
It got a bit messy, but this creates the Output you wanted.
import matplotlib.pyplot as plt
from numpy.random import rand, seed
import math
import numpy as np
import pandas as pd
def find_larger(df_circles_2, idx):
found_greater = False
for i,row in df_circles_2.iterrows():
if i != idx:
distance = math.sqrt( (row['x'] - df_circles_2['x'][idx])**2 + (row['y'] - df_circles_2['y'][idx])**2 )
if distance < (row['r'] + df_circles_2['r'][i]):
if row['r'] > df_circles_2['r'][idx] and (row['keep'] != "discard"):
if df_circles['keep'][i] == "keep":
return "discard"
found_greater = True
if found_greater:
return "undecided"
else:
return "keep"
seed(1)
N = 25 # number of circles
L = 10 # domain size
Rmin = 0.5 # min radius
Rmax = 1 # max radius
cx = rand(N)*(L-2*Rmax) + Rmax
cy = rand(N)*(L-2*Rmax) + Rmax
r = rand(N)*(Rmax-Rmin) + Rmin
# Plotting
for i in range(N):
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='white'))
plt.gca().add_artist(plt.Text(cx[i], cy[i], text = str(i)))
plt.axis('image')
plt.xlim(0,L)
plt.ylim(0,L)
plt.show()
# Searching:
df_circles = pd.DataFrame(np.array([cx, cy, r]).T, columns = ['x', 'y', 'r'])
df_circles['keep'] = "undecided"
while(df_circles['keep'].str.contains('undecided').any()):
for i, row in df_circles.iterrows():
if row['keep'] == "undecided":
df_circles.at[i, 'keep'] = find_larger(df_circles, i)
# Plotting 2
plt.figure(2)
for i in range(N):
if df_circles['keep'][i] == "keep":
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='black'))
else:
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='white'))
plt.axis('image')
plt.xlim(0,L)
plt.ylim(0,L)
plt.show()

Python Plot 3D Histogram Hexagon

I'm testing a TOF camera from Broadcom.
It has hexagonal pixels.
I wish to represent the histogram in 3D as in the utility of the constructor.
I tested the vedo library. But I can’t give the values in Z and reorder the cells and trace to the ground
from vedo import *
from vedo.pyplot import histogram
import numpy as np
N = 2000
x = np.random.randn(N) * 1.0
y = np.random.randn(N) * 1.5
# hexagonal binned histogram:
histo = histogram(x, y,
bins=100,
mode='hexbin',
xtitle="\sigma_x =1.0",
ytitle="\sigma_y =1.5",
ztitle="counts",
fill=True,
cmap='terrain',
)
# add a formula:
f = r'f(x, y)=A \exp \left(-\left(\frac{\left(x-x_{o}\right)^{2}}'
f+= r'{2 \sigma_{x}^{2}}+\frac{\left(y-y_{o}\right)^{2}}'
f+= r'{2 \sigma_{y}^{2}}\right)\right)'
formula = Latex(f, c='k', s=1.5).rotateX(90).rotateZ(90).pos(1.5,-2,1)
show(histo, formula, axes=1, viewup='z')
You can easily create it with e.g.
from vedo import *
import numpy as np
settings.defaultFont = "Theemim"
vals = np.abs(np.random.randn(8*4)) # heights
cols = colorMap(vals, "RdYlBu")
items = []
k = 0
for i in range(8):
for j in range(4):
val = vals[k]
col = cols[k]
x, y, z = [i+j%2/2, j-j%2/6, val+0.01]
hexa = Circle([x,y], r=0.55, res=6)
hbar = hexa.extrude(val) # create the hex bar
hbar.lighting("default").flat().c(col)
txt = Text3D(precision(val,3), [x,y,z], s=.12, justify='center', c='k')
items += [hbar, txt]
k += 1
show(items, axes=dict(xtitle="x-cell"))

python plot line link wrong point

As you can see matplotlib link few wrong points using plt.plot.
data file
How can I improve my curve ?
Can I give him the minimum interval beween two points ?
Will sorting my points help me get a good curve ?
I can sort my x-axis points but y-axis seems more complicated.
Thank you.
import numpy as np
from matplotlib import pyplot as plt
File = "crown" + str(0) + str(0.01)
data = np.loadtxt(File)
data = data.reshape((len(data),3))
print(data)
R0 = 10.
theta = np.arctan2(data[:,1],data[:,0]) #calcul of theta
print(theta)
Radius = np.sqrt(data[:,1]**2 + data[:,0]**2)
Mradius = Radius - np.mean(Radius)
Mradius = Mradius/R0
n = len(theta) # length
Radiushat = np.fft.fft(Mradius,n) #fft of the Radius
PSD = Radiushat * np.conj(Radiushat)/n #Power spectral density
delta = 0.0001
k = (1/(delta*n)) * np.arange(n) #wavenumber
indice = np.where(PSD==np.max(PSD)) #find position indice max PSD array
#print(k[indice][0]) #k_{max}
L = np.arange(1,np.floor(n/2), dtype='int')
# filtered the signal
"""
indices = PSD > np.max(PSD)/3.
PDSclean = PSD * indices
Radiushatclean = indices * Radiushat
ffilt = np.fft.ifft(Radiushatclean)"""
fig,axs = plt.subplots(2,1)
plt.sca(axs[0])
plt.plot(theta,Radius,label="Crown")
ticks = [-np.pi, -np.pi/2., 0, np.pi/2., np.pi]
ticks_labels = ['-π', '-π/2', '0', 'π/2', 'π']
plt.xticks(ticks, ticks_labels)
"""plt.plot(theta,ffilt, label="Filtered")"""
plt.xlabel("theta")
plt.ylabel("R/R(t=0)")
plt.legend()
plt.title('k_{max}=%.2f' % (k[indice][0]))
plt.sca(axs[1])
plt.plot(k[L],PSD[L]/np.max(PSD))
plt.xlim(0,k[L[-1]])
plt.xlabel("k")
plt.ylabel("PSD")
fig.tight_layout()
plt.show()

I am getting two plots for one data set in python

I am working through example 8.1 titled Euler's Method from Mark Newman's book Computational Physics. I rewrote the example as a method with Numpy arrays but when I plot it I get two plots on the same figure not sure how to correct it. Also is there better way to convert my 2 1D arrays into 1 2D array to use for plotting in Matplotlib, thanks.
Newman's example :
from math import sin
from numpy import arange
from pylab import plot,xlabel,ylabel,show
def f(x,t):
return -x**3 + sin(t)
a = 0.0 # Start of the interval
b = 10.0 # End of the interval
N = 1000 # Number of steps
h = (b-a)/N # Size of a single step
x = 0.0 # Initial condition
tpoints = arange(a,b,h)
xpoints = []
for t in tpoints:
xpoints.append(x)
x += h*f(x,t)
plot(tpoints,xpoints)
xlabel("t")
ylabel("x(t)")
show()
My modifications:
from pylab import plot,show,xlabel,ylabel
from numpy import linspace,exp,sin,zeros,vstack,column_stack
def f(x,t):
return (-x**(3) + sin(t))
def Euler(f,x0,a,b):
N=1000
h = (b-a)/N
t = linspace(a,b,N)
x = zeros(N,float)
y = x0
for i in range(N):
x[i] = y
y += h*f(x[i],t[i])
return column_stack((t,x)) #vstack((t,x)).T
plot(Euler(f,0.0,0.0,10.0))
xlabel("t")
ylabel("x(t)")
show()
The reason you get two lines is that t as well as x are plotted against their index, instead of x plotted against t
I don't see why you'd want to stack the two arrays. Just keep then separate, which will also solve the problem of the two plots.
The following works fine.
import numpy as np
import matplotlib.pyplot as plt
f = lambda x,t: -x**3 + np.sin(t)
def Euler(f,x0,a,b):
N=1000
h = (b-a)/N
t = np.linspace(a,b,N)
x = np.zeros(N,float)
y = x0
for i in range(N):
x[i] = y
y += h*f(x[i],t[i])
return t,x
t,x = Euler(f,0.0,0.0,10.0)
plt.plot(t,x)
plt.xlabel("t")
plt.ylabel("x(t)")
plt.show()

Categories

Resources