Difference between revisions of "W1513 Patterns of Patterns"

From Coder Merlin
 
(13 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[File:11.CHOCKER -A-BLOCK.MALTA.2017.jpg|thumb|Repetition of Simple Geometric Shapes]]
[[File:11.CHOCKER -A-BLOCK.MALTA.2017.jpg|thumb|Repetition of simple geometric shapes]]
== Prerequisites ==
== Prerequisites ==
* [[W1512 Colorful Turtles]]
* [[W1512 Colorful Turtles]]
Line 6: Line 6:


== Background ==
== Background ==
Using the tools that we've learned to date, we're able to produce many types of images. In this lab, we'll focus on defining functions (with sensible parameters) that may then be repeatedly invoked to form patterns.
Using the tools that we've learned, we can produce many types of images. In this lab, we'll focus on defining functions (with sensible parameters) that can then be repeatedly invoked to form patterns.


== Prepare ==
== Prepare ==
Line 17: Line 17:


=== First Steps ===
=== First Steps ===
Open the file {{Pathname|Background.swift}} in emacs.
Open the file {{Pathname|Background.swift}} in emacs.


Let's set up the ability to draw and display a series of patterns. Add the following properties to the {{SwiftClass|Background}} class:
Let's set up the ability to draw and display a series of patterns. Add the following properties to the {{SwiftClass|Background}} class:


<syntaxhighlight lang="swift" highlight="3-5">
<syntaxhighlight lang="swift" highlight="3-6">
class Background : RenderableEntity {
class Background : RenderableEntity {


     let maxPattern = 9
     let maxPattern = 9
     var currentPattern = 1
     var currentPattern = 1
     var didPaint = false
     var didRender = false
    var rect: Rect?


