As I mentioned in my last post, I’ve been adding a turtle graphics library to Wombat to use with the other C211 libraries (matrix, image, and tree).
Edit: The most recent C211 APIs can be found here:
Edit: This post used an old version of the turtle graphics API, specifically using the function
spawn rather than
hatch in the code examples. So that the code will run in the newer version of Wombat, I have updated the examples.
Essentially, to think about turtle graphics, consider as a thought experiment a turtle standing in the middle of a giant blank white sheet of paper. Tied to the turtle’s tail is a marker, initially in black. From there, you can issue a series of commands to the turtle, for example, telling it to
move! or to
turn-right!. You can also tell the turtle to
lift-pen! and stop drawing,
drop-pen! and start again, or even
set-pen-color! to change to a different marker. Finally, there are some meta commands that allow you to either
split into more turtles that will work in parallel, to run a
block of commands and then reset back to the original state, or even to
repeat a series of commands. For anything else, just check out the API:
(c211 turtle) API
(hatch)- create a new turtle
(hatch dir)- create a new turtle with the given facing (in degrees, 0 is north, positive to the right / clockwise)
(hatch x y)- create a turtle at the point (
y) instead of the origin
(hatch x y dir)- create a turtle at (
y) with the given facing
(hatch x y dir up/down color)- create a turtle at (
y) with the given facing and a
colorpen that is either
(split t)- create a copy of a turtle so that the two can move in parallel (for branching structures)
(move! t n)- move the turtle
nunits in whichever direction it is facing; draw a line if the pen is
(move-to! t x y)- jump directly to the point (
y), preserving the original facing; draw a line if the pen is
(turtle-location t)- return the turtle’s current location as a list of the form
(turn-left! t d)- turn left / counter-clockwise this many degrees.
(turn-right! t d)- turn right / clockwise this many degrees
(turn-to! t d)- turn directly to a given facing (in degrees, 0 is north, positive to the right / clockwise)
(turtle-direction t)- return the turtle’s current facing
(lift-pen! t)- lift the turtle’s pen and stop drawing
(drop-pen! t)- drop the turtle’s pen and start drawing again
(pen-up/down? t)- return either
upif the pen is up or
downif it’s down
(set-pen-color! t c)- set the turtle’s pen’s color; colors can be accessed from
(pen-color t)- access the turtle’s pen’s current color as a color from
(draw-turtle t)- draw the turtle and any turtles
splitfrom it to the screen; if this turtle was the result of a split, its parent will not be drawn
turtle->image- convert the turtle to an image from
(draw-turtle t)is equivalent to
(draw-image (turtle->image t))
(block t cmds ...)- save the turtle’s current state then execute a series of commands; then reset the turtle to the stored state
(repeat n cmds ...)- repeat a series of commands n times; does not restore turtle state
To use the turtle graphics library, you will have to import it with
(import (c211 turtle)). After that, just give the above commands a try.
Here are some interesting turtles that I’ve written/translated from the Wikipedia page on L-Systems.
To start out with, a simple box. This shows how to move, how to turn, and how to use the
repeat macro. In this example and all of the following ones, a new turtle is hatched when calling the function and returned at the end. The functions could have been written to take a turtle as an argument, alter its state, and return nothing–that’s just not how I did it.
(define box (lambda () (let ([t (hatch)]) (repeat 4 (move! t 100) (turn-right! t 90)) t))) (draw-turtle (box))
Next, the traditional Sierpinski triangle. This is based on the L-systems Wikipedia page, using function calls for the recursive generating function. The original two inverse calls have been combined into the single loop, using the sign of the variable
d to control which way the rotation is currently going.
(define sierpinski (lambda (n) (let ([t (hatch)]) (turn-right! t 30) (let loop ([i 0] [d 60]) (if (= i n) (begin (set-pen-color! t (color 0 0 (mod (+ (color-ref (pen-color t) 'blue) (random 10)) 256))) (move! t 20)) (begin (loop (+ i 1) (- d)) (turn-right! t d) (loop (+ i 1) d) (turn-right! t d) (loop (+ i 1) (- d))))) t))) (draw-turtle (sierpinski 5))
A simple tree that starts with a single turtle and
splits into two additional turtles at each iteration. Using a proper rendering (which I haven’t actually written yet), you could see all of the branches expanding outwards in parallel.
(define tree (lambda (n) (let ([t (hatch)]) (let loop ([i 0] [t t]) (when (< i n) (set-pen-color! t (color 0 (+ 128 (random 128)) 0)) (move! t 50) (let ([l (split t)] [r (split t)]) (loop (+ i 1) t) (turn-left! l (random 45)) (loop (+ i 1) l) (turn-right! r (random 45)) (loop (+ i 1) r)))) t))) (draw-turtle (tree 5))
Rather than using
split as the tree above does, this star uses a combination of
repeat (to get the multiple arms) and
block (to reset to the center for new extension).
(define star (lambda (p n) (let ([t (hatch)]) (let loop ([i 0] [d 100]) (when (< i n) (set-pen-color! t (color 0 (div (* 255 i) n) 0)) (repeat p (turn-right! t (/ 360 p)) (block t (move! t d) (loop (+ i 1) (/ d 2)))))) t))) (draw-turtle (star 5 3))
This is a pretty fractal translated almost directly from the L-System Wikipedia page. Each recursive level became a function directly this time. The coloring is randomly selected tones from yellow to red.
(define dragon (lambda (n) (let ([t (hatch)] [angle 90] [distance 10]) (define (f) (set-pen-color! t (let* ([r (+ 128 (random 128))] [g (min r (random 255))] [b 0]) (color r g b))) (move! t distance)) (define (x i) (when (< i n) (x (+ i 1)) (turn-right! t angle) (y (+ i 1)) (f))) (define (y i) (when (< i n) (f) (x (+ i 1)) (turn-left! t angle) (y (+ i 1)))) (turn-left! t 90) (move! t distance) (x 0) t))) (draw-turtle (dragon 12))