I want to get the coordinates of a number of points that together form an octagon. For a circle this is done easily as follows:
import numpy as np
n = 100
x = np.cos(np.linspace(0, 2 * np.pi, n))
y = np.sin(np.linspace(0, 2 * np.pi, n))
coordinates = list(zip(x, y))
By changing n
I can increase/decrease the "angularity". Now I want to do the same for an octagon. I know that an octagon has 8 sides and the angle between each side 45 degrees.
Let's assume that the perimeter of the octagon is 30.72m. Each side has therefore a length of 3.79m.
perimeter = 30.72
n_sides = 8
angle = 45
How can I get n
coordinates that represent this octagon?
Edit:
With the help of the answers of @lastchance and @mozway I am able to generate an octagon. My goal is to get evenly-spaced n
coordinates from the perimeter of this octagon.
If n = 8
these coordinates correspond to the corners of the octagon, but I'm interested in cases where n > 8
Let's use some math.
Each triangle in the polygon is isosceles. Assuming r
the radius of the containing circle and a
each side of the polygon we have:
n_sides = 8
perimeter = n_sides * a
a/2 = sin(pi/n_sides) / r # isosceles = 2 equal right triangles
perimeter = n_sides * 2 * sin(pi/n_sides) / r
r = perimeter/(2 * n_sides * sin(pi/n_sides))
Thus, the coordinates are:
perimeter = 30.72
n_sides = 8
r = perimeter/(2 * n_sides * np.sin(np.pi/n_sides))
x = r * np.cos(np.linspace(0, 2 * np.pi, n_sides, endpoint=False))
y = r * np.sin(np.linspace(0, 2 * np.pi, n_sides, endpoint=False))
Note the endpoint=False
in linspace
.
As a graph:
ax = plt.subplot(aspect=1)
ax.plot(x, y, marker='o')
Output:
If you want one extra point to "close" the polygon (the last point being the same as the first point):
x = r * np.cos(np.linspace(0, 2 * np.pi, n_sides+1))
y = r * np.sin(np.linspace(0, 2 * np.pi, n_sides+1))
Now, let's use shapely
to check that the calculation is correct:
from shapely.geometry import Polygon
Polygon(zip(x, y)).length # 30.72
n
points on the octagon/polygonNow that we have a polygon, we can interpolate n
points along its perimeter.
Let's generate a "closed" polygon (i.e. n_sides+1 point in which the last one is equal to the first), create a LineString
and interpolate
n
values along it:
from shapely.geometry import LineString
perimeter = 30.72
n_sides = 8
n = 12
# compute the points of the polygon
r = perimeter/(2 * n_sides * np.sin(np.pi/n_sides))
x = r * np.cos(np.linspace(0, 2 * np.pi, n_sides+1))
y = r * np.sin(np.linspace(0, 2 * np.pi, n_sides+1))
# create a line string
# interpolate n points on the perimeter
line = LineString(zip(x, y))
coords = np.concatenate(list(line.interpolate(x).coords
for x in np.linspace(0, line.length, n,
endpoint=False)))
# plot
ax = plt.subplot(aspect=1)
ax.plot(x, y)
ax.plot(*coords.T, ls='', marker='o', label='n = 8')
Alternatively, performing the interpolation with numpy.interp
:
perimeter = 30.72
n_sides = 8
n = 12
# compute the points of the polygon
r = perimeter/(2 * n_sides * np.sin(np.pi/n_sides))
x = r * np.cos(np.linspace(0, 2 * np.pi, n_sides+1))
y = r * np.sin(np.linspace(0, 2 * np.pi, n_sides+1))
# interpolate the n points along the perimeter
ref = np.linspace(0, 1, n_sides+1)*perimeter # distance to origin: ref
dist = np.linspace(0, 1, n+1)*perimeter # distance to origin: n points
new_x = np.interp(dist, ref, x) # interpolated x
new_y = np.interp(dist, ref, y) # interpolated y
ax = plt.subplot(aspect=1)
ax.plot(x, y)
ax.plot(new_x, new_y, ls='', marker='o')
Output: