touchdevelop chapter 7cdn.touchdevelop.com/c03/mvxh-bookchapter7.pdf · chapter 7 basic graphics,...
TRANSCRIPT
Version 1.1 for TouchDevelop v2.8 Basic Graphics, Events and Global Data
– 73 –
Chapter 7
BASIC Graphics, Events,
and Global Data In this chapter, the graphics features of TouchDevelop are introduced and then combined with scripts when an
action, such as turning the phone around, is performed. That topic leads very naturally to scripts which access
the sensors built into a Windows phone. Providing code to be executed whenever an event is triggered then
leads naturally into scripts which use global data items.
7.1 Graphics Facilities
7.2 Events and Sensors
7.3 Global Data
Graphics Facilities The phone’s screen is composed of many dots which can be illuminated in different colors. All characters, all
lines, all pictures seen on the screen are displayed as patterns of these dots where the phone’s software has
selected the color and intensity for each dot.
Graphics programming is, in effect, a matter of writing code to set the color and intensity of each dot in an area
of the screen. Anyone who has had some previous experience with graphics programming can skip the follow-
ing brief introduction to the subject. After that introduction, we will move on to the facilities for graphics pro-
gramming provided in TouchDevelop.
Computer Graphics Terminology
Each dot on the screen is known as a PICTURE ELEMENT and that term is usually abbreviated to PIXEL or to PEL.
We will use the term pixel. The TouchDevelop API for setting the pixel’s color and intensity uses a triple of three
numbers. In most graphics software, these three numbers are one-byte integers in the range 0 to 255, and they
specify the intensity of the red, green and blue components of the color separately. However TouchDevelop
scales these numbers to be in the range 0.0 to 1.0.
Basic Graphics, Events and Global Data Version 1.1 for TouchDevelop v2.8
– 74 –
The triple of three numbers is commonly called an RGB VALUE. A value of 0 means that the color component
has zero intensity and a value of 1.0 means maximum intensity. Some typical combinations of integers as RGB
values in TouchDevelop are:
(0.0, 0.0, 0.0) black (0.0, 1.0, 0.0) green
(1.0, 1.0, 1.0) white (0.0, 0.0, 1.0) blue
(0.5, 0.5, 0.5) gray (1.0, 1.0, 0.0) yellow
(1.0, 0,0, 0.0) red (1.0, 0.0, 1.0) purple
The pixels on the screen are arranged in a rectangular grid. Each pixel has a coordinate position in this grid giv-
en by an x value and a y value. The x values start at 0 and increase from left-to-right
across the screen; the y values start at 0 and increase from top to bottom down the
screen. (Note that y-axis values grow in the opposite direction to how you would normally
draw a graph.)
The size of the displayable area on the screen is exactly 480 pixels wide by 800 pixels high
for a Windows 7 phone. This means that the x and y coordinates can range from (0,0) in
the top-left corner to (479,799) in the bottom right corner, as shown on the right.
A Trivial Graphics Program
A short graphics program was used as an example in Chapter 2.
It begins by creating a new Picture value and assigning it to variable pic. It is an array of pixel values 480 wide
and 300 wide. The width is the maximum which can be displayed on a Windows phone. The height of 300 is
much less than the maximum 800 allowed for the phone. The pixels are initialized to be white. The second
statement draws a solid rectangle in the Picture array. The top left corner of the rectangle is at (10,10) – i.e. 10
pixels in from the left and 10 pixels down from the top. The width of the rectangle is 200 pixels and its height is
100 pixels. The fifth argument of 0 means that we do not want to rotate the rectangle before drawing it onto
the picture object. (To be more precise, it is rotated by 0 degrees.) The last parameter specified the color to use
for the rectangle. The colors→red value is equivalent to using (1.0,0.0,0.0) as the RGB value for the pixels, and
is much more readable.
Figure 7.1: Trivial graphics script
Version 1.1 for TouchDevelop v2.8 Basic Graphics, Events and Global Data
– 75 –
The third statement draws a solid ellipse inside the Picture array. One has to imagine that the ellipse fits inside
a rectangle whose top left corner is at the coordinates (270,190). The width and height of that ellipse are set to
be 200 and 100 respectively. Again, we rotate that containing rectangle by 0 degrees. The color for the ellipse is
chosen to be blue.
The fourth statement draws the four sides of a rectangle at approximately the extreme edges of the Picture
array. The thickness of the lines used for the four sides has been set at 3 pixels. The color of the lines is whatever
has been chosen in the phone’s settings as the accent color; for the screen shot shown below, that accent color
is magenta.
The fifth statement draws a line from the bottom left corner up towards the top right corner, and the thickness
of the line is chosen to be 3 pixels wide.
Finally the last statement sends the picture to be displayed on the wall. A screen shot of our program’s output
appears in Figure 7.2.
More details about Pictures
Any photograph held on the phone or downloadable from the web is held as a Picture value too. This means
that you can write scripts which transform the photograph or overwrite them in some way. Anything is possible
given enough effort spent developing a script.
Just to introduce you to the possibilities, Figure 7.3 shows a script which first prompts the user to enter a string
(to use as the value of the parameter msg), then asks the user to select a photograph from amongst those on
the phone, overprints a text message diagonally across the picture from the bottom left to the top right and
finally displays the picture.
Figure 7.2: Output from trivial graphics script
Basic Graphics, Events and Global Data Version 1.1 for TouchDevelop v2.8
– 76 –
The script has to make some calculations to determine a suitable size for the characters in the message being
displayed and what angle to draw the text at, if we want that text to be along the diagonal (/sdcn). The charac-
ter size calculation is very approximate (as not all characters are the same width) and the formula used here was
found by trial and error.
The angle of the diagonal is found by computing the arctangent of height/width for the image. The math
function atan2 provided in the TouchDevelop API performs that calculation but returns the result in radians.
The angle argument to the draw text method requires the angle in degrees however, so a call to the rad to
deg conversion function is needed too. Finally a minus sign is needed in front because we are drawing the text
in a direction which ascends from left to right which corresponds to an anticlockwise rotation, whereas the draw
text method normally performs a clockwise rotation on the text. A sample output from this script is shown in
Figure 7.4.
Note that the photograph selected for display in Figure 7.4 is too large to fit on the screen. Its width is 800 pixels
yet the screen itself is only 480 pixels wide. The post to wall method scaled the image by a factor of 480/800
in both dimensions so that it would fit without having to scroll the image from side to side.
Figure 7.4: Output from the Overprint Photograph Script
Figure 7.3: Script to Overprint a Photograph
Version 1.1 for TouchDevelop v2.8 Basic Graphics, Events and Global Data
– 77 –
Table 7.1 provides a description of most of the methods available for the Picture datatype.
Table 7.1: Useful Picture Methods
Method Description
Operations on the Image as a Whole
height: Number Returns the picture height in pixels
width: Number Returns the picture width in pixels
invert: Picture Inverts the R,G,B color components of every pixel
clear(col:Color): Nothing Sets all pixels to color col
clone: Picture Returns a duplicate of the picture
post to wall: Nothing Displays the picture on the screen, resizing it if necessary to fit
within the maximum width of 480 pixels and a maximum height
of 800 pixels.
crop(x:Number, y:Number, w:Number,
h:Number): Nothing
Crops the picture to have width w and height h, selecting a sub-
image whose top left corner is at position x,y
blend(other:Picture, x:Number,
y:Number, angle:Number,
alpha:Number): Nothing
Overwrites this picture with picture, other, putting its top left
corner at position x,y and rotating about that corner by angle
degrees in a clockwise direction; alpha is used as the transpar-
ency of all pixels in the other picture.
resize(w:Number, h:Number): Nothing Scales the picture to have width w and height h
Shape Drawing Methods
draw ellipse(x:Number, y:Number,
w:Number, h:Number, angle:Number,
c:Color, th:Number): Nothing
Draws the largest ellipse which fits inside a rectangle with width
w and height h; the top left corner of the rectangle is at position
x,y; the rectangle is rotated about the top left corner by angle
degrees in a clockwise direction; the line has a thickness of th
pixels and is drawn in color c.
draw line(x1:Number, y1:Number,
x2:Number, y2:Number, c:Color,
th:Number): Nothing
Draws a line from position x1,y1 to position x2,y2; the line has
thickness of th pixels and is drawn in color c.
draw rect(x:Number, y:Number,
w:Number, h:Number, angle:Number,
c:Color, th:Number): Nothing
Draws a rectangle whose top left corner is at position x,y; it has
width w and height h; the rectangle is rotated about the top left
corner by angle degrees in a clockwise direction; the line has a
thickness of th pixels and is drawn in color c.
draw text(x:Number, y:Number,
txt:String, fs:Number,
angle:Number, c:Color): Nothing
Draws text as pixels in color c on the picture; the top left of the
first character is at position x,y; the size used for the characters
is fs; the text is rotated by angle degrees about the x,y posi-
tion.
Basic Graphics, Events and Global Data Version 1.1 for TouchDevelop v2.8
– 78 –
fill ellipse(x:Number, y:Number,
w:Number, h:Number, angle:Number,
c:Color): Number
Like draw ellipse except that the ellipse is drawn as a solid ob-
ject (not as a line surrounding an elliptically shaped area)
fill rect(x:Number, y:Number,
w:Number, h:Number, angle:Number,
c:Color): Nothing
Like draw rect except that the rectangle is drawn as a solid ob-
ject (not as four lines surrounding a rectangular area)
Access to Pixels
pixel(x:Number, y:Number): Color Returns the color of the pixel at position x,y
set pixel(x:Number, y:Number,
c:Color): Nothing
Sets the pixel at position x,y to have color c
Working with Pixels
As explained earlier, each pixel has a color and an intensity determined by a triple of three numbers, the RGB
values. There is an additional number attached to each pixel which comes into play if we want to superimpose
one picture on top of another. This additional integer is known as the ALPHA value and controls the degree of
transparency of the pixel.
Suppose that we want to overlay part of picture A with picture B. Should the pixels from Picture B replace the
pixels of A? This is usually known as knockout overprinting. It means that you cannot see through the pixels
from picture B and see any remnants of picture A in the overlaid area. However, perhaps the colors of the B
picture pixels should be blended with the pixels of the A picture so that you can see both the A and B images
combined? The alpha values associated with the B pixels determine their degrees of transparency. If alpha is 1.0,
the pixel is fully opaque and the B pixel will knockout (replace) the A pixel. If alpha is 0.0, the pixel is fully trans-
parent and no trace of picture B will be seen when overlaid on picture A. Intermediate values for alpha result in
partial transparency for the B picture.
Given a picture referenced by variable pic, the pixel at coordinates x,y can be retrieved by a statement like the
following.
var col := pic→pixel(x,y)
The value so obtained has the datatype Color. That value can be decomposed into its A (alpha), R, G and B
components with statements like these:
var red component := col→R
var green component := col→G
var blue component := col→B
var alpha component := col→A
We can change these color components in any way we like, as long as the values stay in the 0.0 to 1.0 range,
and then recombine them to create a new color which can be stored back into the picture as a changed pixel
value. For example, the following code inverts the color intensities, giving an effect similar to creating a color
negative image:
Version 1.1 for TouchDevelop v2.8 Basic Graphics, Events and Global Data
– 79 –
red component := 1.0 - red component
green component := 1.0 - green component
blue component := 1.0 - blue component
col := colors→from argb(alpha component, red component, green component, blue component)
pic→set pixel(x, y, col)
However that particular sequence of statements would rarely be needed because the Picture datatype has an
invert method which performs exactly that operation to every pixel in the image.
Many of the methods used for working with colors and individual pixels are listed below in Table 7.2. They are
accessed from the service named colors. For example,
var y := colors→yellow
declares variable y to have the type Color and initializes it to the yellow color.
Basic Graphics, Events and Global Data Version 1.1 for TouchDevelop v2.8
– 80 –
Table 7.2: Methods provided by the colors Service
Method Description
Composing and Decomposing Color Values
from argb(alpha:Number, r:Number,
g:Number, b:Number): Color
Constructs a color from the alpha and RGB values, which must
all be in the range 0.0 to 1.0
from rgb(r:Number, g:Number,
b:Number): Color
Constructs a color from the RGB values provided; the alpha
component is set to 1.0 (totally opaque)
Access to the Phone’s Theme Colors
accent: Color
background: Color
foreground: Color
chrome: Color
subtle: Color
Returns the accent, background, foreground, chrome and subtle
(light gray) colors selected for the phone’s theme in its Settings
menu
Standard Colors
black: Color
blue: Color
brown: Color
cyan: Color
dark gray: Color
gray: Color
green: Color
light gray: Color
magenta: Color
orange: Color
purple: Color
red: Color
white: Color
yellow: Color
Obtain standard colors by their usual names.
Obtaining Special Colors
random: Color Returns a random (opaque) color
linear gradient(c1:Color, c2:Color,
alpha:Number): Color
Computes a color in between c1 and c2 where c2 overlays c1
with the specified alpha factor for transparency.
Events and Sensors The Windows phone contains a number of sensors, such as an accelerometer, which can trigger events that get
passed on to the TouchDevelop script. In addition, a script which implements an interactive game needs to re-
spond when the user taps certain items on the screen, and those taps can also trigger events.
Version 1.1 for TouchDevelop v2.8 Basic Graphics, Events and Global Data
– 81 –
The various kinds of events recognized by TouchDevelop are listed in Table 7.3. Sensors are covered in some
more detail in Chapter 8 as well as more examples which use events. The events grouped in the ‘Game Playing’
category will be covered only in Chapter 9.
Table 7.3: Events in TouchDevelop Scripts
Event Description
Phone Movement Events
Shake Triggered when the phone is shaken
phone face up Triggered when the phone is turned so that it is face up
phone face down Triggered when the phone is turned so that it is face down
phone portrait Triggered when the phone is turned so the screen in portrait mode (such as
when the phone is vertical)
phone landscape left Triggered when the phone is turned so that its left side is facing down
phone landscape right Triggered when the phone is turned so that its right side is facing down
Media Events
active song changed
The names of these events describe the events camera button pressed
camera button half pressed
camera button released Wall Events
tap wall XX(item: XX)
XX represents any of the TouchDevelop datatypes, such as String or Loca-
tion or Number Map, etc. The event is triggered when a user taps a dis-
play of a value of type XX on the wall (i.e. the screen). The value is passed as
a parameter to the event.
empty space on wall Triggered more space is available on the wall. Typically this happens when
the user scrolled to the bottom of the wall.
Game Playing Events
gameloop An event which is triggered by a timer approximately every 50ms
player state changed
tap board: b(x,y) Triggered when a global data item named b with type Board is tapped; x
and y are the coordinates of the tap.
swipe: board: b(x, y, dx, dy) Similar to tap board, except that it is triggered by a swiping action starting at
position x,y and with a distance vector dx,dy
tap sprite in s(sprite, index,
x, y)
Sprites will be covered in Chapter 9. (They are graphics objects which can
move around on the screen.)
swipe sprite in s(sprite, in-
dex, x, y, delta x, delta y)
drag sprite in s(sprite, in-
dex, x, y, delta x, delta y)
Basic Graphics, Events and Global Data Version 1.1 for TouchDevelop v2.8
– 82 –
Example Event Handling Script: Phone Orientation
A script can optionally include code which is executed whenever a particular kind of event happens. As a simple
example, let us construct a script which displays the words ‘Portrait’ and ‘Landscape’ in large letters on the
screen according to the phone’s current orientation.
To reuse some of the graphics code covered earlier in this chapter, we will create Picture values and write text
with suitable orientations for display into the pictures and display the pictures in the event code.
The code for the main action and all three events is shown in Figure 7.5. As you can see, the main action has
got absolutely nothing to do. Even though control returns from the main action, the script stays active waiting
for events. The only way to stop this script is to tap the phone back button (or to tap the Windows button to exit
from TouchDevelop completely).
Figure 7.5: Code for the Orientation Event Script
Two of the screen displays produced by this script as the phone is rotated are shown below in Figure 7.6.
Version 1.1 for TouchDevelop v2.8 Basic Graphics, Events and Global Data
– 83 –
Figure 7.6: Some Output Displayed by the Orientation Script
Global Data Sometimes an event in a script needs to save information which is used later by an action or by another invoca-
tion of an event (perhaps the same event, perhaps a different event). Where can such information be saved?
The only reasonable answer is to use a GLOBAL DATA VARIABLE. We have already seen some example pro-
grams which use global data items. Let us look at one more.
Example Script: A Pedometer
If you carry your Windows phone with you when jogging or walking briskly, the phone’s sensors should trigger a
SHAKE event each time you take one step. We can therefore write a simple script which records how many
steps you take, incrementing a counter each time the shake event occurs. With a little more coding, we can
compute the number of steps per minute. By using access to the phone’s GPS location, we can also compute
your average speed. However that programming feature does not need to use any event code, so we will omit
that feature in this version of the program.
When we begin creating the Pedometer script, TouchDevelop provides a default action named main and that
is all. No events and no global data items exist for the script. We could tap the plus button to the right of the
data icon to create a global data item for the number of steps taken, but let’s do it another way. We can use the
promote to global data item form of refactoring explained in Chapter 6.
The first thing we do in creating the script is to edit the code for the main action. We add the statement
var steps := 0
Basic Graphics, Events and Global Data Version 1.1 for TouchDevelop v2.8
– 84 –
where we have declared and initialized steps as a local variable. Now, by selecting this declaration line for edit-
ing and selecting the left-hand side variable, we can promote it to a global data item named ◳steps.
Similarly, we should declare
var start time := time→now
and then promote the start time local variable to be the global data item ◳start time. We need to remem-
ber the time at which the script was started so that we can determine the elapsed time, needed for computing
the number of steps per minute.
The code for the Pedometer program is shown in Figure 7.6. The local variable elapsed is the time since the
script was started, measured in seconds. It is probably unnecessary to check that elapsed has a non-zero value,
but it is good programming practice to avoid division by zero. To compute the walking or jogging rate as the
number of steps per minute, we need to multiply the steps per second by 60. Otherwise, the script should be
easy to follow.
Figure 7.6: The Pedometer Script
Output from the script while it is running looks similar to that shown below.
This does not look very professional! Accuracy to 15 decimal places is just a bit excessive. Fortunately it takes
only one extra line of code to reduce the number of digits after to the decimal point to a more reasonable
number. Let’s pick one digit. If we insert either this line of code:
rate := math→round(10*rate)/10
Version 1.1 for TouchDevelop v2.8 Basic Graphics, Events and Global Data
– 85 –
or this line of code
rate := math→round with precision(rate,1)
after the line declaring and initializing the local variable rate, we will get just one digit to the right of the deci-
mal point. The output produced by the program now looks like this:
Now, why did we have to initialize ◳steps to zero in the main action? Why did it not just start with the value 0
when we begin running the script? This is explained in the next subsection.
Persistence of Global Data Values
The very first time you use a script after it has been installed, the global data items will have initial values which
are 0 for Number values, false for Boolean values, an empty string for String values, and date / time combi-
nation from long ago for DateTime values. For all other datatypes, the global data item has the special INVA-
LID value.
It may be necessary for the script to check and initialize global data variables to take account of first time use of
a script. The is invalid method is useful for checking the data items which start off with an invalid value.
When the script is run a second time or a third time or …, the global data items still have the same values as
they had at the end of the previous run. In the terminology of programming languages, we say that the values
of the global data items PERSIST from one run to the next on your phone. Persistence is a highly useful feature
because it allows your scripts to accumulate information over many runs. (In a misuse of the English language,
programmers often say that a variable “is persisted” if it retains its value from one run to the next .)
If you do not want to reuse a value from the previous run of the script, as is the case with the Pedometer exam-
ple, the script should re-initialize the global data items in whichever of its actions will be invoked when the script
is started. (This is likely to be every action which is not flagged as private.)
Basic Graphics, Events and Global Data Version 1.1 for TouchDevelop v2.8
– 86 –