plotting spherical caps - python

Using Python, I would like to draw a sphere in one color and on that sphere a number of spherical caps. The caps are given by their center points (the angles phi, theta) and radii.
Is this possible at all, perhaps with MayaVi?

Using matplotlib:
def plot_spherical_cap(ax, b, opening_angle, radius=1.0):
r = elevation
phi = numpy.linspace(0, 2 * numpy.pi, 30)
theta = numpy.linspace(0, opening_angle, 20)
X = r * numpy.stack([
numpy.outer(numpy.cos(phi), numpy.sin(theta)),
numpy.outer(numpy.sin(phi), numpy.sin(theta)),
numpy.outer(numpy.ones(numpy.size(phi)), numpy.cos(theta)),
], axis=-1)
# rotate X such that [0, 0, 1] gets rotated to `c`;
# <https://math.stackexchange.com/a/476311/36678>.
a = numpy.array([0.0, 0.0, 1.0])
a_x_b = numpy.cross(a, b)
a_dot_b = numpy.dot(a, b)
if a_dot_b == -1.0:
X_rot = -X
else:
X_rot = (
X +
numpy.cross(a_x_b, X) +
numpy.cross(a_x_b, numpy.cross(a_x_b, X)) / (1.0 + a_dot_b)
)
ax.plot_surface(
X_rot[..., 0], X_rot[..., 1], X_rot[..., 2],
rstride=3, cstride=3,
color='#1f77b4',
alpha=0.5,
linewidth=0
)
return

Related

Deriving Cubic Bezier Curve control points & handles from series of points in Python

