Chapter 1 - Turtle Graphics

1 Setup

The following is essentially a Jupyter notebook with a haskell kernel. We will start by turning on some GHC extension, and ihaskell options. The svg files don't work well with my publishing setup.

:extension NoMonomorphismRestriction FlexibleContexts TypeFamilies

:opt svg
:opt no-lint

1.1 Imports

We will need the turtle drawing package, as well as the Cairo backend for saving the images.

import Diagrams.Backend.Cairo
import Diagrams.TwoD.Path.Turtle
import Control.Monad

1.2 Helper Functions

The original turtle geometry is done in degrees; however, once one has learned enough math, radians are usually preferred. After teaching Trigonometry several times I have found many people struggle with \(2\pi\) radians in a circle. So I think the easiest way is via "turns" of a circle. Using turns, we have

\begin{equation*} 1 \textrm{ turn } = \tau = 2\pi = 360^\circ. \end{equation*}

For me this is the most intuitive, see https://tauday.com/tau-manifesto for more information about \(\tau\).

Functions which convert from degrees or from a turn.

tau = 2*pi

fromDeg d = d/360
fromTurn t = t*360

Some short hand functions. The names should be self-explanatory:

  • ltD/left d turns left \(d\) degrees
  • rtD/right d turns right \(d\) degrees
  • fd/forward n moves the turtle forward a distance \(n\)
  • bk/backward n moves the turtle backward a distance \(n\)
  • turn t turn the positive direction (counter clockwise) \(\tau\) turns
  • lt/left t turn left \(\tau\) turns
  • rt/right t turn right \(\tau\) turns
  • pu pick the pen up, no path will be drawn for commands following one of these commands, that is until the pen is put back down
  • pd/penDown puts the pen down, a path will be drawn until the pen is pick up
  • drawTDia draws the turtle diagram in the Jupyter notebook
ltD = left
rtD = right

fd = forward
bk = backward
turn = left . fromTurn
lt = turn
rt = turn . negate
pu = penUp
pd = penDown

drawTDia = diagram . drawTurtle

2 Turtle Drawings

2.1 Basic Shapes

Let start with a square and a triangle of side length \(n\).

square n = replicateM 4 (fd n >> lt (1/4))
triangle n = replicateM 3 (fd n >> lt (1/3))

Now lets draw them

drawTDia $ square 100
square-1.png
drawTDia $ triangle 100
Sorry, your browser does not support SVG.

2.2 Simple Composite Drawings

Lets draw a house, composed of drawing a square under a triangle.

house n = do
  square n 
  pu 
  lt (1/4) 
  fd n
  rt (1/4)
  pd
  triangle n
drawTDia $ house 100
Sorry, your browser does not support SVG.

2.3 More Complex Figures

We will define a basic doodle, and then replicate that in various ways doodle, which we will call a re-do-odle.

doodle = do
  fd 100
  rt (1/4)
  fd 100
  rt (1/4)
  fd 50
  rt (1/4)
  fd 50
  rt (1/4)
  fd 100
  rt (1/4)
  fd 25
  rt (1/4)
  fd 25
  rt (1/4)
  fd 50
drawTDia doodle
Sorry, your browser does not support SVG.
redoodle4 = replicateM 4 doodle

drawTDia redoodle4
Sorry, your browser does not support SVG.
redoodle9 = replicateM 9 (doodle >> rt (1/36) >> fd 50)

drawTDia redoodle9
Sorry, your browser does not support SVG.
redoodle15 = replicateM 15 (doodle >> lt (1/8) >> fd 100)

drawTDia redoodle15
Sorry, your browser does not support SVG.

2.4 Circles and Arcs

Next we define how to draw arcs, and circles (actually a regular 1000-gon of side length \(s\)).

circSides = 1000

arc s t = replicateM n (fd s >> turn (t'/circSides))
  where
    n = round ((abs t)*circSides)
    t' = t/(abs t)

circle s = arc s 1
circles s n = replicateM n (circle s >> turn (1/ fromIntegral n))

drawTDia $ circles 1 9
Sorry, your browser does not support SVG.
petal s = replicateM 2 (arc s (1/6) >> turn (1/3))

flower s = replicateM 6 (petal s >> turn (1/6))

drawTDia $ flower 1
Sorry, your browser does not support SVG.
ray s = replicateM 2 (arc s (1/4) >> arc s (-1/4))

sun s = replicateM 9 (ray s >> turn (4/9))

drawTDia $ sun 1
Sorry, your browser does not support SVG.

2.5 Polygons and Friends

The polygon function draws a regular $n$-gon. The poly and newpoly draw other types of polygons.

polygon sides len = replicateM sides (fd len >> turn (1/s))
  where s = fromIntegral sides

poly len t = replicateM 1000 (fd len >> lt t)

newpoly len t = replicateM 10 (fd len >> lt t >> fd len >> lt (2*t))

We can reproduce all the examples in the book.

import qualified Diagrams.Prelude as D
diagram $ D.hsep 0.2 (map drawTurtle [poly 1 (fromDeg 72), poly 1 (fromDeg 60)])
Sorry, your browser does not support SVG.
diagram $ D.hsep 0.2 (map drawTurtle [poly 1 (fromDeg 144), poly 1 (fromDeg 135), poly 1 (fromDeg 108)])
Sorry, your browser does not support SVG.
diagram $ D.hsep 0.2 (map drawTurtle [newpoly 1 (fromDeg 144), newpoly 1 (fromDeg 135), newpoly 1 (fromDeg 108)])
Sorry, your browser does not support SVG.

2.6 Recursion

polySpiral 1 t = fd 1 >> lt t
polySpiral n t = fd n >> lt t >> polySpiral (n-1) t
drawTDia $ polySpiral 70 (fromDeg 95)
Sorry, your browser does not support SVG.
drawTDia $ polySpiral 70 (1/4)
Sorry, your browser does not support SVG.
drawTDia $ polySpiral 70 (1/3)
Sorry, your browser does not support SVG.
drawTDia $ polySpiral 70 (fromDeg 117)
Sorry, your browser does not support SVG.
polySpiral' l t = fd l >> lt t >> polySpiral (l+1) t

3 Exercises

3.1 Spiral Dots

dot = do
  pd
  fd 1
  bk 1
  pu
  
spiDot 1 t = fd 1 >> dot >> lt 1
spiDot n t = pu >> fd n >> dot >> lt t >> spiDot (n-1) t
drawTDia $ spiDot 70 (fromDeg 95)
Sorry, your browser does not support SVG.
drawTDia $ spiDot 70 (1/4)
Sorry, your browser does not support SVG.
drawTDia $ spiDot 70 (fromDeg 43)
Sorry, your browser does not support SVG.

Author: Ryan Jensen (ryan.jensen@mathnotes.cc)

Modified: 2020-06-01 Mon 14:59

Generated with: Emacs 26.3 (Org mode 9.3.7)