Introduction
This is a turtle graphics program which I wrote to improve my knowledge of F#. The turtle, shown as a cursor, draws lines on a canvas when given simple commands relative to its own position such as left, right and forward. These simple commands can provide building blocks for the drawing of complex shapes such as spirals and fractals. F#'s interactive prompt provides a useful means of controlling the turtle. This article will explain how to use the turtle graphics program, and show the F# and WPF code which was used to implement it, and provide some example code for generating fractal shapes using L-system grammars.
How to Use the Turtle
Install and Run F#
These instructions assume that you are using F# interactive with Visual Studio, either Visual Studio 2010 or the free shell. Information about installing F# can be found at the Microsoft F# Development Center. Open the file FSharpTurtle.fsx, select all using Ctrl-A, then press Alt-Enter to load the script into F# Interactive. A canvas with the turtle will appear.
Turtle Commands
The turtle responds to the commands left
, right
, forward
and move
or their abbreviations lt
, rt
, fd
and mv.
Each of these commands is followed by a float and two semicolons (e.g. left 90.0;;
). The left
and right
commands turn the turtle a given angle, forward
draws a line and move
moves the turtle without drawing a line. Typing clear();;
clears the canvas and returns the turtle to its original position. The default colours (white background, black line, red arrow) can be changed using the settings
variable. F# uses the <-
operator to change the values of variables.
settings.BackgroundColour <- Brushes.Black;;
settings.ArrowColour <- Brushes.Green;;
settings.LineColour <- Brushes.Red;;
Example: Drawing a Square
Typing the command below in F# Interactive draws a square on the canvas.
for i = 1 to 4 do
forward 200.0
left 90.0;;
The turtle draws a line and turns left until it returns to its original position.
Example: Drawing Spirals
let rec polyspi (angle:float)(inc:float)(side:float)(times:int) =
if times > 0 then
forward side
right angle
polyspi angle inc (side + inc)(times - 1)
else None |> ignore
I adapted the spiral pattern drawing function above from Turtle Geometry (p 18-19). In F#, the let
keyword is used to declare functions and variables while rec
indicates that the function is recursive. Calling this function with different parameters creates differently shaped spirals, so more specialised versions of the polyspi function can be created using partial function application, also known as currying. The functions declared below have their first 3 polyspi parameters pre-filled and take only the final parameter (times:int)
as an argument.
let polyspi90 = polyspi 90. 5. length
let polyspi95 = polyspi 95. 1. length
let polyspi117 = polyspi 117. 3. length
Turtle Graphics Implementation
Type Definitions
The type definitions below, which are called records, are used to store the state of the turtle. The turtle has an x y point on the canvas and an angle, represented by the record labels P
of type Point
and Angle
of type float. The keyword mutable
is used to specify that the value of the variables can be changed.
type Point = { mutable X : float
mutable Y : float}
type Turtle = { mutable P : Point
mutable Angle : float }
The code below constructs the turtle record and puts it in the middle of the window. The type Turtle
can be inferred from the record labels so it does not need to be specified.
let turtle = {P = {X = w.ActualWidth/2.;Y = w.ActualHeight/2.}; Angle = 0.}
Turtle Coordinates
Turtle commands use direction and distance from a starting point to draw lines. The program uses trigonometry to find the xy coordinates of the end point of the line. The nextPoint
function defined below takes the distance given to forward
and treats it as the hypotenuse of a right-angled triangle. This, combined with the turtle angle is used to find the lengths of the adjacent (x) and opposite (y) sides of the triangle and add them to starting point to give an endpoint for the line.
let nextPoint hypotenuse =
let newX = turtle.P.X + hypotenuse * Math.Cos(degreesRadians(turtle.Angle))
let newY = turtle.P.Y + hypotenuse * Math.Sin(degreesRadians(turtle.Angle))
let newPoint = {X = newX;Y = newY}
newPoint
WPF Canvas and Turtle Arrow Polygon
The turtle graphics are drawn on a WPF canvas. WPF canvases are not usually scrollable, but I found some C# code for a scrollable subclass of Canvas here and rewrote it in F#. The code below creates a window, scrollable canvas, scrollviewer, and an arrow to represent the turtle.
let w = new Window(Topmost=true)
w.Show()
let c = new ScrollableCanvas()
c.Background <- Brushes.White
let scrollViewer = new ScrollViewer()
scrollViewer.HorizontalScrollBarVisibility <- ScrollBarVisibility.Auto
scrollViewer.VerticalScrollBarVisibility <- ScrollBarVisibility.Auto
w.Content <- scrollViewer
scrollViewer.Content <- c
let makeArrow() =
let arrow = new Polygon()
arrow.Fill <- Brushes.Red
let p1 = new System.Windows.Point(0.,20.)
let p2 = new System.Windows.Point(25.,10.)
let p3 = new System.Windows.Point(0.,0.)
let centrePoint = new System.Windows.Point(0.5,0.5)
let pCollection = new PointCollection()
pCollection.Add p1
pCollection.Add p2
pCollection.Add p3
arrow.RenderTransformOrigin <- centrePoint
arrow.Points <- pCollection
arrow
let mutable arrow = makeArrow()
c.Children.Add(arrow)
The left and right functions defined below change the angle of the turtle, then use it to rotate the arrow polygon.
let left deg = turtle.Angle <- turtle.Angle - deg
arrow.RenderTransform <- new RotateTransform(Angle = turtle.Angle)
let right deg = turtle.Angle <- turtle.Angle + deg
arrow.RenderTransform <- new RotateTransform(Angle = turtle.Angle)
The moveTo
function moves the arrow to the given xy coordinates on the WPF canvas.
let moveTo x y = turtle.P.X <- x
turtle.P.Y <- y
Canvas.SetLeft(arrow, turtle.P.X - 12.5)
Canvas.SetTop(arrow, turtle.P.Y - 10.0)
The
forward
function uses
nextPoint
to get the endpoint of the line, draws the line, then moves the arrow cursor to the endpoint.
let forward distance =
let next = nextPoint distance
let l = new Line()
l.X1 <- turtle.P.X
l.Y1 <- turtle.P.Y
l.X2 <- next.X
l.Y2 <- next.Y
l.Stroke <- settings.LineColour
c.Children.Add(l) |> ignore
moveTo next.X next.Y
Using L-systems to Draw Fractal Shapes
Fractals and L-systems
Fractals are geometric shapes which are composed of self-similar parts. The growth of fractal shapes, including complex, organic-looking structures can be modelled using L-system grammars and turtle graphics. An L-system grammar has a set of symbols, a starting string of symbols and production rules describing how symbols are replaced with other symbols. Symbols can be variables or constants; constants remain the same each generation and variables are replaced with other symbols. Some of the symbols represent turtle drawing commands.
DOL-systems
This turtle program includes code which can be used to draw the simplest L-systems called DOL-systems, which are deterministic (there is only one production rule per symbol) and context-free (a production rule only depends on a symbol, not its neighbouring symbols).
L-system Implementation
The data type defined below can be used to describe a simple L-system. It has a string
of starting symbols Start
, a Dictionary
of production rules Rules
where the char
key is a symbol and the string
value is the resulting string
of symbols. The turtle turns a certain Angle
left or right when the shape is drawn.
type LSystem = { Start : string
Rules : Dictionary<char,string< /> />
Angle : float}
The plant like image above can be drawn using the l-system defined below.
let branching =
let rules = new Dictionary<char, />()
rules.Add('F',"FF")
rules.Add('X',"F-[[X]+X]+F[+FX]-X")
rules.Add('+',"+")
rules.Add('-',"-")
rules.Add('[',"[")
rules.Add(']',"]")
{Start = "X";Rules = rules;Angle=22.5}
The symbols in this l-system grammar (apart from X) match drawing commands in the function defined below. The pushTurtle
and popTurtle
functions add and remove the current state of the turtle from a stack, allowing a branching shape to be drawn.
let drawCommand (lsys:LSystem)(symbol:char) =
match symbol with
| 'F' -> forward length
| 'G' -> forward length
| 'f' -> move length
| '+' -> left (lsys.Angle)
| '-' -> right (lsys.Angle)
| '[' -> pushTurtle()
| ']' -> popTurtle()
| _ -> None |> ignore
The function drawLSystem
draws an L-system lsys
after generating a string
of instructions
for n
iterations.
let drawLSystem (lsys:LSystem) (n:int) =
let instructions = generateString lsys n in
for command in instructions do
drawCommand lsys command
Other L-system examples are provided in the file FSharpTurtle.fsx.
References
F#
Turtle Graphics
- Turtle Geometry - Harold Abelson and Andrea diSessa
L-systems
WPF
History
- 10th October, 2010: Initial post