I am trying to find the control points and handles of a Cubic Bezier curve from a series of points. My current code is below (credit to Zero Zero on the Python Discord). The Cubic Spline is creating the desired fit, but the handles (in orange) are incorrect. How may I find the handles of this curve?
Thank you!
import numpy as np
import scipy as sp
def fit_curve(points):
# Fit a cubic bezier curve to the points
curve = sp.interpolate.CubicSpline(points[:, 0], points[:, 1], bc_type=((1, 0.0), (1, 0.0)))
# Get 4 control points for the curve
p = np.zeros((4, 2))
p[0, :] = points[0, :]
p[3, :] = points[-1, :]
p[1, :] = points[0, :] + 0.3 * (points[-1, :] - points[0, :])
p[2, :] = points[-1, :] - 0.3 * (points[-1, :] - points[0, :])
return p, curve
ypoints = [0.0, 0.03771681353260319, 0.20421680080883106, 0.49896111463402026, 0.7183501026981503, 0.8481517096346528, 0.9256128196832564, 0.9705404287079152, 0.9933297674379904, 1.0]
xpoints = [x for x in range(len(ypoints))]
points = np.array([xpoints, ypoints]).T
from scipy.interpolate import splprep, splev
tck, u = splprep([xpoints, ypoints], s=0)
#print(tck, u)
xnew, ynew = splev(np.linspace(0, 1, 100), tck)
# Plot the original points and the Bézier curve
import matplotlib.pyplot as plt
#plt.plot(xpoints, ypoints, 'x', xnew, ynew, xpoints, ypoints, 'b')
plt.axis([0, 10, -0.05, 1.05])
plt.legend(['Points', 'Bézier curve', 'True curve'])
plt.title('Bézier curve fitting')
# Get the curve
p, curve = fit_curve(points)
# Plot the points and the curve
plt.plot(points[:, 0], points[:, 1], 'o')
plt.plot(p[:, 0], p[:, 1], 'o')
plt.plot(np.linspace(0, 9, 100), curve(np.linspace(0, 9, 100)))
plt.show()
The answer for my case was a Bezier best fit function that accepts an input of point values, fits the points to a Cubic Spline, and outputs the Bézier handles of the curve by finding their coefficients.
Here is one such script, fitCurves, which can be used like so:
import numpy as np
from fitCurve import fitCurve
import matplotlib.pyplot as plt
y = [0.0,
0.03771681353260319,
0.20421680080883106,
0.49896111463402026,
0.7183501026981503,
0.8481517096346528,
0.9256128196832564,
0.9705404287079152,
0.9933297674379904,
1.0]
x = np.linspace(0, 1, len(y))
pts = np.array([x,y]).T
bezier_handles = fitCurve(points=pts , maxError=20)
x_bez = []
y_bez = []
for bez in bezier_handles:
for pt in bez:
x_bez.append(pt[0])
y_bez.append(pt[1])
plt.plot(pts[:,0], pts[:,1], 'bo-', label='Points')
plt.plot(x_bez[:2], y_bez[:2], 'ro--', label='Handle') # handle 1
plt.plot(x_bez[2:4], y_bez[2:4], 'ro--') # handle 2
plt.legend()
plt.show()
fitCurve.py
from numpy import *
""" Python implementation of
Algorithm for Automatically Fitting Digitized Curves
by Philip J. Schneider
"Graphics Gems", Academic Press, 1990
"""
# evaluates cubic bezier at t, return point
def q(ctrlPoly, t):
return (1.0-t)**3 * ctrlPoly[0] + 3*(1.0-t)**2 * t * ctrlPoly[1] + 3*(1.0-t)* t**2 * ctrlPoly[2] + t**3 * ctrlPoly[3]
# evaluates cubic bezier first derivative at t, return point
def qprime(ctrlPoly, t):
return 3*(1.0-t)**2 * (ctrlPoly[1]-ctrlPoly[0]) + 6*(1.0-t) * t * (ctrlPoly[2]-ctrlPoly[1]) + 3*t**2 * (ctrlPoly[3]-ctrlPoly[2])
# evaluates cubic bezier second derivative at t, return point
def qprimeprime(ctrlPoly, t):
return 6*(1.0-t) * (ctrlPoly[2]-2*ctrlPoly[1]+ctrlPoly[0]) + 6*(t) * (ctrlPoly[3]-2*ctrlPoly[2]+ctrlPoly[1])
# Fit one (ore more) Bezier curves to a set of points
def fitCurve(points, maxError):
leftTangent = normalize(points[1] - points[0])
rightTangent = normalize(points[-2] - points[-1])
return fitCubic(points, leftTangent, rightTangent, maxError)
def fitCubic(points, leftTangent, rightTangent, error):
# Use heuristic if region only has two points in it
if (len(points) == 2):
dist = linalg.norm(points[0] - points[1]) / 3.0
bezCurve = [points[0], points[0] + leftTangent * dist, points[1] + rightTangent * dist, points[1]]
return [bezCurve]
# Parameterize points, and attempt to fit curve
u = chordLengthParameterize(points)
bezCurve = generateBezier(points, u, leftTangent, rightTangent)
# Find max deviation of points to fitted curve
maxError, splitPoint = computeMaxError(points, bezCurve, u)
if maxError < error:
return [bezCurve]
# If error not too large, try some reparameterization and iteration
if maxError < error**2:
for i in range(20):
uPrime = reparameterize(bezCurve, points, u)
bezCurve = generateBezier(points, uPrime, leftTangent, rightTangent)
maxError, splitPoint = computeMaxError(points, bezCurve, uPrime)
if maxError < error:
return [bezCurve]
u = uPrime
# Fitting failed -- split at max error point and fit recursively
beziers = []
centerTangent = normalize(points[splitPoint-1] - points[splitPoint+1])
beziers += fitCubic(points[:splitPoint+1], leftTangent, centerTangent, error)
beziers += fitCubic(points[splitPoint:], -centerTangent, rightTangent, error)
return beziers
def generateBezier(points, parameters, leftTangent, rightTangent):
bezCurve = [points[0], None, None, points[-1]]
# compute the A's
A = zeros((len(parameters), 2, 2))
for i, u in enumerate(parameters):
A[i][0] = leftTangent * 3*(1-u)**2 * u
A[i][1] = rightTangent * 3*(1-u) * u**2
# Create the C and X matrices
C = zeros((2, 2))
X = zeros(2)
for i, (point, u) in enumerate(zip(points, parameters)):
C[0][0] += dot(A[i][0], A[i][0])
C[0][1] += dot(A[i][0], A[i][1])
C[1][0] += dot(A[i][0], A[i][1])
C[1][1] += dot(A[i][1], A[i][1])
tmp = point - q([points[0], points[0], points[-1], points[-1]], u)
X[0] += dot(A[i][0], tmp)
X[1] += dot(A[i][1], tmp)
# Compute the determinants of C and X
det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]
det_C0_X = C[0][0] * X[1] - C[1][0] * X[0]
det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]
# Finally, derive alpha values
alpha_l = 0.0 if det_C0_C1 == 0 else det_X_C1 / det_C0_C1
alpha_r = 0.0 if det_C0_C1 == 0 else det_C0_X / det_C0_C1
# If alpha negative, use the Wu/Barsky heuristic (see text) */
# (if alpha is 0, you get coincident control points that lead to
# divide by zero in any subsequent NewtonRaphsonRootFind() call. */
segLength = linalg.norm(points[0] - points[-1])
epsilon = 1.0e-6 * segLength
if alpha_l < epsilon or alpha_r < epsilon:
# fall back on standard (probably inaccurate) formula, and subdivide further if needed.
bezCurve[1] = bezCurve[0] + leftTangent * (segLength / 3.0)
bezCurve[2] = bezCurve[3] + rightTangent * (segLength / 3.0)
else:
# First and last control points of the Bezier curve are
# positioned exactly at the first and last data points
# Control points 1 and 2 are positioned an alpha distance out
# on the tangent vectors, left and right, respectively
bezCurve[1] = bezCurve[0] + leftTangent * alpha_l
bezCurve[2] = bezCurve[3] + rightTangent * alpha_r
return bezCurve
def reparameterize(bezier, points, parameters):
return [newtonRaphsonRootFind(bezier, point, u) for point, u in zip(points, parameters)]
def newtonRaphsonRootFind(bez, point, u):
"""
Newton's root finding algorithm calculates f(x)=0 by reiterating
x_n+1 = x_n - f(x_n)/f'(x_n)
We are trying to find curve parameter u for some point p that minimizes
the distance from that point to the curve. Distance point to curve is d=q(u)-p.
At minimum distance the point is perpendicular to the curve.
We are solving
f = q(u)-p * q'(u) = 0
with
f' = q'(u) * q'(u) + q(u)-p * q''(u)
gives
u_n+1 = u_n - |q(u_n)-p * q'(u_n)| / |q'(u_n)**2 + q(u_n)-p * q''(u_n)|
"""
d = q(bez, u)-point
numerator = (d * qprime(bez, u)).sum()
denominator = (qprime(bez, u)**2 + d * qprimeprime(bez, u)).sum()
if denominator == 0.0:
return u
else:
return u - numerator/denominator
def chordLengthParameterize(points):
u = [0.0]
for i in range(1, len(points)):
u.append(u[i-1] + linalg.norm(points[i] - points[i-1]))
for i, _ in enumerate(u):
u[i] = u[i] / u[-1]
return u
def computeMaxError(points, bez, parameters):
maxDist = 0.0
splitPoint = len(points)/2
for i, (point, u) in enumerate(zip(points, parameters)):
dist = linalg.norm(q(bez, u)-point)**2
if dist > maxDist:
maxDist = dist
splitPoint = i
return maxDist, splitPoint
def normalize(v):
return v / linalg.norm(v)

