exupero's blog
RSSApps

Coastal variation

In the previous post we created island outlines, but the texture of the coastlines was too consistent for real islands, whose coastlines vary in roughness, craggy in some places, smooth in others. Here we'll generate such variation with a only couple new functions. I've already set the stage by making subdivide-with a higher-order function, so all we need to do is add some roughness characteristics to each point in our N-gon, use them when subdividing, and interpolate the added characteristics.

We'll again start with an irregular N-gon:

(let [rng (java.util.Random. 1)
      sides (.nextInt rng 3 9)]
  (->> sides
    regular-ngon
    (irregularize rng 0.35)
    (map (partial apply cartesian))
    loop-around))

But instead of working with just vertex coordinates, we'll add a couple roughness parameters that specify how far we can push midpoints, as well as which side to bias toward:

(defn add-roughness [rng vert]
  {:vertex vert
   :balance (.nextDouble rng)
   :max-offset (.nextDouble rng 0.05 1.05)})

To interpolate, we need a new midpoint offsetting function:

(defn avg [& xs]
  (/ (reduce + xs)
     (count xs)))
(defn offset-roughened-midpoint
  [rng
   {[x1 y1] :vertex b1 :balance o1 :max-offset}
   {[x2 y2] :vertex b2 :balance o2 :max-offset}]
  (let [mx (avg x1 x2)
        my (avg y1 y2)
        vx (- (- y1 y2))
        vy (- x1 x2)
        balance (avg b1 b2)
        max-offset (avg o1 o2)
        s (* max-offset (- (.nextDouble rng) balance))]
    {:vertex [(+ mx (* s vx))
              (+ my (* s vy))]
     :balance balance
     :max-offset max-offset}))

Now we can subdivide sides repeatedly and extract the final vertices:

(let [rng (java.util.Random. 1)
      sides (.nextInt rng 3 9)]
  (-> sides
    (->>
      regular-ngon
      (irregularize rng 0.35)
      (map (partial apply cartesian))
      loop-around
      (map #(add-roughness rng %)))
    (as-> verts
      (iterate-n-times
        #(subdivide-with
           (partial offset-roughened-midpoint rng)
           %)
        verts
        10))
    (->> (map :vertex))))

Now generated islands have areas of coastline that are smooth, areas that are rough, and areas that are in between. Play with :balance and :max-offset to see how it affects the final product.

In the next post, we'll try a different technique for subdivision, but the overall approach to generating islands will remain the same.