Search code examples
pythonmathformulasmoothingcurve

Formulae / Method to calculate smooth curved line between two points satisfying headings of both points


enter image description here
As shown in the image, formulae or method how to calculate smooth curved line is needed.The red line is the line which will look at the end after solving.

I'll appreciate your help / discussion.

I need mathematical formulae and may be how to solve it using scipy python package.


Solution

  • Actually, you will gain more flexibility if you use cubic Bezier curves.

    This for the first problem:

    import math
    import numpy as np
    import matplotlib.pyplot as plt
    
    #-----------------------------
    
    def length( vector ): return np.linalg.norm( vector )      # length of a vector
    def normalise( vector ): return vector / length(vector)    # normalise a vector (numpy array)
    
    #-----------------------------
    
    class Bezier:
        '''class for Cubic Bezier curve'''
        def __init__( self, q0, q1, q2, q3 ):
            self.p0 = np.array( q0 )
            self.p1 = np.array( q1 )
            self.p2 = np.array( q2 )
            self.p3 = np.array( q3 )
    
        def pt( self, t ):
            return ( 1.0 - t ) ** 3 * self.p0 + 3.0 * t * ( 1.0 - t ) ** 2 * self.p1 + 3.0 * t ** 2 * ( 1.0 - t ) * self.p2 + t ** 3 * self.p3
    
        def curve( self, n ):
            crv = []
            for t in np.linspace( 0.0, 1.0, n ):
                crv.append( self.pt( t ) )
            return crv
    
    #-----------------------------
    
    def drawBezier( x1, y1, heading1, x2, y2, heading2, n ):
        '''Turns two points plus headings into cubic Bezier control points [ P0, P1, P2, P3 ] by adding intermediate control points'''
        result = []
    
        rad1, rad2 = heading1 * math.pi / 180, heading2 * math.pi / 180
        d1 = np.array( [ np.sin( rad1 ), np.cos( rad1 ) ] )    # direction vector at point 1
        d2 = np.array( [ np.sin( rad2 ), np.cos( rad2 ) ] )
    
        p0 = np.array( ( x1, y1 ) )                            # start point of Bezier curve
        p3 = np.array( ( x2, y2 ) )                            # end point of Bezier curve
        p1 = p0 + d1 * length( p3 - p0 ) / 2.0                 # use direction at P0 to get P1
        p2 = p3 - d2 * length( p3 - p0 ) / 2.0                 # use direction at p3 to get P2
    
        arc = Bezier( p0, p1, p2, p3 )                         # define a cubic Bezier object
    
        x = [];   y = []
        for pt in arc.curve( n ):
            x.append( pt[0] )
            y.append( pt[1] )
        plt.plot( x, y )
        
    #-----------------------------
    
    x1, y1, heading1 = 0.0, 0.0, 0.0
    x2, y2, heading2 = 1.0, 1.0, 90.0
    drawBezier( x1, y1, heading1, x2, y2, heading2, 100 )
    plt.plot( [ x1, x2 ], [ y1, y2 ], 'o' )                   # plot original support points
    plt.show()
    

    enter image description here

    The advantage of the cubic Bezier is that you can also seamlessly deal with the cases such as where the two direction lines don't cross. For example

    x2, y2, heading2 = 1.0, 1.0, 0.0
    

    with output

    enter image description here