how to create a square on top of a given Vector. This square should be orthogonal to the given vector

Programming in Python (Blender):
I want to create a square and print all vertices (A;B;C;D) into my console on top of a given Vector. The square should be orthogonal to this vector, like this:
def create_verts_around_point(radius, vert):
# given Vector
vec = np.array([vert[0], vert[1], vert[2]])
# side_length of square
side_length = radius
# Vctor x-direction (1,0,0)
x_vec = np.array([1,0,0])
# Vekctor y-direction (0,1,0)
y_vec = np.array([0,1,0])
# Vector z-direction (0,0,1)
z_vec = np.array([0,0,1])
p1 = vec + (side_length/2) * x_vec + (side_length/2) * y_vec + (side_length/2) * z_vec
p2 = vec - (side_length/2) * x_vec + (side_length/2) * y_vec + (side_length/2) * z_vec
p3 = vec - (side_length/2) * x_vec - (side_length/2) * y_vec + (side_length/2) * z_vec
p4 = vec + (side_length/2) * x_vec - (side_length/2) * y_vec + (side_length/2) * z_vec
But my output looks like this in the end (Square is always parallel to my x-axis and y-axis):
I don't think you're really thinking about this problem in 3D, but see if this is close.
I create a square, perpendicular to the X axis. I then rotate that square based on the angles in x, y, and z. I then position the square at the end of the vector and plot it. I add plot points for the origin and the end of the vector, and I duplicate the last point in the square do it draws all the lines.
import math
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
def create_verts_around_point(sides, vert):
x0, y0, z0 = vert
# Here is the unrotated square.
half = sides/2
square = [
[0, -half,-half],
[0, -half, half],
[0, half, half],
[0, half,-half],
]
# Now find the rotation in each direction.
thetax = math.atan2( z0, y0 )
thetay = math.atan2( z0, x0 )
thetaz = math.atan2( y0, x0 )
# Now rotate the cube, first in x.
cubes = []
txcos = math.cos(thetax)
txsin = math.sin(thetax)
tycos = math.cos(thetay)
tysin = math.sin(thetay)
tzcos = math.cos(thetaz)
tzsin = math.sin(thetaz)
for x,y,z in square:
x,y,z = (x, y * txcos - z * txsin, y * txsin + z * txcos)
x,y,z = (x * txcos - z * txsin, y, x * txsin + z * txcos)
x,y,z = (x * txcos - y * txsin, x * txsin + y * txcos, z)
cubes.append( (x0+x, y0+y, z0+z) )
return cubes
point = (10,10,10)
square = create_verts_around_point(5, point)
points = [(0,0,0),point] + square + [square[0]]
x = [p[0] for p in points]
y = [p[1] for p in points]
z = [p[2] for p in points]
ax = plt.figure().add_subplot(111, projection='3d')
ax.plot( x, y, z )
plt.show()
Output:

