I\'m trying to devise a method for generating random 2D convex polygons. It has to have the following properties:
I've made the ruby port as well thanks to both @Mangara's answer and @Azat's answer:
#!/usr/bin/env ruby
# frozen_string_literal: true
module ValtrAlgorithm
module_function def random_polygon(length)
raise ArgumentError, "length should be > 2" unless length > 2
min_x, *xs, max_x = Array.new(length) { rand }.sort
min_y, *ys, max_y = Array.new(length) { rand }.sort
# Divide the interior points into two chains and
# extract the vector components.
vec_xs = to_random_vectors(xs, min_x, max_x)
vec_ys = to_random_vectors(ys, min_y, max_y).
# Randomly pair up the X- and Y-components
shuffle
# Combine the paired up components into vectors
vecs = vec_xs.zip(vec_ys).
# Sort the vectors by angle, in a counter clockwise fashion. Remove the
# `-` to make it clockwise.
sort_by { |x, y| - Math.atan2(y, x) }
# Lay them end-to-end
point_x = point_y = 0
min_polygon_x = min_polygon_y = 0
points = []
vecs.each do |vec_x, vec_y|
points.append([vec_x, vec_y])
point_x += vec_x
point_y += vec_y
min_polygon_x = [min_polygon_x, point_x].min
min_polygon_y = [min_polygon_y, point_y].min
end
shift_x = min_x - min_polygon_x
shift_y = min_y - min_polygon_y
result = points.map { |point_x, point_y| [point_x + shift_x, point_y + shift_y] }
# Append first point to make it a valid linear ring
result << result.first
end
private def to_random_vectors(coordinates, min, max)
last_min = last_max = min
ary = []
coordinates.each do |coordinate|
if rand > 0.5
ary << coordinate - last_min
last_min = coordinate
else
ary << last_max - coordinate
last_max = coordinate
end
end
ary << max - last_min << last_max - max
end
end