Difference between revisions of "W1521 Moving Along"

From Coder Merlin
Line 7: Line 7:


== Background ==
== Background ==
An '''event''' is an asynchronous occurrence to which our software may react through the implementation of an '''event handler'''.  Events are often triggered by either the user (e.g. moving the mouse or pressing a key) or by the system (e.g. timers, screen refresh).  Handling user-generated events in this manner enables our system to be more ''responsive'', because our program has the opportunity to react to the event as it occurs.  An alternative method involved '''polling''', in which we periodically check to see if the state of a system has changed.  For example, we can periodically check to see if a key has been pressed on the keyboard.  Generally, polling leads to a less than ideal experience for the user.  We may, for example, miss a keypress because we were busy doing something else, or we may respond much more slowly and not meet the user's expectations.  In this lab, we'll be using the '''update''' event handler to refresh the objects drawn on the canvas and the '''onClick''' event to respond to a mouse click.  The '''update''' event handler is invoked by the system periodically to refresh the canvas, while the '''onClick''' event is generated as a result of the user clicking the mouse.
Using the tools that we've learned to date, we're able to produce many types of still images.  In this lab, we'll focus on defining objects which persist throughout our Igis session, enabling us to modify the properties of these objects to create simple animations.
Using the tools that we've learned to date, we're able to produce many types of still images.  In this lab, we'll focus on defining objects which persist throughout our Igis session, enabling us to modify the properties of these objects to create simple animations.



Revision as of 10:36, 9 February 2019

Within these castle walls be forged Mavens of Computer Science ...
— Merlin, The Coder
Animated Horse

Prerequisites[edit]

Research[edit]

Background[edit]

An event is an asynchronous occurrence to which our software may react through the implementation of an event handler. Events are often triggered by either the user (e.g. moving the mouse or pressing a key) or by the system (e.g. timers, screen refresh). Handling user-generated events in this manner enables our system to be more responsive, because our program has the opportunity to react to the event as it occurs. An alternative method involved polling, in which we periodically check to see if the state of a system has changed. For example, we can periodically check to see if a key has been pressed on the keyboard. Generally, polling leads to a less than ideal experience for the user. We may, for example, miss a keypress because we were busy doing something else, or we may respond much more slowly and not meet the user's expectations. In this lab, we'll be using the update event handler to refresh the objects drawn on the canvas and the onClick event to respond to a mouse click. The update event handler is invoked by the system periodically to refresh the canvas, while the onClick event is generated as a result of the user clicking the mouse.

Using the tools that we've learned to date, we're able to produce many types of still images. In this lab, we'll focus on defining objects which persist throughout our Igis session, enabling us to modify the properties of these objects to create simple animations.

Experiment[edit]

Getting Started[edit]

Breathe-document-new

Begin a new project


Create an Igis shell project within your "project" directory.

cd ~/projects
git clone https://github.com/TangoGolfDigital/IgisShell IgisShell-MovingAlong

Enter into the Sources directory of the new project.

cd IgisShell-MovingAlong/Sources/IgisShell/

Build the project. (This may take some time.)

swift build
Start button green arrow

Run the project.


swift run

Open a browser (or use a new tab on an already-open browser). Go to the URL: http://www.codermerlin.com/users/user-name/dyn/index.html

NOTE: You MUST change user-name to your actual user name. For example, http://www.codermerlin.com/users/john-williams/dyn/index.html

You'll know your 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.)

Oxygen480-actions-help-hint.svg Helpful hint: It's useful to bookmark this page in your browser.

First Steps[edit]

Let's set start by creating a ball. Edit file "main.swift":

emacs main.swift

Edit the file by finding the definition of the Painter class. Before the init constructor, add the following:

    let ball : Ellipse
    var coordinatesSet = false

This defines a property of type Ellipse, named "ball". It also defines a boolean flag used to determine if we've set the initial coordinates.

We'll need to initialize the ball in our init constructor. Edit the constructor as follows:

    required init() {
        ball = Ellipse(center:Point(x:0, y:0), radiusX:30, radiusY:30, fillMode:.fillAndStroke)
    }