Nullcline Plot for Nonlinear System of ODEs

I am attempting to plot the nullcline (steady state) curves of the Oregonator model to assert the existence of a limit cycle by applying the Poincare-Bendixson Theorem. I am close, but for some reason the plot that is produced shows two straight lines. I think it has something to do with the plotting stage. Any ideas?
Also any hints for how to construct a quadrilateral to apply the theorem with would be most appreciated.
Code:
import numpy as np
import matplotlib.pyplot as plt
# Dimensionless parameters
eps = 0.04
q = 0.0008
f = 1
# Oregonator model as numpy array
def Sys(Y, t = 0):
return np.array((Y[0] * (1 - Y[0] - ((Y[0] - q) * f * Y[1]) / (Y[0] + q)) / eps, Y[0] - Y[1] ))
# Oregonator model steady states
def g(x,z):
return (x * (1 - x) + ((q - x) * f * z) / (q + x)) / eps
def h(x,z):
return x - z
# Initial lists containing values
x = []
z = []
def sys(iv1, iv2, dt, time):
# initial values:
x.append(iv1)
z.append(iv2)
# Compute and fill lists
for i in range(time):
x.append(x[i] + (g(x[i],z[i])) * dt)
z.append(z[i] + (h(x[i],z[i])) * dt)
return x, z
sys(1, 0.5, 0.01, 30)
# Locate and find equilibrium points
eqp = []
def find_fixed_points(r):
for x in range(r):
for z in range(r):
if ((g(x, z) == 0) and (h(x, z) == 0)):
eqp.append((x,z))
return eqp
# Plot nullclines
plt.plot([0,2],[2,0], 'r-', lw=2, label='x-nullcline')
plt.plot([1,1],[0,2], 'b-', lw=2, label='z-nullcline')
# Plot equilibrium points
for point in eqp:
plt.plot(point[0],point[1],"red", marker = "o", markersize = 10.0)
plt.legend(loc='best')
x = np.linspace(0, 2, 20)
z = np.linspace(0, 2, 20)
X1 , Z1 = np.meshgrid(x, z) # Create a grid
DX1, DZ1 = Sys([X1, Z1]) # Compute reaction rate on the grid
M = (np.hypot(DX1, DZ1)) # Norm reaction rate
M[ M == 0] = 1. # Avoid zero division errors
DX1 /= M # Normalise each arrows
DZ1 /= M
plt.quiver(X1, Z1, DX1, DZ1, M, pivot='mid')
plt.xlabel("x(\u03C4)")
plt.ylabel("z(\u03C4)")
plt.legend()
plt.grid()
plt.show()

Orientation density plot (matplotlib)

