Code samples are live and edibtable enabled by Klipse

I was working on another blog post involving polygons and realized how nice it would be to have some random polygons to test with. Turns out its a interesting little problem and now its become a post of its own.

Let's start with generating a regular polygon. This is a perfect problem for polar coordinates. A polygon can be defined as a set of polar points with equal radius and equally divided angles.

``````(defn polar-polygon [edges radius]
(mapv #(vector (* % (/ (* 2 js/Math.PI) edges)) radius)
(range edges)))

(polar-polygon 5 70)
``````

It works, but most graphics programs work with rectangular coordinates. Let's write a quick function to transform polar polygons to rectangular polygons with their center at point `[x y]`.

``````(defn to-rect
[(+ x (* radius (js/Math.cos theta)))
(+ y (* radius (js/Math.sin theta)))]))

(defn regular-polygon
([edges] (regular-polygon edges 100))
(mapv (partial to-rect center) (polar-polygon edges radius))))

(regular-polygon 5)
``````

And render it.

``````(render-polygon! (regular-polygon 7))
``````

Or render a few!

``````(render-polygon! (regular-polygon 9)              {:color "green"})
(render-polygon! (regular-polygon 5 [260 50]  50) {:color "red"})
(render-polygon! (regular-polygon 3 [310 130] 40) {:color "blue"})
``````

Now to make a random polygon. Take that regular one and add some random noise. We'll modify the original function to add some randomness to the radius and theta. But only enough so the points don't overlap avoiding complex polygons.

``````(defn random-polar-polygon [edges radius]
(let [angle-increment (/ (* 2 js/Math.PI) edges)]
(mapv
(fn [edge]
[(+ (* angle-increment edge) (* (rand) angle-increment))
(range edges))))

(defn random-polygon
([n] (random-polygon n 100))
(mapv (partial to-rect center) (random-polar-polygon n radius))))

(render-polygon! (random-polygon 9)               {:color "green"})
(render-polygon! (random-polygon 7  [260 50]  50) {:color "red"})
(render-polygon! (random-polygon 13 [310 130] 40) {:color "blue"})
``````

Hmm... Too pointy. It would be better if the shapes were a little closer to their regular self. Adjusting the distribution and not the range will allow us to continue generating a wide set of polygons, but make it more likely to generate polygons seen in the real world. The `pow` function map values `[0,1] => [0,1]` when applied to the uniform random distribution provides a skew. Using this we can skew our random noise to generate a more standard looking set of polygons.

``````(defn random-polar-polygon [edges radius]
angle-damper  1.22
angle-increment (/ (* 2 js/Math.PI) edges)]
(mapv
(fn [edge]
[(+ (* angle-increment edge)
(* (js/Math.pow (rand) angle-damper) angle-increment))