Functions: Mouse

Control objects on the screen using the mouse!

Functions with Mouse Interaction



Objectives of this tutorial:

1. Learn to use functions in more interesting applications
Exercises:
1. Now that you can create simple functions, let’s create a more difficult function that will 
help us with our next program. Go to Try ELM.
A) Let’s start with this template of code that we will modify, and add our own functions. 
Copy-Paste the follow lines:

import Color exposing (..)
import Graphics.Collage exposing (..)
import Graphics.Element exposing (..)
import Mouse
import Window
import Text exposing (..)
main = Signal.map2 layout Mouse.position Window.dimensions 
layout (x, y) (width, height) = 
     let (xCentered, yCentered) = 
     (toFloat x - toFloat width/2, toFloat height/2 - toFloat y)
     in collage width height
                [ eye |> move (xCentered,yCentered)
                , text <| bold <| fromString 
                       <| "Window Dimensions: " ++ (toString (width, height))
                , (text <| bold <| fromString <| "Mouse Position: " ++ (toString (x,y))) 
                  |> move (0,-20) 
                ]
eye = group [ (filled darkBrown (circle 13))  
            , (filled black (circle 10)) 
            , move (-4,4) (filled white (circle 3)) 
            ]
stopX (x,y)  = (if | x > 100    -> 100
                   | otherwise -> x       
               , y)
stopY (x,y)  = (x,
               if | y < -200    -> -200
                  | otherwise -> y
               )
broccoli (x,y)  = (if | x > 100   -> 100
                      | x < -100    -> -100
                      | otherwise -> x
                   , y)
doubleSpeedX (x,y) = (2*x, y)
distance (x1,y1) (x2,y2) = sqrt((x1 - x2)^2 + (y1 - y2)^2)


In depth:
- The main function uses Signal.map2 to combine two 
signals together.  The first signals change according to the mouse position, and the 
second signals change according to the size of the window. Try moving the mouse 
around and watch how the numbers change. We need both signals because the mouse 
(0,0) point is the top-left corner, but when drawing, (0,0) is the middle.  We can only 
convert between the two if we know the size of the window, which can change.  Try 
resizing the drawing window by dragging the pane divider (the bar in the middle) and 
see the window dimensions get updated.  Signal.map2  uses a function, 
in this case the custom function layout that we created, to do the combination.  The 
logic of your program is all contained in this function and functions it calls. 
- Next we need to determine where the middle of the screen is. The 
(xCentered,yCentered) is calculated using the mouse position and the 
window dimensions. The toFloat is used to convert a data type to Float 
(decimal numbers).
- Now that we have used let to declare what the centered x and y values 
are, we use in to say where we need to use these values. In this case, it 
is in a collage which has the dimensions of the window. Inside the collage function, we 
call the shape that moves based on the mouse signal, and we have some text that 
updates based on the two signals in our program. The backwards infix operator  
<|  simply feeds the data on its right side to the function that it points to (left 
side). You can also use  |>  to do so the other way around, just like 
|> move (0,-20) does, except you need to remember that it needs to 
know what exactly is being fed.  This is why we surround the data before the operator 
with brackets. 
B) (Optional) If you want change the eye to your own shape, you can use a shape you’ve 
already made, such as the heart from the heart tutorial. Make sure to change the variable 
name  eye  to describe your new shape.
 
eye = group [ (filled darkBrown (circle 13))  
                 , (filled black (circle 10)) 
                    , move (-4,4) (filled white (circle 3)) 
                    ]


You will also have to change eye in  eye |> move (xCentered, 
yCentered) to your new variable name. 
For example: If you called the shapes smileyFace = group […] , then 
change eye to smileyFace because that is the new group of 
shapes you would like to use. 
C) The first function (stopX) stops the object from moving beyond 100 pixels 
to the right (from the center). Test the following function:
 stopX (x,y)  = (if | x > 100    -> 100
                         | otherwise -> x       
                                    , y)

by applying the function to (xCentered,yCentered) This is what it will look 
like:  |> move (stopX (xCentered,yCentered)) . Move the mouse around and 
see what happens.

Explanation:
- The name of the function is  stopX , and it takes the input 
(x,y).  (The inputs are also called arguments.) In this function, the 
output point (x,y) is sometimes the same as the input, and sometimes it is different.  
- if | … -> …  is a special function which makes a choice, and is 
sometimes called a “case statement”. It computes conditions given on the left, 
following the |, top to bottom, until it finds a “true” case, and then 
computes the value after the  -> .  Translated into English, this function 
says “if x is greater than 100, the value is 100, otherwise the value is 
x.”

D) The second function (stopY) limits the object from moving down less than 
(-200) pixels. Test this function by applying it in the same way you did for the 
stopX function.

E) The third function (broccoli) uses three cases. Test the function and see 
what it does. Once you know what it does, change the function name “broccoli” so that it 
describes what it does. (This is an example of how a function name or variable name can 
be anything you want, but should be named something descriptive, so that everyone can 
understand what the function does.)

F) The fourth function (doubleSpeedX) doubles the distance that the object 
moves based on the mouse position. Test the function by applying it to 
(xCentered,yCentered). 

G) Now try to change the doubleSpeedX function to halve the speed instead. 
Remember to change the name of the function so that it matches this new purpose.

H) (Optional) You can even add multiple cases and limit the object to move only in a square, 
or anything else you can think of! Remember that you can have the function modify BOTH 
x and y at once, since the argument that the function takes is 
(x,y). 

BONUS: Black Hole Challenge: 
We have included a function (distance) that can calculate the distance from 
one point to another (the function takes both points as arguments). 
The Challenge: Write a new function which makes the eye (object) “invisible” when inside 
a circle of radius 100 in the middle of the canvas. 

Hints / Help:
To do this, you will have to use the distance formula given, as well as have the 
 (width, height)  the argument. In English, this is what you need to code: “If the 
distance from (x,y) and (0,0) is greater than 100, the value is x, otherwise the value is 
2000”. Keep in mind, the reason we said “otherwise the value is 2000” is because we want 
it to look like it disappeared. That is why we change its x-value to a huge number like 
2000, so that we can’t see it on our screen. You can change 2000 to be any big number, as 
long as we cannot see the object on the screen. You must do this for the y-value as well. 
To get you started, the frame of the code would look like this: 


blackHole (x,y) (width,height) = (If | distance (x,y) (width, height) …
       | otherwise … 
 ,    
  If | distance (x,y) (width, height) …
     | otherwise …
  )

~ Finally, you will need to apply the blackHole function to (xCentered, yCentered) and the 
middle of the screen, which is (0,0). Applying the function would look like:
 |> move (blackHole (xCentered, yCentered) (0,0)) 

                                
Previous Tutorial Back to Tutorials Next Tutorial