I have a large set of vectors, which orientations I want to analyze by looking at the polar angles theta and phi. So I start with a 2D array of those angles.
I want to visualize it by plotting a histogram, or preferentially the kernel density onto a sphere (or the 2D projection, since I only need the show half a sphere).
If I use the polar angles for the histogram, it will be completely wrong, since - to give an example - the points theta = 1°, phi = 0° & 180° are closer than theta = 30°, phi = 0° & 180°. The binning fails.
If I use 2D cartesian coordinates of the projection for the histogram, I run into the same problem: points on x = 0, y = 0 & 0.1 are closer than x = 0, y = 0.5 & 0.6.
Right now I'm using a kernel density estimate with 3D cartesian coordinates:
from scipy import stats
X, Y, Z = [], [], []
for i in range(300):
for j in range(500):
X.append(np.sin(i / 300 * pi / 2) * np.cos(j / 500 * 2 * pi))
Y.append(np.sin(i / 300 * pi / 2) * np.sin(j / 500 * 2 * pi))
Z.append(np.cos(i / 300 * pi / 2))
X, Y, Z = np.array(X), np.array(Y), np.array(Z)
positions = np.vstack([X, Y, Z])
# x, y and z for my data: sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)
values = np.vstack([np.sin(data[:, 1]) * np.cos(data[:, 2]), np.sin(data[:, 1]) * np.sin(data[:, 2]), np.cos(data[:, 1])])
fig, ax = subplots(1, figsize=(7, 7))
kernel = stats.gaussian_kde(values)
density = kernel(positions)
ax.tricontourf(X, Y, density, levels=np.linspace(0, max(density), 50))
plt.box('off')
ax.set_ylim((-1.01, 1.01))
ax.set_xlim((-1.01, 1.01))
This gives me:
Polar density plot
I don't know if a) this gives the correct result and b) if there isn't a better solution for this.

Finding intersection between straight line and contour