     init() {
     init() {
Line 35: Line 36:
</syntaxhighlight>
</syntaxhighlight>


This will enable us to keep track of whether or not we need to paint, and if so, which pattern should be painted.
This lets us keep track of whether we need to render, and if so, which pattern should be rendered. We'll use the {{SwiftIdentifier|rect}} to remember our own size.


To indicate the pattern being painted, let's add a function that we can invoke to render a label on the canvas. Each of the following functions should be added as a method within the {{SwiftClass|Background}} class.
=== Label ===
To indicate the pattern we're rendering, let's add a function that we can render a label on the canvas. Each of the following functions should be added as a method in the {{SwiftClass|Background}} class.
<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
     func renderLabel(canvas:Canvas, patternId:Int) {
     func renderLabel(canvas:Canvas, patternId:Int) {
Line 46: Line 48:
     }
     }
</syntaxhighlight>
</syntaxhighlight>
 
=== Rendering Functions ===
Next, add nine functions for nine different patterns. Each function will have the form '''renderPattern''N''()''', where ''N'' is the number of the pattern to be rendered. For example, the first two functions would be:
Next, add nine functions for nine different patterns. Each function will have the form '''renderPattern''N''()''', where ''N'' is the number of the pattern to be rendered. For example, the first two functions would be:
<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
     func renderPattern1(canvas:Canvas) {
     func renderPattern1(canvas:Canvas) {
Line 58: Line 60:
</syntaxhighlight>
</syntaxhighlight>


Now, add an '''render''' method as follows:
Now, add a '''render''' method as follows:
<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
     override func render(canvas:Canvas) {
     override func render(canvas:Canvas) {
         if let canvasSize = canvas.canvasSize, !didPaint {
         if let canvasSize = canvas.canvasSize, !didRender {
           // Clear the entire canvas
           // Clear the entire canvas
           let clearRect = Rect(topLeft:Point(x:0, y:0), size:canvasSize)
           let clearRect = Rect(topLeft:Point(x:0, y:0), size:canvasSize)
Line 89: Line 91:
                     fatalError("Unexpected pattern: \(currentPattern)")
                     fatalError("Unexpected pattern: \(currentPattern)")
             }
             }
             didPaint = true
             didRender = true
         }
         }
     }
     }
</syntaxhighlight>
</syntaxhighlight>
 
=== Responding to Mouse Click Events===
Finally, we'll enable the canvas to respond to mouse click events. To do so requires three steps:
Finally, we'll enable the canvas to respond to mouse click events. To do so requires four steps:
 
==== Declare Conformance to Protocol ====
First, declare our intent to conform to the desired protocol:
First, declare our intent to conform to the desired protocol by ''modifying'' the existing class declaration to include '''EntityMouseClickHandler''':


<syntaxhighlight lang="swift" highlight="1">
<syntaxhighlight lang="swift" highlight="1">
Line 104: Line 106:
</syntaxhighlight>
</syntaxhighlight>


Next, let the dispatcher know that we want to receive this event as long as we belong to the {{SwiftClass|Layer}}. To do so, we register an event handler while we're being {{SwiftFunction|setup}} and unregister the same handler during {{SwiftClass|teardown}}.
==== Notify Dispatcher ====
Next, let the dispatcher know that we want to receive this event as long as we belong to the {{SwiftClass|Layer}}. To do so, we register an event handler while we're being {{SwiftIdentifier|setup}} and unregister the same handler during {{SwiftIdentifier|teardown}}. We'll also keep track of our size during {{SwiftIdentifier|setup}}. We need this to report our location and size to the {{SwiftClass|Layer}} so that it knows if we've been clicked on.


<syntaxhighlight lang="swift" highlight="5">
<syntaxhighlight lang="swift">
     override func setup(canvasSize:Size, canvas:Canvas) {
     override func setup(canvasSize:Size, canvas:Canvas) {
         dispatcher.registerEntityMouseClickHandler(handler:self)
         dispatcher.registerEntityMouseClickHandler(handler:self)
        rect = Rect(size: canvasSize)
     }
     }


Line 116: Line 120:
</syntaxhighlight>
</syntaxhighlight>


Finally, and an '''onClick()''' method so that we may cycle through each of the patterns. Note that the onClick() method does not provide us with a Canvas, so we'll need to update our properties in order to impact the next '''render()''' and inform it to render the next pattern.
==== Implement Event Handler ====
Finally, and an '''onClick()''' method so that we can cycle through each of the patterns. Note that the onClick() method does not provide us with a Canvas, so we'll need to update our properties to affect the next '''render()''' and inform it to render the next pattern.


<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
     override func onClick(location:Point) {
     func onEntityMouseClick(globalLocation: Point) {
         currentPattern += 1
         currentPattern += 1
         if (currentPattern > maxPattern) {
         if (currentPattern > maxPattern) {
             currentPattern = 1
             currentPattern = 1
         }
         }
         didPaint = false
         didRender = false
    }
</syntaxhighlight>
 
==== Report Our Size to the Layer ====
<syntaxhighlight lang="swift">
    override func boundingRect() -> Rect {
        if let rect = rect {
            return rect
        } else {
            return Rect()
        }
     }
     }
</syntaxhighlight>
</syntaxhighlight>




{{RunProgram|Run the program and view in a browser before continuing. Be sure to click on the Canvas several times and observe the behavior.}}
{{RunProgram|Run the program and view in a browser before continuing. Be sure to click on the Canvas several times and observe the behavior.}}


=== Exercises ===
=== Exercises ===


While working through these exercises:
While working through these exercises:
* Use well defined functions that do one thing and do one thing well
* '''Programmatically''' produce the pattern; the use of images is strictly prohibited
* Use well-defined functions that do one thing and do one thing well
* Do not repeat the same functionality in different functions
* Do not repeat the same functionality in different functions
* Use appropriate and meaningful names for all elements
* Use appropriate and meaningful names for all elements
* Ensure that the pattern number remains visible and is not obscured by the pattern
* Ensure that the pattern number remains visible and is not obscured by the pattern


1. Reproduce the below pattern using the function labeled '''paintPattern1()''' <br/>
1. Reproduce the below pattern using the function labeled '''renderPattern1()''' <br/>
[[File:IgisShell-Pattern-1.png]]
[[File:IgisShell-Pattern-1.png]]
<br/>
<br/>


2. Reproduce the below pattern using the function labeled '''paintPattern2()''' <br/>
2. Reproduce the below pattern using the function labeled '''renderPattern2()''' <br/>
[[File:IgisShell-Pattern-2.png]]
[[File:IgisShell-Pattern-2.png]]
<br/>
<br/>


3. Reproduce the below pattern using the function labeled '''paintPattern3()''' <br/>
3. Reproduce the below pattern using the function labeled '''renderPattern3()''' <br/>
[[File:IgisShell-Pattern-3.png]]
[[File:IgisShell-Pattern-3.png]]
<br/>
<br/>


4. Reproduce the below pattern using the function labeled '''paintPattern4()''' <br/>
4. Reproduce the below pattern using the function labeled '''renderPattern4()''' <br/>
[[File:IgisShell-Pattern-4.png]]
[[File:IgisShell-Pattern-4.png]]
<br/>
<br/>


5. Reproduce the below pattern using the function labeled '''paintPattern5()''' <br/>
5. Reproduce the below pattern using the function labeled '''renderPattern5()''' <br/>
[[File:IgisShell-Pattern-5.png]]
[[File:IgisShell-Pattern-5.png]]
<br/>
<br/>


6. Reproduce the below pattern using the function labeled '''paintPattern6()''' <br/>
6. Reproduce the below pattern using the function labeled '''renderPattern6()''' <br/>
[[File:IgisShell-Pattern-6.png]]
[[File:IgisShell-Pattern-6.png]]
<br/>
<br/>


7. Reproduce the below pattern using the function labeled '''paintPattern7()''' <br/>
7. Reproduce the below pattern using the function labeled '''renderPattern7()''' <br/>
[[File:IgisShell-Pattern-7.png]]
[[File:IgisShell-Pattern-7.png]]
<br/>
<br/>


8. Reproduce the below pattern using the function labeled '''paintPattern8()''' <br/>
8. Reproduce the below pattern using the function labeled '''renderPattern8()''' <br/>
[[File:IgisShell-Pattern-8.png]]
[[File:IgisShell-Pattern-8.png]]
<br/>
<br/>


9. Reproduce the below pattern using the function labeled '''paintPattern9()''' <br/>
9. Reproduce the below pattern using the function labeled '''renderPattern9()''' <br/>
[[File:IgisShell-Pattern-9.png]]
[[File:IgisShell-Pattern-9.png]]
<br/>
<br/>
Line 190: Line 207:
== Exercises ==
== Exercises ==
{{W1513-Exercises}}
{{W1513-Exercises}}
* {{MMMAssignment|M1513-28}}
[[Category:IGIS]]

Latest revision as of 07:26, 15 May 2022

Within these castle walls be forged Mavens of Computer Science ...
— Merlin, The Coder
Repetition of simple geometric shapes

Prerequisites[edit]

Research[edit]

Background[edit]

Using the tools that we've learned, we can produce many types of images. In this lab, we'll focus on defining functions (with sensible parameters) that can then be repeatedly invoked to form 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 W1513


Enter the Sources/ScenesShell directory of the new project:

ty-cam@codermerlin:~/Experiences$  cd W1513/Sources/ScenesShell/


Start button green arrow
Run the program.

ty-cam@codermerlin:~/Experiences/W1513/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.)

Hint.pngHelpful Hint
It's useful to bookmark this page in your browser.

Experiment[edit]

Stop button red ex
Stop the running program.

Return to the console and press CONTROL-C

First Steps[edit]

Open the file Background.swift in emacs.

Let's set up the ability to draw and display a series of patterns. Add the following properties to the Background class:

class Background : RenderableEntity {

    let maxPattern = 9
    var currentPattern = 1
    var didRender = false
    var rect: Rect?

    init() {
        // Using a meaningful name can be helpful for debugging
        super.init(name:"Background")
    }
}

This lets us keep track of whether we need to render, and if so, which pattern should be rendered. We'll use the rect to remember our own size.

Label[edit]

To indicate the pattern we're rendering, let's add a function that we can render a label on the canvas. Each of the following functions should be added as a method in the Background class.

    func renderLabel(canvas:Canvas, patternId:Int) {
        let text = Text(location:Point(x:15, y:40), text:"\(patternId)")
        text.font = "30pt Arial"
        canvas.render(FillStyle(color:Color(.black)))
        canvas.render(text)
    }

Rendering Functions[edit]

Next, add nine functions for nine different patterns. Each function will have the form renderPatternN(), where N is the number of the pattern to be rendered. For example, the first two functions would be:

    func renderPattern1(canvas:Canvas) {
        renderLabel(canvas:canvas, patternId:1)
    }

    func renderPattern2(canvas:Canvas) {
        renderLabel(canvas:canvas, patternId:2)
    }

Now, add a render method as follows:

    override func render(canvas:Canvas) {
        if let canvasSize = canvas.canvasSize, !didRender {
           // Clear the entire canvas
           let clearRect = Rect(topLeft:Point(x:0, y:0), size:canvasSize)
           let clearRectangle = Rectangle(rect:clearRect, fillMode:.clear)
           canvas.render(clearRectangle)

           switch (currentPattern) {
                case 1:
                    renderPattern1(canvas:canvas)
                case 2:
                    renderPattern2(canvas:canvas)
                case 3:
                    renderPattern3(canvas:canvas)
                case 4:
                    renderPattern4(canvas:canvas)
                case 5:
                    renderPattern5(canvas:canvas)
                case 6:
                    renderPattern6(canvas:canvas)
                case 7:
                    renderPattern7(canvas:canvas)
                case 8:
                    renderPattern8(canvas:canvas)
                case 9:
                    renderPattern9(canvas:canvas)
                default:
                    fatalError("Unexpected pattern: \(currentPattern)")
            }
            didRender = true
        }
    }

Responding to Mouse Click Events[edit]

Finally, we'll enable the canvas to respond to mouse click events. To do so requires four steps:

Declare Conformance to Protocol[edit]

First, declare our intent to conform to the desired protocol by modifying the existing class declaration to include EntityMouseClickHandler:

class Background : RenderableEntity, EntityMouseClickHandler {
   ...
}

Notify Dispatcher[edit]

Next, let the dispatcher know that we want to receive this event as long as we belong to the Layer. To do so, we register an event handler while we're being setup and unregister the same handler during teardown. We'll also keep track of our size during setup. We need this to report our location and size to the Layer so that it knows if we've been clicked on.

    override func setup(canvasSize:Size, canvas:Canvas) {
        dispatcher.registerEntityMouseClickHandler(handler:self)
        rect = Rect(size: canvasSize)
    }

    override func teardown() {
        dispatcher.unregisterEntityMouseClickHandler(handler:self)
    }

Implement Event Handler[edit]

Finally, and an onClick() method so that we can cycle through each of the patterns. Note that the onClick() method does not provide us with a Canvas, so we'll need to update our properties to affect the next render() and inform it to render the next pattern.

    func onEntityMouseClick(globalLocation: Point) {
        currentPattern += 1
        if (currentPattern > maxPattern) {
            currentPattern = 1
        }
        didRender = false
    }

Report Our Size to the Layer[edit]

    override func boundingRect() -> Rect {
        if let rect = rect {
            return rect
        } else {
            return Rect()
        }
    }


Start button green arrow
Run the program and view in a browser before continuing. Be sure to click on the Canvas several times and observe the behavior.

Exercises[edit]

While working through these exercises:

  • Programmatically produce the pattern; the use of images is strictly prohibited
  • Use well-defined functions that do one thing and do one thing well
  • Do not repeat the same functionality in different functions
  • Use appropriate and meaningful names for all elements
  • Ensure that the pattern number remains visible and is not obscured by the pattern

1. Reproduce the below pattern using the function labeled renderPattern1()
IgisShell-Pattern-1.png

2. Reproduce the below pattern using the function labeled renderPattern2()
IgisShell-Pattern-2.png

3. Reproduce the below pattern using the function labeled renderPattern3()
IgisShell-Pattern-3.png

4. Reproduce the below pattern using the function labeled renderPattern4()
IgisShell-Pattern-4.png

5. Reproduce the below pattern using the function labeled renderPattern5()
IgisShell-Pattern-5.png

6. Reproduce the below pattern using the function labeled renderPattern6()
IgisShell-Pattern-6.png

7. Reproduce the below pattern using the function labeled renderPattern7()
IgisShell-Pattern-7.png

8. Reproduce the below pattern using the function labeled renderPattern8()
IgisShell-Pattern-8.png

9. Reproduce the below pattern using the function labeled renderPattern9()
IgisShell-Pattern-9.png

Key Concepts[edit]

Key ConceptsKeyConceptsIcon.png
  • Well-defined functions
    • should do only one thing and one thing well
    • should be relatively short and easy to understand
    • should base output only on input (and perhaps an object's properties)
      • specifically, avoid referencing global variables
    • proper use of parameters enable functions to be reused under different circumstances
  • Best Practices
    • DRY because DIE
      • DRY - Don't Repeat Yourself
      • DIE - Duplication Is Evil

Exercises[edit]

ExercisesExercisesIcon.png
  • Produce all patterns specified above using graphic functions and the correct function name. (You may not use the Image class.)
  •  M1513-28  Complete  Merlin Mission Manager  Mission M1513-28.