Get Evenly Spaced Points from a Curved Shape

Question:

How may I take a shape that was created with more points at its curves and subdivide it so that the points are distributed more equally along the curve? In my research I thought that numpy‘s interp might be the right function to use, but I don’t know what to use for the parameters (x, xp, fp, left, right, & period). Any help would be very appreciated!

Here is an animation showing the desired output.

Example of Shape with Even Distribution

This is the code for the input rounded rectangle:

from matplotlib import pyplot as plt
import numpy as np

x_values = [1321.4, 598.6, 580.6, 563.8, 548.6, 535.4, 524.5, 516.2, 511,
509.2, 509.2, 511, 516.2, 524.5, 535.4, 548.6, 563.8, 580.6, 598.6, 1321.4, 1339.4,
1356.2, 1371.4, 1384.6, 1395.5, 1403.8, 1409, 1410.8, 1410.8, 1409, 1403.8, 1395.5,
1384.6, 1371.4, 1356.2, 1339.4, 1321.4]
y_values = [805.4, 805.4, 803.5, 798.3, 790.1,
779.2, 766, 750.8, 734, 716, 364, 346, 329.2, 314, 300.8, 289.9, 281.7, 276.5, 274.6,
274.6, 276.5, 281.7, 289.9, 300.8, 314, 329.2, 346, 364, 716, 734, 750.8, 766, 779.2,
790.1, 798.3, 803.5, 805.4]

fig, ax = plt.subplots(1)
ax.plot(x_values,y_values)
ax.scatter(x_values,y_values)
ax.set_aspect('equal')
plt.show()

Thank you!

Asked By: Dr. Pontchartrain

||

Answers:

One way to do this is with the shapely library. You can use your points to make a LineString object, and then interpolate that using numpy.linspace like you expected.

import numpy as np
import matplotlib.pyplot as plt
import shapely

x_values = [1321.4, 598.6, 580.6, 563.8, 548.6, 535.4, 524.5, 516.2, 511,
509.2, 509.2, 511, 516.2, 524.5, 535.4, 548.6, 563.8, 580.6, 598.6, 1321.4, 1339.4,
1356.2, 1371.4, 1384.6, 1395.5, 1403.8, 1409, 1410.8, 1410.8, 1409, 1403.8, 1395.5,
1384.6, 1371.4, 1356.2, 1339.4, 1321.4]
y_values = [805.4, 805.4, 803.5, 798.3, 790.1,
779.2, 766, 750.8, 734, 716, 364, 346, 329.2, 314, 300.8, 289.9, 281.7, 276.5, 274.6,
274.6, 276.5, 281.7, 289.9, 300.8, 314, 329.2, 346, 364, 716, 734, 750.8, 766, 779.2,
790.1, 798.3, 803.5, 805.4]

coords = np.array([x_values, y_values]).T

poly = shapely.LineString(coords)

coords_new = poly.interpolate(np.linspace(0, 1, num=51), normalized=True)

x_new = [pt.x for pt in coords_new]
y_new = [pt.y for pt in coords_new]

plt.plot(x_new, y_new)
plt.scatter(x_new, y_new)
plt.show()

interpolated polygon

Answered By: Roy Smart

This is a very rudimental solution, where you interpolate between the neighbouring points on a line.

Basically what the code does is, that it places "temporary" points on the line between the neighbour points evenly spaced with approximately stepsize spacing. This will result in an approximately even spacing on the curve (the smaller the stepsize the denser the more points you have). Now if you pick every skipper-th point on it, the spacing between those points will be approx skipper*stepsize.

There is a small flaw in it, the first and the last points are not guaranteed to be this far from eachother, so you have to play with the skipper to fix that.

The code:

stepsize = 10**-2

new_x = np.array([])
new_y = np.array([])

for i in range(len(x_values)):
    x0 = x_values[i]
    x1 = x_values[(i+1)%len(x_values)]
    y0 = y_values[i]
    y1 = y_values[(i+1)%len(x_values)]

    dx = x1-x0
    dy = y1-y0

    length = np.sqrt(dx**2+dy**2)
    N = int(length/stepsize)

    new_x = np.append(new_x,np.linspace(x_values[i],x_values[(i+1)%len(x_values)],N))
    new_y = np.append(new_y,np.linspace(y_values[i],y_values[(i+1)%len(x_values)],N))

skipper = 6650

fig, ax = plt.subplots(1)
ax.plot(x_values,y_values)
ax.scatter(new_x[::skipper],new_y[::skipper])
ax.set_aspect('equal')
plt.show()
Answered By: Phoenixdust

enter image description here

from matplotlib import pyplot as plt
import numpy as np

x = np.array([1321.4, 598.6, 580.6, 563.8, 548.6, 535.4, 524.5, 516.2, 511,
509.2, 509.2, 511, 516.2, 524.5, 535.4, 548.6, 563.8, 580.6, 598.6, 1321.4, 1339.4,
1356.2, 1371.4, 1384.6, 1395.5, 1403.8, 1409, 1410.8, 1410.8, 1409, 1403.8, 1395.5,
1384.6, 1371.4, 1356.2, 1339.4, 1321.4])
y = np.array([805.4, 805.4, 803.5, 798.3, 790.1,
779.2, 766, 750.8, 734, 716, 364, 346, 329.2, 314, 300.8, 289.9, 281.7, 276.5, 274.6,
274.6, 276.5, 281.7, 289.9, 300.8, 314, 329.2, 346, 364, 716, 734, 750.8, 766, 779.2,
790.1, 798.3, 803.5, 805.4])

fig, ax = plt.subplots(1)
ax.set_aspect('equal')
ax.scatter(x, y, s=40, zorder=3, alpha=0.3)

# compute the distances, ds, between points
dx, dy = x[+1:]-x[:-1],  y[+1:]-y[:-1]
ds = np.array((0, *np.sqrt(dx*dx+dy*dy)))

# compute the total distance from the 1st point, measured on the curve
s = np.cumsum(ds)

# interpolate using 200 point
xinter = np.interp(np.linspace(0,s[-1], 200), s, x)
yinter = np.interp(np.linspace(0,s[-1], 200), s, y)

# plot the interpolated points
ax.scatter(xinter, yinter, s=5, zorder=4)
plt.show()
Answered By: gboffi