W1512 Colorful Turtles
Prerequisites[edit]
Research[edit]
Background[edit]
Turtle graphics enable us to not only lift and drop the pen, but also to change the pen color and thickness. Using these tools and prior knowledge, we can generate a wide variety of colorful and complex patterns.
Prepare[edit]
Create a new Scenes shell project within your Experiences directory:
Enter the Sources/ScenesShell directory of the new project:
![]() |
Run the program.
ty-cam@codermerlin:~/Experiences/W1512/Sources/ScenesShell$ run
|
Open a browser (or use a new tab on an already-open browser). Then, go to the URL: https://www.codermerlin.com/igis/user-name/
![]() |
Caution |
|
You'll know you're successful if you see the title bar change to "Coder Merlin: IGIS". (The browser window will be blank because we haven't added any graphics yet.)
![]() |
Helpful Hint |
It's useful to bookmark this page in your browser. |
Experiment[edit]
![]() |
Stop the running program.
Return to the console and press CONTROL-C |
First Steps[edit]
Open the file Background.swift in emacs.
Add a new property to the Background class:
class Background : RenderableEntity {
var didDraw = false
init() {
// Using a meaningful name can be helpful for debugging
super.init(name:"Background")
}
}
This will enable us to keep track of whether or not the turtle completed its drawing mission.
Add a new method (below init) as follows:
override func render(canvas:Canvas) {
if let canvasSize = canvas.canvasSize, !didDraw {
let turtle = Turtle(canvasSize:canvasSize)
turtle.forward(steps:100)
turtle.right(degrees:90)
turtle.forward(steps:100)
turtle.right(degrees:90)
turtle.forward(steps:100)
turtle.right(degrees:90)
turtle.forward(steps:100)
turtle.right(degrees:90)
canvas.render(turtle)
didDraw = true
}
}
The conditional that we added will evaluate to true only if the size of the canvas is available and we didn't yet draw with the turtle. If the consequent is executed, the very last statement will set the flag didDraw to true, ensuring that the consequent executes only once.
The method creates a new Turtle. (Remember that the initial position of the turtle is home. In the home position, the turtle is located in the center of the canvas and pointed up (northward). We then:
- Tell the turtle to move forward 100 steps
- Turn right (clockwise) 90 degrees
- Tell the turtle to move forward 100 steps
- Turn right (clockwise) 90 degrees
- Tell the turtle to move forward 100 steps
- Turn right (clockwise) 90 degrees
- Tell the turtle to move forward 100 steps
- Turn right (clockwise) 90 degrees
Remember to save the file, then suspend emacs.
![]() |
Run the program and view in a browser before continuing. |
It appears that several of those steps were repeated. As we've learned, a better option to organize this code would be to use a loop. Let's refactor our code as follows:
override func render(canvas:Canvas) {
if let canvasSize = canvas.canvasSize, !didDraw {
let turtle = Turtle(canvasSize:canvasSize)
for _ in 1 ... 4 {
turtle.forward(steps:100)
turtle.right(degrees:90)
}
canvas.render(turtle)
didDraw = true
}
}
Remember to save the file, then suspend emacs.
![]() |
Run the program and view in a browser before continuing. |
While this is an improvement, we can do better. Let's refactor some more and move the interesting functionality to a separate function and invoke that function from render.
func renderSquare(turtle: Turtle) {
for _ in 1 ... 4 {
turtle.forward(steps: 100)
turtle.right(degrees: 90)
}
}
override func render(canvas:Canvas) {
if let canvasSize = canvas.canvasSize, !didDraw {
let turtle = Turtle(canvasSize: canvasSize)
renderSquare(turtle: turtle)
canvas.render(turtle)
didDraw = true
}
}
![]() |
Run the program and view in a browser before continuing. |
Let's add a parameter to the function enabling us to make a square of any color and any size.
func renderSquare(turtle: Turtle, color: Color, width: Int) {
turtle.penColor(color:color)
for _ in 1 ... 4 {
turtle.forward(steps: width)
turtle.right(degrees: 90)
}
}
override func render(canvas:Canvas) {
if let canvasSize = canvas.canvasSize, !didDraw {
let turtle = Turtle(canvasSize:canvasSize)
renderSquare(turtle:turtle, color:Color(.red), width:50)
canvas.render(turtle)
didDraw = true
}
}
![]() |
Run the program and view in a browser before continuing. |
First Pattern[edit]
Let's make good use of our function and invoke it in a loop, drawing 50 squares of different sizes.
override func render(canvas:Canvas) {
if let canvasSize = canvas.canvasSize, !didDraw {
let turtle = Turtle(canvasSize:canvasSize)
for i in 1 ... 50 {
let width = i * 10
renderSquare(turtle: turtle, color: Color(.red), width: width)
}
canvas.render(turtle)
didDraw = true
}
}
![]() |
Run the program and view in a browser before continuing. |
Now, let's change the colors in a specific pattern. To do so, we'll define an array to hold some colors, and then select each color in turn.
override func render(canvas:Canvas) {
if let canvasSize = canvas.canvasSize, !didDraw {
let turtle = Turtle(canvasSize:canvasSize)
let colors = [Color(.palegreen), Color(.mediumspringgreen), Color(.limegreen), Color(.lime)]
var colorIndex = 0
for i in 1 ... 50 {
let width = i * 10
let color = colors[colorIndex]
renderSquare(turtle:turtle, color:color, width:width)
colorIndex = (colorIndex + 1) % colors.count
}
canvas.render(turtle)
didDraw = true
}
}
![]() |
Observe, Ponder, and Journal Section 1 |
|
![]() |
Run the program and view in a browser before continuing. |
More Interesting Patterns[edit]
What would happen if we add just one line of code that rotates the turtle a few degrees after we draw each square?
override func render(canvas:Canvas) {
if let canvasSize = canvas.canvasSize, !didDraw {
let turtle = Turtle(canvasSize:canvasSize)
let colors = [Color(.palegreen), Color(.mediumspringgreen), Color(.limegreen), Color(.lime)]
var colorIndex = 0
for i in 1 ... 50 {
let width = i * 10
let color = colors[colorIndex]
renderSquare(turtle:turtle, color:color, width:width)
colorIndex = (colorIndex + 1) % colors.count
turtle.right(degrees:5)
}
canvas.render(turtle)
didDraw = true
}
}
![]() |
Observe, Ponder, and Journal Section 2 |
Before running the program, give the above change some thought.
|
![]() |
Run the program and view in a browser before continuing. |
![]() |
Observe, Ponder, and Journal Section 3 |
|
Exercises[edit]
![]() |
Exercises |
|
Key Concepts[edit]
![]() |
Key Concepts |
|