Myath
Myath

Reputation: 563

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.

Upvotes: 14

Views: 37016

Answers (3)

Chema
Chema

Reputation: 31

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

Upvotes: 3

Joe Bathelt
Joe Bathelt

Reputation: 5869

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

Upvotes: 15

Matthew Drury
Matthew Drury

Reputation: 1095

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.

Upvotes: 4

Related Questions