Generate a new set of points along a line

Question:

I have a Python project where I need to redraw a line many times with the points in random places but keeping the line’s shape and point count roughly the same. The final output will be using polygonal points and not Bezier paths (though I wouldn’t be opposed to using Bezier as an intermediary step).

This animation is demonstrating how the points could move along the line to different positions while maintaining the general shape.
Point Count animation

I also have a working example below where I’m moving along the line and picking random new points between existing points (the red line, below). It works okay, but I’d love to hear some other approaches I might take if someone knows of a better one?

Though this code is using matplotlib to demonstrate the line, the final program will not.

import numpy as np
from matplotlib import pyplot as plt
import random
from random import (randint,uniform)

def move_along_line(p1, p2, scalar):
    distX = p2[0] - p1[0]
    distY = p2[1] - p1[1]

    modX = (distX * scalar) + p1[0]
    modY = (distY * scalar) + p1[1]

    return [modX, modY]

x_coords = [213.5500031,234.3809357,255.211853,276.0427856,296.8737183,317.7046204,340.1997681,364.3751221,388.5505066,414.8896484,444.5192261,478.5549622,514.5779419,545.4779053,570.3830566,588.0241699,598.2469482,599.772583,596.758728,593.7449341,590.7310791,593.373291,610.0373535,642.1326294,677.4451904,710.0697021,737.6887817,764.4020386,791.1152954,817.8284912,844.541687,871.2550049,897.9682007,924.6813965,951.3945923,978.1078491,1009.909546,1042.689941,1068.179199,1089.543091]
y_coords = [487.3099976,456.8832703,426.4565125,396.0297852,365.6030273,335.1763,306.0349426,278.1913452,250.3477478,224.7166748,203.0908051,191.2358704,197.6810608,217.504303,244.4946136,276.7698364,312.0551453,348.6885986,385.4395447,422.1904297,458.9414063,495.5985413,527.0128479,537.1477661,527.6642456,510.959259,486.6988525,461.2799683,435.8611145,410.4422913,385.023468,359.6045532,334.18573,308.7669067,283.3480835,257.929184,239.4429474,253.6099091,280.1803284,310.158783]
plt.plot(x_coords,y_coords,color='b')
plt.scatter(x_coords,y_coords,s=2)

new_line_x = []
new_line_y = []

for tgt in range(len(x_coords)-1):
    #tgt = randint(0, len(x_coords)-1)
    next_pt = tgt+1
    new_pt = move_along_line([x_coords[tgt],y_coords[tgt]], [x_coords[next_pt],y_coords[next_pt]], uniform(0, 1))
    new_line_x.append(new_pt[0])
    new_line_y.append(new_pt[1])

plt.plot(new_line_x,new_line_y,color='r')
plt.scatter(new_line_x,new_line_y,s=10)
ax = plt.gca()
ax.set_aspect('equal')
plt.show()

enter image description here

Thank you very much!

Asked By: Dr. Pontchartrain

||

Answers:

I’m not sure if this is the most optimal way to do this but essentially you want to follow these steps:

Calculate the distance of the entire path, and the distance between all the points. Then for each point, tally the distances to that point.

Generate a new set of random points along the path starting with 0, then for each pair of points calculate a random distance: random value between 0 and 1 * total length of the path.

Sort these distances from smallest to largest.

For each random distance loop over the distances find the index where the random distance is > than distance i, and less than distance i+1. Interpolate new x and y values from these points.

from matplotlib import pyplot as plt
from scipy.interpolate import interp1d
import numpy
import random
import math

x_coords = [195.21,212.53,237.39,270.91,314.21,368.43,434.69,514.1,607.8,692.69,746.98,773.8,776.25,757.45,720.52,668.55,604.68,545.37,505.79,487.05,490.27,516.58,567.09,642.93,745.2,851.5,939.53,1010.54,1065.8,1106.58,1134.15,1149.75,1154.68]
y_coords = [195.34,272.27,356.59,438.98,510.14,560.76,581.52,563.13,496.27,404.39,318.83,242.15,176.92,125.69,91.02,75.48,81.62,113.49,168.57,239.59,319.29,400.38,475.6,537.67,579.32,586.78,558.32,504.7,436.69,365.05,300.55,253.95,236.03]
n_points = 100
x_coords = numpy.array(x_coords)
x_min = x_coords.min()
x_max = x_coords.max()
x_range = x_max - x_min
distances = []
tallied_distances = [0]
tallied_distance = 0
for i in range(0, len(x_coords) -1):
    xi = x_coords[i]
    xf = x_coords[i + 1]
    yi= y_coords[i]
    yf = y_coords[i+1]
    d = math.sqrt((xf-xi)**2 + (yf-yi)**2)
    tallied_distance += d
    tallied_distances.append(tallied_distance)
random_distances_along_line = [0]
for i in range(0, n_points-2):
    random_distances_along_line.append(random.random()*tallied_distance)
random_distances_along_line.sort()
new_x_points = [x_coords[0]]
new_y_points = [y_coords[0]]
for i in range(0, len(random_distances_along_line)):
    dt = random_distances_along_line[i]
    for j in range(0, len(tallied_distances)-1):
        di = tallied_distances[j]
        df = tallied_distances[j+1]
        if di < dt and dt < df:
            difference = dt - di
            xi = x_coords[j]
            xf = x_coords[j+1]
            yi = y_coords[j]
            yf = y_coords[j+1]
            xt = xi+(xf-xi)*difference/(df-di)
            yt = yi+(yf-yi)*difference/(df-di)
            new_x_points.append(xt)
            new_y_points.append(yt)
new_x_points.append(x_coords[len(x_coords)-1])
new_y_points.append(y_coords[len(y_coords)-1])
plt.plot(new_x_points, new_y_points)
plt.scatter(new_x_points, new_y_points,s=2)
ax = plt.gca()
ax.set_aspect('equal')
plt.show()
Answered By: BrendanOtherwhyz