Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw a curve connecting two points instead of a straight line

I want to do something like this:
enter image description here

I have the points but don't know how to plot the curves instead of straight lines.

Thank you.

like image 387
Myath Avatar asked May 02 '15 22:05

Myath


3 Answers

For people interested in this question, I followed Matthew's suggestion and came up with this implementation:

def hanging_line(point1, point2):
    import numpy as np

    a = (point2[1] - point1[1])/(np.cosh(point2[0]) - np.cosh(point1[0]))
    b = point1[1] - a*np.cosh(point1[0])
    x = np.linspace(point1[0], point2[0], 100)
    y = a*np.cosh(x) + b

    return (x,y)

Here is what the result looks like:

import matplotlib.pyplot as plt

point1 = [0,1]
point2 = [1,2]
x,y = hanging_line(point1, point2)

plt.plot(point1[0], point1[1], 'o')
plt.plot(point2[0], point2[1], 'o')
plt.plot(x,y)

§1

like image 197
Joe Bathelt Avatar answered Oct 22 '22 08:10

Joe Bathelt


You are going to need some expression for the curve you want to plot, then you can make the curve out of many line segments.

Here's a parabola:

x = np.linspace(-1, 1, 100)
y = x*x
plt.plot(x, y)

parabola

Here's a sin curve:

x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)

sin

Each of these looks smooth, but is actually made up of many small line segments.

To get a collection of curves like you showed, you are going to need some expression for a curve you want to plot in terms of its two endpoints. The ones in your picture look like catenarys which are (approximately) the shape a hanging chain assumes under the force of gravity:

x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = 2*np.cosh(x/2)
plt.plot(x, y)

catenary

You will have to find a way of parameterizing this curve in terms of its two endpoints, which will require you substituting your values of y and x into:

y = a*cosh(x/a) + b

and solving the resulting pair of equations for a and b.

like image 44
Matthew Drury Avatar answered Oct 22 '22 09:10

Matthew Drury


There is a cool (at least for me) way to draw curve lines between two points, using Bezier curves. Just with some simple code you can create lists with dots connecting points and chart them with matplotlib, for example:

def recta(x1, y1, x2, y2):
    a = (y1 - y2) / (x1 - x2)
    b = y1 - a * x1
    return (a, b)

def curva_b(xa, ya, xb, yb, xc, yc):
    (x1, y1, x2, y2) = (xa, ya, xb, yb)
    (a1, b1) = recta(xa, ya, xb, yb)
    (a2, b2) = recta(xb, yb, xc, yc)
    puntos = []

    for i in range(0, 1000):
        if x1 == x2:
            continue
        else:
            (a, b) = recta(x1, y1, x2, y2)
        x = i*(x2 - x1)/1000 + x1
        y = a*x + b
        puntos.append((x,y))
        x1 += (xb - xa)/1000
        y1 = a1*x1 + b1
        x2 += (xc - xb)/1000
        y2 = a2*x2 + b2
    return puntos

Then, just run the function for some starting, mid and ending points, and use matplotlib:

lista1 = curva_b(1, 2, 2, 1, 3, 2.5)
lista2 = curva_b(1, 2, 2.5, 1.5, 3, 2.5)
lista3 = curva_b(1, 2, 2.5, 2, 3, 2.5)
lista4 = curva_b(1, 2, 1.5, 3, 3, 2.5)

fig, ax = plt.subplots()
ax.scatter(*zip(*lista1), s=1, c='b')
ax.scatter(*zip(*lista2), s=1, c='r')
ax.scatter(*zip(*lista3), s=1, c='g')
ax.scatter(*zip(*lista4), s=1, c='k')

This should be the results:

several Bezier quadratic curves

By extending the code a little more, you can get forms like this:

Bezier quartic curve

like image 3
Chema Avatar answered Oct 22 '22 09:10

Chema