3

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!

3 Answers3

5

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()
gboffi
  • 22,939
  • 8
  • 54
  • 85
1

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

Roy Smart
  • 664
  • 4
  • 12
  • Thank you very much for the reply, Roy! Unfortunately I won't have access to `shapely` (it's exactly what I need though!). Is there anything similar in `numpy` or `scipy`? Or can I find the `LineString` function somewhere? Thank you! – Dr. Pontchartrain Feb 10 '23 at 22:08
  • 1
    @Dr. Pontchartrain, I'm sure there's a way to use numpy/scipy to suit your needs. The problem I'm struggling with is that while you need evenly spaced points along a coordinate `t` that parameterizes your rectangle, `numpy.interp` and `scipy.interp1d` would be more suited to giving you evenly spaced points along the independent variable `x`. I haven't found an easy way to resolve this issue yet. – Roy Smart Feb 10 '23 at 22:22
  • I truly appreciate the effort, Roy. – Dr. Pontchartrain Feb 10 '23 at 22:23
1

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()
Phoenixdust
  • 433
  • 2
  • 8
  • Thank you for the message & code. Will this look as even with different shapes? Could you please explain the difference between `stepsize` & `skipper`? – Dr. Pontchartrain Feb 10 '23 at 22:25
  • 1
    I have edited the answer to explain it in a bit more detail. Feel free to ask if you still have questions! And yes, it would look even-ish with any kind of shapes. But bgoffi's answer is much better! – Phoenixdust Feb 10 '23 at 22:39