A curiosity journal of math, physics, programming, astronomy, and more.

Lindenmeyer systems playground

Not long ago I learned about Replicant, so as an excuse to play around with it I created a playground for Lindenmeyer systems based on my previous posts about L-systems. You can use the playground here.

While building it, I added support for pushing and popping states with the [ and ] operators, which turned out to be relatively straight-forward. The conversion of commands such as [:turn 90] into a set of points looks like this:

(defmulti update-state (fn [_ [cmd]] cmd))
(defmethod update-state nil [state _]
  state)
(defmethod update-state :forward [state [_ param]]
  (let [{[x y] :point :keys [heading points]} state
        radians (* heading Math/PI (/ 180))
        x (+ x (* param (Math/sin radians)))
        y (+ y (* param (Math/cos radians)))]
    (assoc state :point [x y] :points (conj points [x y]))))
(defmethod update-state :turn [state [_ param]]
  (assoc state :heading (- (state :heading) param)))
(defmethod update-state :push-state [state _]
  (update state :states conj {:point (state :point) :heading (state :heading)}))
(defmethod update-state :pop-state [state _]
  (let [{:keys [point heading]} (-> state :states peek)]
    (-> state
        (assoc :point point :heading heading)
        (update :points conj :move point)
        (update :states pop))))
(defn coords [cmds]
  (:points
    (reduce update-state
            {:point [0 0]
             :heading 180
             :points [[0 0]]
             :states []}
            cmds)))

Note that when popping the state the list of points gets the non-coordinate value :move, which is recognized by the drawing code and tells it not to draw a line segment from the previous point to the new point.

I also improved the generation of random L-systems. I've noted some symmetries in the rules of systems that produce pleasing outputs, but in the playground I created symmetries by transforming a randomly generated base rule, then selectively reversing it, inverting it, and concatenating with the original rule to create new, related rules. It will also occasionally insert [ and ] commands, though not always in a meaningful way (e.g., [+]). The current system seems to have the best success rate of any of my previous attempts, more frequently generating non-trivial and visually distinctive patterns.

If you have suggestions for further improvements, please let me know.