Second in our programming series
In the first article in this series, we looked at
some of the graphics
capabilities of GEM, culminating in a simple demonstration. In this
article, we will examine some more GEM functions and add a couple of
demos to the first version of the program.
HITCHES, GLITCHES AND PROBLEMS.
There were a few teething troubles with the first
article which I will deal with first. I compiled the first program
with Metacomco's Lattice C compiler version 3.03, with which it
compiled and linked without problems. I subsequently upgraded to
version 3.04 of the compiler, and on recompiling the program two
warnings were output. These may have occurred on other systems, so I
include fixes for them here. The first warning came from the
compiler, in that a function return value mismatch warning occurred
three times. This happens because Lattice 3.04 complies very closely
with the proposed ANSI standard for C, in which functions not
returning a value to the calling function should be declared as type
void. Three functions main(), init_gem() and finish_gem() return
no value, and the way to fix this warning is either to declare these
functions as void, or at least with Lattice C to use a compiler
option to disable this warning message in this particular context.
The problem doesn't affect the running of this particular program,
it is just a warning of a syntax error that might cause problems
elsewhere.
The second warning was returned by the linker, to
the effect that the GEM VDI global arrays contrl[ ], intin[ ], ptsin[
], intout[ ] and ptsout[ ] had been defined more than once. Again,
this does not prevent the program from operating, since the linker
uses the first values it finds and ignores any others. The reason is
presumably that the compiler automatically defines these arrays so
the programmer doesn't have to do it. This may be specific to
Lattice, but if you are like me and don't like seeing warnings from
the compiler or linker here's how to fix the problem:
i) delete the definitions of the five GEM VDI
arrays
ii) replace them with the following arrays: WORD work_in[ 11 ],
work_out[ 57 ]
iii) replace all references to intin[] with work_in[ ], and all
references to intout[ ] with work_out[ ]
Other minor problems were that the XBIOS function
Setpallete() was misspelled as Setpalette() (not in the listing,
just in the text of the article) and that the format of the string
for alert boxes was shown incorrectly in the text. The brackets
surrounding the icon, text and buttons should be square not round
brackets, and the character used to separate the lines of text and
the buttons is the vertical line character, not a colon. This
character is obtained by pressing Shift and the key adjacent to the
left Shift key on the ST keyboard. It does print out as two short
vertical lines one above the other on a dot matrix printer, so the
typesetter's confusion is understandable! Again, these errors did
not affect the actual listing.
Table 1. The attribute functions.
Attribute
group |
Functions |
Feature(s)
changed |
Line |
vsl_type |
line style |
|
vsl_width |
line thickness |
|
vsl_color |
line colour |
|
vsl_ends |
line end style |
|
|
|
Fill |
vsf_interior |
fill style |
|
vsf_style |
pattern fill type |
|
vsf_color |
fill color |
|
vsf_perimeter |
fill border visibility |
|
|
|
Polymarker |
vsm_type |
marker type |
|
vsm_height |
marker height |
|
vsm_colour |
marker colour |
|
|
|
Text |
vst_height |
character height |
|
vst_rotation |
text baseline rotation |
|
vst_color |
text colour |
|
vst_effects |
text special effects
|
|
vst_alignment |
text alignment |
|
|
|
Miscellaneous |
vs_color |
change a colour |
|
vswr_mode |
change writing mode |
THE PROGRAM LISTINGS
Listing 1 accompanying this article contains some
minor changes and additions which should be made to the previous
version (0.2) of the program.
Listings 1 & 2 source code
There are some additional #defines and
declarations, a few extra lines for main() to enable the new demos
to be called, and I have tidied up the finish_gem() function
slightly to provide a cleaner exit back to the desktop. Make the
necessary changes, then add Listing 2 to the modified program. Once
successfully compiled, run the program. Demo 1 will appear as
before, but this time holding down the right mouse button will
progress to demo 2. In this demo, holding down either button will
run demo 3 and holding down both simultaneously will exit to the
desktop. In demo 3, there is a change, since the program waits not
for a mouse button click but for a keypress. Pressing any key
returns to demo 1, unless you press the space bar which will
terminate the program.
GRAPHICS FUNCTIONS AND ATTRIBUTES
Before we look at the two new demos to be added to
the program, it seems an opportune moment to examine the question of
graphic attributes. As we have seen, GEM possesses a large number of
graphics functions. Each of these have a number of features or
attributes which may be set as desired, including such things as
colour, fill styles, line type and so on. Not all graphics functions
are affected by all the various attributes and in fact each graphic
output function is usually associated with a particular group of
attributes. There are four such groups line, fill, text and
polymarker plus one or two attributes affecting all graphic
output. These four groups are listed in Table 1. This is not a
complete list, but it contains the vast majority of attribute
functions most users will need. Clearly, changing one attribute does
not affect another, so that (for example) changing the text colour
will not alter the line or fill colours. From now on, when a graphic
function is said to be associated with a particular group of
attributes it should be easy to decide how to change a particular
feature.
DEMONSTRATION 2
The second demonstration to be included in our
developing program is another example of simple animation, carried
out this time by rapidly drawing and redrawing circles. The aim is
to give the illusion of a chain of circles moving across the screen.
The demo starts by first setting the background
colour to black. The screen is cleared, the demo's title printed at
the bottom of the screen and three of the fill attributes are set.
We looked at vsf_interior() last time, but note that a fill style of
zero denotes no fill - that is, just the outline of the object will
be drawn. When GEM draws an object which is associated with the fill
attributes, it encloses the object with a line drawn in the current
fill colour. You would not be able to distinguish this line if the
object is drawn in solid colour, but it might be visible if a
patterned fill style were used. For this reason, the enclosing line
can be disabled with the function vsf_perimeter(). The format of
this function is :
vsf_perimeter( device handle, perimeter flag )
where the perimeter flag can have the value 0
(outline disabled) or 1 (outline enabled). Here we ensure that the
outline is enabled, because if it were not the combination of no
fill and no visible outline would result in invisible circles!
After initialising some variables, the program
draws 10 circles starting at position 160 (x-coordinate), 100
(y-coordinate). The centre of the circle is changed each time
through the loop by adding the incremental values x_incr and y_incr.
There are in fact two ways of drawing circles in
GEM. The first is to use v_circle() as shown here, which is called
by:
v_circle( device handle, circle centre
x-coordinate, circle centre y-coordinate, circle radius )
The parameters passed to the function are
self-explanatory, and are the same as in v_pieslice() which we
looked at last time. The alternative method is to use v_arc() as
follows:
v_arc( device handle, circle centre x-coordinate,
circle centre y-coordinate, circle radius, start angle, stop angle )
These parameters are identical to those in
v_pieslice(). The major difference between the two functions, other
than the obvious one that v_circle() draws complete circles whereas
v_arc() can draw portions of a circle (i.e. arcs), is that v_circle()
uses fill attributes but v_arc() uses line attributes. If you want
circles filled with a colour or pattern you can use v_circle() but
if you want circles drawn with thick lines or different line types
(such as dotted lines) use v_arc(). You might care to modify this
program to use v_arc() rather than v_circle(). To draw a complete
circle using v_arc() set the start angle to 0 and the stop angle to
3600 (angles are given in tenths of degrees). You can change the
line colour with vsLcolor(), which is called in identical fashion to
vsf_color(), and the line width with vsLwidth() which has this
format:
vsLwidth( device handle, line width )
where width is the line thickness in pixels. The
width should be an odd number (default thickness is 1) and if an
even number is given the nearest lower odd number is used. The line
type can be changed with vsLtype() as follows:
vsLtype( device handle, line type )
where line type can have one of these values:
value |
line type result |
1 |
solid line |
2 |
long dashed line |
3 |
dotted line |
4 |
dash-dot line |
5 |
dashed line |
6 |
dash-dot-dot line |
7 |
user defined line style |
Experiment with these values and see what effects
are produced.
To return to the demo, the program next enters a
'while' loop in which another circle is drawn at the head of the
chain while the last one is erased. This is done by setting the
circle colour to zero (background) and overwriting the last circle.
At the end of each pass through the loop, the state of the mouse
buttons is checked and providing a button has not been pressed the
loop continues. The function used to detect a button press is
vq_mouse() (just to make a change from graf_mkstate() which we used
last time). Call this function as follows:
vq_mouse( device handle, mouse button, mouse
x-coordinate, mouse y-coordinate)
where the mouse button can have the same values as
we found with graf_mkstate(). We are not interested in the position
of the mouse here, so the coordinates are just placed in the dummy
variable. Note that pointers to the three variables, not the
variable names themselves, are passed to the function. (This must
also be done for graf_mkstate().) Finally, before returning the
mouse button state to the main() function, the mouse button is
checked again until the user releases it. This ensures that the
button is not still being pressed on exit from the function, which
might have untoward effects.
DEMONSTRATION 3
This simple demo shows how text can be printed to
the screen using GEM. Rather than go through the demo in detail
it's very simple I will discuss the functions used in the program
and leave you to decide how the program works.
GEM has two functions for outputting text to a
graphics screen. The simpler is v_gtext(), which is used as follows:
v_gtext( device handle, text x-coordinate, text y-
coordinate, text string )
How the x and y coordinates are used is affected
by one of the text attribute functions, vst_alignment(), which we
will look at in a moment. The text string can either be a literal
string enclosed in double quotes (the compiler automatically adds a
terminating null byte as required by C convention) or a pointer to
the string. Both methods are used here, the second method being used
to print the Atari logo on the screen. The logo comes in two halves
with the character values OE and OF (hex) respectively, and these
values are placed into the array str[] at the beginning of the
program.
The function vst_alignment() alters the way GEM
uses the supplied coordinates. It is called as follows:
vst_alignment( device handle, horizontal
alignment, vertical alignment, selected horizontal alignment,
selected vertical alignment )
where the last two parameters are supplied as the
addresses of (i.e. pointers to) two variables into which GEM will
place the selected alignments. In this example, the dummy variable
is used for convenience. The horizontal alignment can have one of
three values:
value |
text
alignment |
0 |
left justified x
coordinate is that of the leftmost end of the string |
1 |
centred x coordinate is
that of the centre of the string |
2 |
right justified x
coordinate is that of the rightmost end of the string |
By default the text is set to left justified. The
vertical alignment can have the following values:
value |
text
alignment |
0 |
y coordinate is that of the
text baseline |
1 |
half line |
2 |
ascent line |
3 |
bottom line |
4 |
descent line |
5 |
topline |
I am unsure of the effect of values other than
zero, which is the default value. Readers may care to try them out
and see what results are produced.
The second method of text printing is to use
v_justified(). This is used as follows:
v_justified( device handle, x-coordinate,
y-coordinate, text string, string length, word space flag, character
space flag)
The first four parameters are the same as for
v_gtext(). The string length is the distance on screen, expressed in
pixels, into which the text string is to fit. Text can therefore be
compressed or stretched by giving a length different to that of the
string itself. To do this, GEM will either have to alter spaces
between characters or between words, or both. You can specify which
of these methods is used by setting the two flags in the function.
If the flag is set to zero, space alteration is disabled; if set to
one, it is enabled.
The other attribute functions used here are
straightforward. Text colour is selected with vst_color(), and
character height (in pixels) with vst_height(). There are minimum
and maximum limits on character size, and these are returned in the
four parameters following the requested height. We aren't really
interested in these here, so they are placed in a dummy variable.
Text special effects can be selected using vst_effects() which is
called as follows:
vst_effects( device handle, effects )
where different values of the variable effects
have these results:
value |
effect |
0 |
normal text |
1 |
bold text |
2 |
light text |
4 |
italic text |
8 |
underlined text |
16 |
outlined text |
32 |
shadowed text |
These values can be combined, so that (for
example) a value of 28 would produce underlined italic outlined
text.
Most of these effects are shown in the demo,
although the effect of shadowed text is not very apparent.
The final effect shown is that of changing the
text baseline vector using vst_rotation(). This is called as
follows:
vst_rotation( device handle, baseline rotation )
where the baseline rotation is expressed in tenths
of degrees. Normally, this is set to zero, producing horizontal
text, but it can supposedly produce text printed at
an angle. Unfortunately, I found that in low resolution at least
only angles of 0, 90, 180, or 270 degrees were accepted, and any
other value resulted in the nearest of these angles being selected.
This may simply be a limitation of low resolution and it may be that
other angles can be used in higher resolutions.
Some of the text on the screen is shown enclosed
in a box with rounded corners. The box is drawn with the following
function:
v_rbox( device handle, points array )
where the array contains the x and y coordinates
of the top left corner of the
box in its first two elements, and the coordinates of the diagonally
opposite corner in the second two elements. This function uses line
attributes.
Finally, just for a change the demo waits for a
keypress before exiting rather than a mouse button click. For this
purpose we could have used a standard C library function, but
instead I have used the GEM function evnt_keybd(). This waits for
the user to press a key and returns a 16-bit integer (not a char)
containing the value of the key pressed. The value returned is
therefore not an ASCII value but a GEM VDI keycode; lists of these
can be found in most GEM reference books. The great advantage of
using this function rather than one from the standard library is
that it will detect the use of the function keys as well as standard
ASCII keycodes.
COMING NEXT
I think that about wraps it up this time. In the
next part of this series we will look at some of the remaining GEM
graphic functions, and after that it will be time to examine those
features which really distinguish GEM such as windows, menus,
dialogs and so on.
top