Lisp Turtle Graphics

art lisp

Table of Contents

Turtle Graphics in Lisp .

Lisp is a language for building languages.

Using global state, we can write special purpose sublanguages with little effort.

Two choices:

I go with the first one.

Scenes

A scene is a collection of shapes that are written to a json file and then processed by the rust renderer.

(require "asdf")
(require :generative)

(defclass scene ()
  ((shapes :initform '())))

(defun make-scene ()
  (make-instance 'scene))

(defmethod add-shape ((s scene) shape)
  (push shape (slot-value s 'shapes)))

(defmethod encode-json ((s scene) out)
  (encode-json-object s '(shapes) out))

(defvar *scene* (make-scene))

(defmacro with-scene (&rest body)
  `(let ((*scene* (make-scene)))
     ,@body))

Turtle

A global variable is used to store a 2D graphics turtle, and

(defclass turtle ()
  ((position
    :initarg :position
    :accessor turtle-position)
   ;; Angle in radians
   (direction
    :initarg :direction
    :accessor turtle-direction)
   (scale
    :initarg :scale
    :initform 1.0)))

(defun make-turtle ()
  (make-instance 'turtle
                 :position (vec2 0.0)
                 :direction 0.0))

(defvar *turtle* (make-turtle))
(defmethod copy-turtle ((turtle turtle))
  (with-slots (direction position scale) turtle
      (make-instance 'turtle
                     :position position
                     :direction direction
                     :scale scale)))

(defmethod forward (by &optional drawing)
  (with-slots ((p position) (d direction) (s scale)) *turtle*
    (let ((p-new (add p (mult (vec2-from-angle d) (* by s)))))
      (if drawing
          (add-shape *scene* (make-line p p-new)))
      (setf p p-new))))

(defmethod backward (by &optional drawing)
  (with-slots ((p position) (d direction) (s scale)) *turtle*
    (let ((p-new (sub p (mult (vec2-from-angle d) (* by s)))))
      (if drawing
          (add-shape *scene* (make-line p p-new)))
      (setf p p-new))))

(defmethod turtle-scale (by)
  (with-slots ((s scale)) *turtle*
      (setf s (* s by))))

(defmethod rotate-left (by)
  (with-slots (direction) *turtle*
    (setf direction
          (mod (+ direction (rad by)) PII))))

(defmethod rotate-right (by)
  (with-slots (direction) *turtle*
    (setf direction
          (mod (- direction (rad by)) PII))))

Helper Macros

(defmacro with-copy (&rest body)
  `(let ((*turtle* (copy-turtle *turtle*)))
    ,@body))

(defmacro with-scale (by &rest body)
  `(let ((*turtle* (copy-turtle *turtle*)))
     (turtle-scale ,by)
     ,@body))

Branching System

    (defvar leaf-angle 30)
    (defvar leaf-bend 30)
    (defvar leaf-step 0.5)
    (defvar leaf-div 8)
    (defvar branch-angle 5)

    (defun leaf ()
      (with-copy
          (rotate-left leaf-bend)
        (repeat (1+ leaf-div)
                (forward leaf-step t)
                (rotate-right (/ (* 2 leaf-bend) leaf-div))))
      (with-copy
          (rotate-right leaf-bend)
        (repeat (1+ leaf-div)
                (forward leaf-step t)
                (rotate-left (/ (* 2 leaf-bend) leaf-div)))))

    (defun branch (steps angle)
      (repeat steps
              (with-copy (with-copy (rotate-right leaf-angle) (leaf)))
              (forward 1.0 t)
              (with-copy (rotate-left leaf-angle) (leaf))
              (forward 1.5 t)
              (rotate-right angle)
              (turtle-scale 0.95))
      (leaf))

   (setq *turtle* (make-turtle))

(with-scene
   (rotate-right 180)
    (branch 8 4)

    (with-output (out "/tmp/out.json")
      (encode-json *scene* out))

  (sb-ext:run-program
     "/home/leon/.cargo/bin/plot_2d"
     (list "/tmp/out.json"
           "/home/leon/org/pages/images/turtle_graphics/branch.svg"
           "-a"
           "--type"
           "svg"
           "--format"
           "20,20")
     :output *standard-output*))
branch.svg

Recursion

  (defun branch2 (steps angle)
    (repeat steps
            (with-copy (with-copy (rotate-right leaf-angle) (branch steps angle)))
            (forward 1.0 t)
            (with-copy (rotate-left leaf-angle) (branch steps angle))
            (forward 1.5 t)
            (rotate-right angle)
            (turtle-scale 0.95))
    (leaf))

 (setq *turtle* (make-turtle))

 (with-scene
 (rotate-right 180)
  (branch2 8 4)

  (with-output (out "/tmp/out.json")
    (encode-json *scene* out))

(sb-ext:run-program
   "/home/leon/.cargo/bin/plot_2d"
   (list "/tmp/out.json"
         "/home/leon/org/pages/images/turtle_graphics/branch2.svg"
         "-a"
         "--type"
         "svg"
         "--format"
         "20,20")
   :output *standard-output*))
branch2.svg

If you have an idea how this page could be improved or a comment send me a mail.