Search code examples
pythonalgorithmmathvectorcomputational-geometry

offset a line/vector parallel to given lines


Given some XY coordinates, I am attempting to create new lines which are parallel to the original coordinates.

However, they seem to be intersecting when the vector 'goes back', like this:

(BLUE: Original coords, ORANGE: programmed coords supposed to be parallel) enter image description here

This is the full python code.

import numpy as np
import matplotlib.pyplot as plt

xN = [11.86478, 24.851482, 75.38245, 84.50359, 58.3, 58.001]
yN = [4.3048816, 3.541581, 4.0219164, 2.854434, 0.0, 0.001]

newX = []
newY = []

d = 1
for i in range(len(xN)-1):
    r = np.sqrt((xN[i+1]-xN[i])**2+(yN[i+1]-yN[i])**2) 
    dx = d/r*(yN[i]-yN[i+1]) 
    dy = d/r*(xN[i+1]-xN[i]) 

    newX.append(xN[i]+dx)
    newY.append(yN[i]+dy)

plt.plot(xN, yN)
plt.plot(newX, newY)
plt.show()

Is there some algorithm/technique to achieve a parallel offset without intersecting with the original lines? Thanks

UPDATE:

While adding the abs() to dx/dy d/r*abs(yN[i]-yN[i+1]) solves the first part, if I wanted to go all the way round, it still intersects, due the line being same size.

enter image description here

I am trying to achieve the following (I manually created the parallel line for visual understanding): enter image description here


Solution

  • After some research I found a solution which offsets the lines by chosen offset value.

    This was taken from this C# algorithm and coded into Python. enter image description here

    NOTE: This does not work properly with collinear shape. Additionally, this is done with explicit coding, it requires refining.

    Full code:

    import numpy as np
    import matplotlib.pyplot as plt
    
    xN = [11.86478, 24.851482, 75.38245, 84.50359, 58.3, 0.4]
    yN = [4.3048816, 3.541581, 4.0219164, 2.854434, 0.0, 1.0]
    
    newX = []
    newY = []
    
    def findIntesection(p1x, p1y, p2x, p2y, p3x,p3y, p4x, p4y):
        dx12 = p2x - p1x
        dy12 = p2y - p1y
        dx34 = p4x - p3x
        dy34 = p4y - p3y
    
        denominator = (dy12*dx34-dx12*dy34)
    
        t1 = ((p1x - p3x) * dy34 + (p3y - p1y) * dx34)/ denominator
    
        t2 = ((p3x - p1x) * dy12 + (p1y - p3y) * dx12)/ -denominator;
        
        intersectX = p1x + dx12 * t1
        intersectY = p1y + dy12 * t1
    
        if (t1 < 0): t1 = 0
        elif (t1 > 1): t1 = 1
        if (t2 < 0): t2 = 0
        elif (t2 > 1): t2 = 1
        
        return intersectX,intersectY
    
    def normalizeVec(x,y):
        distance = np.sqrt(x*x+y*y)
        return x/distance, y/distance
    
    def getEnlarged(oldX, oldY, offset):
        num_points = len(oldX)
        
        for j in range(num_points):
            i = j - 1
            if i < 0:
                i += num_points
            k = (j + 1) % num_points
    
            vec1X =  oldX[j] - oldX[i]
            vec1Y =  oldY[j] - oldY[i]
            v1normX, v1normY = normalizeVec(vec1X,vec1Y)
            v1normX *= offset
            v1normY *= offset
            n1X = -v1normY
            n1Y = v1normX
            pij1X = oldX[i] + n1X
            pij1Y = oldY[i] + n1Y
            pij2X = oldX[j] + n1X
            pij2Y = oldY[j] + n1Y
    
            vec2X =  oldX[k] - oldX[j]
            vec2Y =  oldY[k] - oldY[j]
            v2normX, v2normY = normalizeVec(vec2X,vec2Y)
            v2normX *= offset
            v2normY *= offset
            n2X = -v2normY
            n2Y = v2normX
            pjk1X = oldX[j] + n2X
            pjk1Y = oldY[j] + n2Y
            pjk2X = oldX[k] + n2X
            pjk2Y = oldY[k] + n2Y
            
            intersectX,intersetY = findIntesection(pij1X,pij1Y,pij2X,pij2Y,pjk1X,pjk1Y,pjk2X,pjk2Y)
            
            #print(intersectX,intersetY)
            
            newX.append(intersectX)
            newY.append(intersetY)
    
    getEnlarged(xN, yN, 1)
    
    plt.plot(xN, yN)
    plt.plot(newX, newY)
    plt.show()
    

    This gives the following output: enter image description here