The setup method is invoked once, when our canvas is first setup. We'll use this opportunity to set the style of our ellipse. Edit setup as follows:

    override func setup(canvas:Canvas) {
        let strokeStyle = StrokeStyle(color:Color(.orange))
        let fillStyle = FillStyle(color:Color(.red))
        let lineWidth = LineWidth(width:5)
        canvas.paint(strokeStyle, fillStyle, lineWidth)
    }


We've prepared the attributes for our canvas but we haven't yet drawn the ball. We'll do that in the update method. The update method is invoked frequently for as long as our application is running. We'll use it to set the initial coordinates (if they haven't yet been set) and draw the ball in the center of the screen. Add an update method as follows, immediately after the setup method.

    override func update(canvas:Canvas) {
        if let canvasSize = canvas.canvasSize {
            // Set the coordinates to the center of the screen if they've not yet been set
            if !coordinatesSet { 
                let canvasCenter = Point(x:canvasSize.width/2, y:canvasSize.height/2)
                ball.center = canvasCenter
                coordinatesSet = true
            }
            canvas.paint(ball)
        }
    }

Note that we can't set the coordinates of the ball earlier than the update method because the size of the canvas isn't known prior to the invocation of this method.

Start button green arrow

Run the project.

View the results in the browser. Be sure that you understand the role of each method.

Mouse Clicks[edit]

Let's add another method, onClick, which is invoked when the mouse button is clicked. The method provides the location on the canvas at the position that the mouse was clicked. We can use the location to move the ball from its original, center position to the location that the mouse was clicked.

Add the following:

    override func onClick(location:Point) {
        ball.center = location
    }

Note the manner in which we took advantage of objects by assigning an instance of Point directly to the ball's center, which is also a property of type Point.

Start button green arrow

Run the project.

View the results in the browser. Did the ball move to the new location? If not, what happened? Why?

Clear the Canvas[edit]

Let's add another flag to track whether or not we need to draw the ball. We only need to draw it if it's moved. If it hasn't moved, then there's no reason to redraw it. Add the following property (below "var coordinateSet = false"):

    var needToDraw = true

We initialize this property to true because at this point, we haven't yet drawn the ball. Each time we do draw the ball, we'll set this property to false. We won't change the flag to true unless something has changed. Modify the update method so that we check the flag before painting the ball and subsequently clear the flag:

        if needToDraw {
                canvas.paint(ball)
                needToDraw = false
        }

We'll also need to modify the onClick method to set this flag to true, indicating that we'll need to draw the ball (because we've changed the center).

    override func onClick(location:Point) {
        ball.center = location
        needToDraw = true
    }


Finally, we'll need to clear the canvas. We do this by painting a rectangle and specifying the .clear fill mode. Let's define a helper function for this purpose:

    func clearScreen(canvas:Canvas, canvasSize:Size) {
        let rect = Rect(topLeft:Point(x:0, y:0), size:canvasSize)
        let rectangle = Rectangle(rect:rect, fillMode:.clear)
        canvas.paint(rectangle)
    }

Of course, we'll need to invoke the function. The best place to do this is immediately before we paint the ball. Add the following line immediately before the ball is painted:

                clearScreen(canvas:canvas, canvasSize:canvasSize)
Start button green arrow

Run the project.

View the results in the browser. Did the ball move to the new location? If not, what happened? Why?


Tracking the Mouse[edit]

Clicking the mouse can move the ball to that location, but we can also have the ball track the mouse. We'll simply use a different method, named onMouseMove. This method is invoked whenever the mouse is moved on the canvas. Change the name of the onClick method to onMouseMove. No other changes are necessary.

Start button green arrow

Run the project.

View the results in the browser. Does the ball track the mouse? How is this behavior different from the situation when we were using onClick?

Exercises[edit]

  1. Change the background of the animation from white to a sky of blue and grass of green

Key Concepts[edit]

  • Flags are Boolean values that indicate the state of a binary situation
    • clearing a flag means setting it to false
    • setting a flag means setting it to true