I am trying to find the intersection point of a straight(dashed red) with the contour-line highlighted in red(see plot). I used .get_paths in the second plot to isolate said contour line form the others(second plot).
I have looked at a contour intersection problem, How to find all the intersection points between two contour-set in an efficient way, and have tried to use it as a base but have not been able to reproduce anything useful.
http://postimg.org/image/hz01fouvn/
http://postimg.org/image/m6utofwb7/
Does any one have any ideas?
relevant functions to recreate plot,
#for contour
def p_0(num,t) :
esc_p = np.sum((((-1)**n)*(np.exp(t)**n)*((math.factorial(n)*((n+1)**0.5))**-1)) for n in range(1,num,1))
return esc_p+1
tau = np.arange(-2,3,0.1)
r=[]
p1 = p_0(51,tau)
p2 = p_0(51,tau)
for i in p1:
temp_r=i/p2
r.append(temp_r)
x,y= np.meshgrid(tau,tau)
cs = plt.contour(x, y, np.log(r),50,colors='k')
whichContour =20
pa = CS.collections[whichContour].get_paths()[0]
v = pa.vertices
xx = v[:, 0]
yy = v[:, 1]
plt.plot(xx, yy, 'r-', label='Crossing contour')
#straight line
p=0.75
logp = (np.log(p*np.exp(tau)))
plt.plot(tau,logp)
Current attempt,
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import math
def intercepting_line() :
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
#fake data
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10.0 * (Z2 - Z1)
#plot
cs = plt.contour(X,Y,Z)
whichContour = 2 # change this to find the right contour lines
#get the vertices to calculate an intercept with a line
p = cs.collections[whichContour].get_paths()[0]
#see: http://matplotlib.org/api/path_api.html#module-matplotlib.path
v = p.vertices
xx = v[:, 0]
yy = v[:, 1]
#this shows the innermost ring now
plt.plot(xx, yy, 'r--', label='inner ring')
#fake line
x = np.arange(-2, 3.0, 0.1)
y=lambda x,m:(m*x)
y=y(x,0.9)
lineMesh = np.meshgrid(x,y)
plt.plot(x,y,'r' ,label='line')
#get the intercepts, two in this case
x, y = find_intersections(v, lineMesh[1])
print x
print y
#plot the intercepting points
plt.plot(x[0], y[0], 'bo', label='first intercept')
#plt.plot(x[1], y[1], 'rs', label='second intercept')
plt.legend(shadow=True, fancybox=True, numpoints=1, loc='best')
plt.show()
#now we need to calculate the intercept of the vertices and whatever line
#this is pseudo code but works in case of two intercepting contour vertices
def find_intersections(A, B):
# min, max and all for arrays
amin = lambda x1, x2: np.where(x1<x2, x1, x2)
amax = lambda x1, x2: np.where(x1>x2, x1, x2)
aall = lambda abools: np.dstack(abools).all(axis=2)
slope = lambda line: (lambda d: d[:,1]/d[:,0])(np.diff(line, axis=0))
x11, x21 = np.meshgrid(A[:-1, 0], B[:-1, 0])
x12, x22 = np.meshgrid(A[1:, 0], B[1:, 0])
y11, y21 = np.meshgrid(A[:-1, 1], B[:-1, 1])
y12, y22 = np.meshgrid(A[1:, 1], B[1:, 1])
m1, m2 = np.meshgrid(slope(A), slope(B))
m1inv, m2inv = 1/m1, 1/m2
yi = (m1*(x21-x11-m2inv*y21) + y11)/(1 - m1*m2inv)
xi = (yi - y21)*m2inv + x21
xconds = (amin(x11, x12) < xi, xi <= amax(x11, x12),
amin(x21, x22) < xi, xi <= amax(x21, x22) )
yconds = (amin(y11, y12) < yi, yi <= amax(y11, y12),
amin(y21, y22) < yi, yi <= amax(y21, y22) )
return xi[aall(xconds)], yi[aall(yconds)]
At the moment it finds intersecting points but only where the line is uniform, the main reason why I cannot find a solution here is that I dont understand the original authors train of thinking here,
yi = (m1*(x21-x11-m2inv*y21) + y11)/(1 - m1*m2inv)
xi = (yi - y21)*m2inv + x21
Use shapely can find the intersection point, than use the point as the init guess value for fsolve() to find the real solution:
#for contour
def p_0(num,t) :
esc_p = np.sum((((-1)**n)*(np.exp(t)**n)*((math.factorial(n)*((n+1)**0.5))**-1)) for n in range(1,num,1))
return esc_p+1
tau = np.arange(-2,3,0.1)
x,y= np.meshgrid(tau,tau)
cs = plt.contour(x, y, np.log(p_0(51, y)/p_0(51, x)),[0.2],colors='k')
p=0.75
logp = (np.log(p*np.exp(tau)))
plt.plot(tau,logp)
from shapely.geometry import LineString
v1 = cs.collections[0].get_paths()[0].vertices
ls1 = LineString(v1)
ls2 = LineString(np.c_[tau, logp])
points = ls1.intersection(ls2)
x, y = points.x, points.y
from scipy import optimize
def f(p):
x, y = p
e1 = np.log(0.75*np.exp(x)) - y
e2 = np.log(p_0(51, y)/p_0(51, x)) - 0.2
return e1, e2
x2, y2 = optimize.fsolve(f, (x, y))
plt.plot(x, y, "ro")
plt.plot(x2, y2, "gx")
print x, y
print x2, y2
Here is the output:
0.273616328952 -0.0140657435002
0.275317387697 -0.0123646847549
and the plot:
See your contour lines as polylines and plug the vertex coordinates into the implicit line equation (F(P) = a.X + b.Y + c = 0). Every change of sign is an intersection, computed by solving 2x2 linear equations. You need no sophisticated solver.
If you need to detect the contour lines simultaneously, it is not much more complicated: consider the section of the terrain by a vertical plane through the line. You will obtain altitudes by linear interpolation along the edges of the grid tiles that are crossed. Finding the intersections with the grid is closely related to the Bresenham line drawing algorithm.
Then what you get is a profile, i.e. a function of a single variable. Locating the intersections with the horizontal planes (iso-values) is also done by detecting changes of sign.
This is a way that I used to solve this problem
def straight_intersection(straight1, straight2):
p1x = straight1[0][0]
p1y = straight1[0][1]
p2x = straight1[1][0]
p2y = straight1[1][1]
p3x = straight2[0][0]
p3y = straight2[0][1]
p4x = straight2[1][0]
p4y = straight2[1][1]
x = p1y * p2x * p3x - p1y * p2x * p4x - p1x * p2y * p4x + p1x * p2y * p3x - p2x * p3x * p4y + p2x * p3y * p4x + p1x * p3x * p4y - p1x * p3y * p4x
x = x / (p2x * p3y - p2x * p4y - p1x * p3y + p1x * p4y + p4x * p2y - p4x * p1y - p3x * p2y + p3x * p1y)
y = ((p2y - p1y) * x + p1y * p2x - p1x * p2y) / (p2x - p1x)
return (x, y)
While this question is old now, i want to share my answer for this problem for future generations to come.
Actually, there is two more solutions i found to this problem that is relatively efficient.
First solution is using pointPolygonTest in opencv recursively like that.
# Enumerate the line segment points between 2 points
for pt in zip(*line(*p1, *p2)):
if cv2.pointPolygonTest(conts[0], pt, False) == 0: # If the point is on the contour
return pt
Second solution, you can simply draw the contour and line and make a np.logical_and() to get the answer
blank = np.zeros((1000, 1000))
blank_contour = drawContour(blank.copy(), cnt[0], 0, 1, 1)
blank_line = cv2.line(blank.copy(), line[0], line[1], 1, 1)
intersections = np.logical_and(blank_contour, black_line)
points = np.where(intersections == 1)

Categories

Resources