Difference between revisions of "W1512 Colorful Turtles"
Jeff-strong (talk | contribs) m (Editorial review and minor corrections) |
|||
(11 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
[[File:Hawaii turtle 2.JPG|thumb|Hawaii | [[File:Hawaii turtle 2.JPG|thumb|Hawaii turtle]] | ||
== Prerequisites == | == Prerequisites == | ||
* [[W1511 Walking Turtles]] | * [[W1511 Walking Turtles]] | ||
Line 7: | Line 7: | ||
== Background == | == Background == | ||
Turtle graphics enable us to | Turtle graphics enable us to lift and drop the pen and to change the pen color and thickness. Using these tools and knowledge you gained from earlier experiences, you can generate a wide variety of colorful and complex patterns. | ||
== Prepare == | |||
{{ScenesShellPrepare|W1512}} | |||
== Experiment == | == Experiment == | ||
{{StopProgram|Stop the running program. | |||
Return to the ''console'' and press {{SpecialKey|CONTROL|C}} | |||
}} | |||
=== | === First Steps === | ||
Open the file {{Pathname|Background.swift}} in emacs. | |||
Add a new property to the {{SwiftClass|Background}} class: | |||
<syntaxhighlight lang=" | <syntaxhighlight lang="swift" highlight="3"> | ||
class Background : RenderableEntity { | |||
var didDraw = false | |||
init() { | |||
// Using a meaningful name can be helpful for debugging | |||
super.init(name:"Background") | |||
} | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
This lets us keep track of whether the turtle completed its drawing mission. | |||
Add a new method (below {{SwiftIdentifier|init}}) as follows: | |||
{{ | |||
<syntaxhighlight lang="swift"> | <syntaxhighlight lang="swift"> | ||
override func render(canvas:Canvas) { | override func render(canvas:Canvas) { | ||
Line 67: | Line 49: | ||
turtle.forward(steps:100) | turtle.forward(steps:100) | ||
turtle.right(degrees:90) | turtle.right(degrees:90) | ||
canvas. | canvas.render(turtle) | ||
didDraw = true | didDraw = true | ||
Line 74: | Line 56: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The method creates a new Turtle. | The conditional that we added evaluates 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 sets 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 in the center of the canvas and pointed up (northward). We then: | |||
# Tell the turtle to move forward 100 steps | # Tell the turtle to move forward 100 steps | ||
# Turn right (clockwise) 90 degrees | # Turn right (clockwise) 90 degrees | ||
Line 84: | Line 68: | ||
# Turn right (clockwise) 90 degrees | # Turn right (clockwise) 90 degrees | ||
Remember to save the file, then suspend emacs. | |||
{{RunProgram|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. | |||
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: | |||
<syntaxhighlight lang="swift" highlight="5-8"> | <syntaxhighlight lang="swift" highlight="5-8"> | ||
override func render(canvas:Canvas) { | override func render(canvas:Canvas) { | ||
Line 98: | Line 83: | ||
turtle.right(degrees:90) | turtle.right(degrees:90) | ||
} | } | ||
canvas. | canvas.render(turtle) | ||
didDraw = true | didDraw = true | ||
Line 105: | Line 90: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Remember to save the file, then suspend emacs. | |||
{{RunProgram|Run the program and view in a browser before continuing.}} | |||
While this is an improvement, we can do better. | |||
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 {{SwiftIdentifier|render}}. | |||
<syntaxhighlight lang="swift" highlight="1-6,12"> | <syntaxhighlight lang="swift" highlight="1-6,12"> | ||
func | func renderSquare(turtle: Turtle) { | ||
for _ in 1 ... 4 { | for _ in 1 ... 4 { | ||
turtle.forward(steps:100) | turtle.forward(steps: 100) | ||
turtle.right(degrees:90) | turtle.right(degrees: 90) | ||
} | } | ||
} | } | ||
Line 120: | Line 107: | ||
if let canvasSize = canvas.canvasSize, !didDraw { | if let canvasSize = canvas.canvasSize, !didDraw { | ||
let turtle = Turtle(canvasSize:canvasSize) | let turtle = Turtle(canvasSize: canvasSize) | ||
renderSquare(turtle: turtle) | |||
canvas. | canvas.render(turtle) | ||
didDraw = true | didDraw = true | ||
Line 128: | Line 115: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{RunProgram|Run the program and view in a browser before continuing.}} | |||
Let's add a parameter to the function | Let's add a parameter to the function that lets us make a square of any color and any size. | ||
<syntaxhighlight lang="swift" highlight="1-2,4,13"> | <syntaxhighlight lang="swift" highlight="1-2,4,13"> | ||
func | func renderSquare(turtle: Turtle, color: Color, width: Int) { | ||
turtle.penColor(color:color) | turtle.penColor(color:color) | ||
for _ in 1 ... 4 { | for _ in 1 ... 4 { | ||
turtle.forward(steps:width) | turtle.forward(steps: width) | ||
turtle.right(degrees:90) | turtle.right(degrees: 90) | ||
} | } | ||
} | } | ||
Line 147: | Line 133: | ||
let turtle = Turtle(canvasSize:canvasSize) | let turtle = Turtle(canvasSize:canvasSize) | ||
renderSquare(turtle:turtle, color:Color(.red), width:50) | |||
canvas. | canvas.render(turtle) | ||
didDraw = true | didDraw = true | ||
Line 154: | Line 140: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{RunProgram|Run the program and view in a browser before continuing.}} | |||
=== First Pattern === | === First Pattern === | ||
Line 165: | Line 152: | ||
for i in 1 ... 50 { | for i in 1 ... 50 { | ||
let width = i * 10 | let width = i * 10 | ||
renderSquare(turtle: turtle, color: Color(.red), width: width) | |||
} | } | ||
canvas. | canvas.render(turtle) | ||
didDraw = true | didDraw = true | ||
Line 173: | Line 160: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{RunProgram|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. | |||
Now, let's change the colors in a specific pattern. | |||
<syntaxhighlight lang="swift" highlight="5-6,9-11"> | <syntaxhighlight lang="swift" highlight="5-6,9-11"> | ||
override func render(canvas:Canvas) { | override func render(canvas:Canvas) { | ||
Line 188: | Line 174: | ||
let width = i * 10 | let width = i * 10 | ||
let color = colors[colorIndex] | let color = colors[colorIndex] | ||
renderSquare(turtle:turtle, color:color, width:width) | |||
colorIndex = (colorIndex + 1) % colors.count | colorIndex = (colorIndex + 1) % colors.count | ||
} | } | ||
canvas. | canvas.render(turtle) | ||
didDraw = true | didDraw = true | ||
Line 197: | Line 183: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Take careful note of how the colors are selected in turn. | {{Observe|Section 1| | ||
* Take careful note of how the colors are selected in turn. What prevents us from running off the right edge of the array? | |||
}} | |||
{{RunProgram|Run the program and view in a browser before continuing.}} | |||
=== More Interesting Patterns === | === More Interesting Patterns === | ||
Line 213: | Line 202: | ||
let width = i * 10 | let width = i * 10 | ||
let color = colors[colorIndex] | let color = colors[colorIndex] | ||
renderSquare(turtle:turtle, color:color, width:width) | |||
colorIndex = (colorIndex + 1) % colors.count | colorIndex = (colorIndex + 1) % colors.count | ||
turtle.right(degrees:5) | turtle.right(degrees:5) | ||
} | } | ||
canvas. | canvas.render(turtle) | ||
didDraw = true | didDraw = true | ||
Line 223: | Line 212: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{Observe|Section 2| | |||
Before running the program, give the above change some thought. | |||
# What do you predict will happen? | |||
# Before proceeding, draw your hypothesis on graph paper. | |||
}} | |||
{{RunProgram|Run the program and view in a browser before continuing.}} | |||
{{Observe|Section 3| | |||
# | # Was your hypothesis correct? If not, why not? | ||
}} | |||
==== | == Exercises == | ||
{{W1512-Exercises}} | |||
* {{MMMAssignment|M1512-28}} | |||
== Key Concepts == | == Key Concepts == | ||
* Beautiful patterns | {{KeyConcepts| | ||
** Polygons | * Beautiful patterns can be generated using simple polygons | ||
** After drawing each polygon, the turtle's starting position | ** Polygons can be drawn in various sizes | ||
** As more and more polygons are drawn, a pattern | ** After drawing each polygon, the turtle's starting position or rotation, or both, are slightly modified so that the subsequent polygon is subtly different | ||
** As more and more polygons are drawn, a pattern emerges | |||
* Defining functions to contain repeated code greatly aids comprehension | * Defining functions to contain repeated code greatly aids comprehension | ||
* A series of colors | * A series of colors can be represented in an array | ||
** The expression '''index | ** The expression '''index {{Equal}} (index + 1) % count''' is a very common paradigm for rotating through a series of elements in an array | ||
}} | |||
[[Category:IGIS]] |
Latest revision as of 17:23, 27 April 2022
Prerequisites[edit]
Research[edit]
Background[edit]
Turtle graphics enable us to lift and drop the pen and to change the pen color and thickness. Using these tools and knowledge you gained from earlier experiences, you can generate a wide variety of colorful and complex patterns.
Prepare[edit]
Create a new Scenes shell project within your Experiences directory:
ty-cam@codermerlin:~$ cd ~/Experiences
ty-cam@codermerlin:~/Experiences$ git clone https://github.com/TheCoderMerlin/ScenesShellBasic W1512
Enter the Sources/ScenesShell directory of the new project:
ty-cam@codermerlin:~/Experiences$ cd W1512/Sources/ScenesShell/
Run the program.
ty-cam@codermerlin:~/Experiences/W1512/Sources/ScenesShell$ run |
Ensure that you are logged on to the wiki. Then, click on the Tools menu followed by right-clicking on IGIS and selecting the menu item Open in New Window or Open in New Tab.
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.)
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 lets us keep track of whether 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 evaluates 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 sets 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 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 that lets us 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
}
}
- Take careful note of how the colors are selected in turn. What prevents us from running off the right edge of the array?
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
}
}
Before running the program, give the above change some thought.
- What do you predict will happen?
- Before proceeding, draw your hypothesis on graph paper.
Run the program and view in a browser before continuing. |
- Was your hypothesis correct? If not, why not?
Exercises[edit]
- J1512 Create a journal and answer all questions. Be sure to include all sections of the journal, properly formatted. Upload an image of your graph paper drawing using an appropriate filename which includes "J1512" (such as J1512.png).
- Produce a new pattern by repeatedly drawing a polygon (of no fewer than five sides) of various sizes at various angles. Define your own color scheme and rotate through the colors.
- Before drawing your pattern, render to the canvas an aesthetic background using the drawing primitives that were covered in earlier projects (e.g. Ellipses, Rectangles, Images).
- M1512-28 Complete Merlin Mission Manager Mission M1512-28.
Key Concepts[edit]
- Beautiful patterns can be generated using simple polygons
- Polygons can be drawn in various sizes
- After drawing each polygon, the turtle's starting position or rotation, or both, are slightly modified so that the subsequent polygon is subtly different
- As more and more polygons are drawn, a pattern emerges
- Defining functions to contain repeated code greatly aids comprehension
- A series of colors can be represented in an array
- The expression index = (index + 1) % count is a very common paradigm for rotating through a series of elements in an array