Graphics Programming in Icon
Graphics Programming in Icon
Graphics Programming in Icon
Ralph E. Griswold
Clinton L. Jeffery
Gregg M. Townsend
@
SM
PEER-TO-PEER
COMMUNICATIONS
This book originally was published by Peer-to-Peer Communications. It is out
of print and the rights have reverted to the authors, who hereby place it in the public
domain. Contents
Publisher's Cataloging-in-Publication
(Provided by Quality Books, Inc.)
Preface xi
Griswold, Ralph E., 1934-
Graphics programming in Icon / Ralph E. Griswold, Clinton
L. Jeffery, Gregg M. Townsend -- 1st ed. Acknowledgments xiii
p. em.
Includes bibliographical references and index.
ISBN 1-57398-009-9 1 Introduction 1
1. Icon (Computer program language) 2. Computer graphics.
3. Computer drawing. I. Jeffery, Clinton L. II. Townsend, The Icon Programming Language 1
Gregg M. III Title. Graphics in Icon 1
QA76.73.I19G75 1998 005.133 Organization of the Book 2
QBI98-66640
How to Use This Book 5
© 1998 by Ralph E. Griswold, Clinton L. Jeffery, and Gregg M. Townsend
All rights reserved. No part of this book may be reproduced, in any form or by any means, without advance 2 Icon 7
written permission from the publisher.
Getting Started 7
Published by Peer-to-Peer Communications, Inc.
P.O. Box 640218 Expression Evaluation 9
San Jose, California 95164-0218, U.S.A.
Phone: 408-420-2677 • Fax: 804-975-0790 Types, Values, and Variables 18
E-mail: info@peer-to-peer.com • World Wide Web: http://www.peer-to-peer.com/
Numerical Computation 21
10 9 8 7 6 5 4 3 2
Structures 23
Peer-to-Peer offers discounts on this book when ordered in bulk quantities. For more information, contact the Sales Characters and Strings 29
Department at the address above.
Procedures and Scope 35
DISCLAIMER File Input and Output 39
This book and CD-ROM are provided "as is". The implied warranties of merchantability and fitness for a particular
purpose are expressly disclaimed. The programs contained herein have not been thoroughly tested under all
Preprocessing 42
conditions. Therefore their reliability, serviceability, and function are not guaranteed. Running Icon Programs 44
The information contained herein was valid at the time it was written and conveyed as accurately as possible by the The Icon Program Library 46
authors. However, some information may be incorrect or may have changed prior to publication. The authors and
publisher make no claims that the material contained herein is entirely correct, and assume no liability for use of Special Topics 48
the material contained herein.
Library Resources 55
A number of words that appear in initial capitalization in the text may be trademarks or service marks, or signify
other proprietary rights. No attempt has been made, however, to designate as trademarks or service marks all Tips, Techniques, and Examples 55
personal computer words or terms in which proprietary rights might exist. The inclusion, exclusion, or definition
of a word or term is not intended to affect, or to express any judgement on, the validity or legal status of any
proprietary right that may be claimed in that word or term. 3 Graphics 59
Basic Window Operations 60
Window Attributes 62
Example - Random Rectangles 64
Events 66
ill
iv v
References 493
Index 495
Graphics Programming
Images produced with the aid of computers seem to be everywhere:
video games, animated cartoons, special effects for television and motion
pictures, business presentations, multimedia, ... and as a component of most
computer applications. This is a book for programmers who want to create
images and incorporate visual interfaces in their applications.
Historically, graphics programming has been difficult, requiring expen-
sive equipment and a large amount of effort to produce even crude results.
Recent advances in hardware and software have put graphics within reach of
most programmers, but programming often remains much more difficult than
it should be. Part of the reason for this is that most programming languages were
designed before graphics became generally accessible. As a result, graphics
facilities generally have been ad hoc appendages to programming languages
rather than integral parts of them. Furthermore, most graphics programming
has been done in relatively low-level programming languages, requiring te-
dious, time-consuming programming.
This book advocates graphics programming in a high-level program-
ming language, Icon, that integrates graphics with other features. Using Icon,
programs with graphics are faster and easier to write than in most programming
languages. If you're familiar with graphics programming in a lower-level
language, leaf through the book and look at the images and the code that
produced them.
About Icon
If you don't already know Icon, you might wonder why you should take
the trouble to learn another programming language. We've already mentioned
xi
xii
the time and effort Icon can save in graphics programming. That alone will more
than reward the effort of learning Icon. There also is much more to Icon that
makes it worth knowing.
Icon has a large repertoire of operations for manipulating structures -
records, lists, sets, and tables - and extensive capabilities for processing strings
Acknowledgments
of characters. At the heart of Icon is a goal-directed expression-evaluation
mechanism that simplifies many programming tasks. Storage is allocated auto-
matically - you never have to worry about allocating space - and garbage
collection reclaims unused space as necessary. It's not only easy, it's fun to
program in Icon.
Icon programs also are highly portable. You can, for example, take one The Icon programming language is the result of the work of many persons in
written on a UNIX platform and run it under Microsoft Windows with little or addition to the authors of this book, including Cary Coutant, Dave Hanson, Tim
no modification. Korb, Bill Mitchell, Ken Walker, and Steve Wampler. The graphics portions of
Icon also have benefitted from contributions by Darren Merrill and Cheyenne
Wills.
Graphics Programming Using Icon
The software interface tools that provide support for visual interfaces
To use this book, you should have some programming experience (not were written by Jon Lipp. The initial version of the visual interface builder was
necessarily a knowledge of Icon), some experience with applications that use designed and implemented by Mary Cameron. Jason Peacock also contributed
graphics (but not necessarily any experience in graphics programming), and to the interface tools.
access to a PC running Windows or Windows NT, or a UNIX system running X
The Windows implementation was assisted by a software donation from
Windows.
Microsoft Corporation.
This book includes Version 9.3 of Icon (for both Windows and UNIX) on
Support came from the National Science Foundation, the Department of
CD-ROM; see Appendix Q. Icon implementations and other resources also are
Computer Science at The University of Arizona, and the Division of Computer
available on the Internet; See Appendix P. It's easy to install Icon; you can be up
Science at The University of Texas at San Antonio (UTSA). A UTSA Faculty
and trying simple programs quickly.
Research Award was crucial to the success of this endeavor.
There are many resources to help you. Icon comes with a large support
Lyle Raines designed the Icon "Rubik's Cube" that appears following
library that includes many examples of Icon programming, useful programs,
Chapter l.
and many procedures that you can use in your own programs. There's also a
large community of Icon users, an electronic newsgroup, and many other The authors are particularly grateful to persons who have read and
resources. commented on drafts of this book: Bob Alexander, Richard Goerwitz, Bob
Goldberg, Roger Hare, Bill Mitchell, and Steve Wampler. Special thanks go to
Madge Griswold for her thoughtful reading and editing of several drafts of this
book. We also would like to thank David Bacon for a perceptive and encouraging
review of an early draft of this book.
Special thanks go to students who used Icon and drafts of this book in
experimental courses on graphics programming. The problems they found and
xiii
xiv
the suggestions they made contributed to the graphics facilities and to the
presentation of those facilities in this book.
Some of the material that appears in this book is adapted from articles in
'Cirly.e and is used here by permission of the editors.
Finally, we wish to thank members of the Icon user community for ideas,
suggestions, and most of all, encouragement.
!"#$%&'()*"+%,&-.$ /-%0."123
!"#$%&'()'*+%,-,'.#$,$%*%/#0,%1*'#,23%(4%4,*25',$6%7,%5$,%#2%4('%*%85#.9
.",.9%2"*2%2"#:)$%*',%45:.2#(:#:)%&'(&,';36
+(.$&(4*5("/6-7864* /-%0."923
!",%.;*$$#.%<#,'&#:$9#%2'#*:);,%#$%$"(/:%",',%/#2"%*%&,:2*)(:*;%),:,'=
*;#>*2#(:6
!"#$%&'#()$*+,-,%*.$-$'#(," /-&#$*012
!"#$!"#$%&!'$%&'(&)*$)++',-$-#+#./0'1$'2$.'+'&-$3-01($#0/"#&$456$'&
!"#$%&'(")#*'+,-"**./0 (-"*.'123 789$.'+'&$-%).#:
!"#$% &#$'()*% +)$% ',-&./0&% 1*% )% 2#230045(#40% ',-6,)78% 4-3% /-.43#46
/-77043$9
3&"4,5*3$'#&"6-$7 /-&#$*018
4'("5.'6/%&'*7.'8%-%/'9%%: (-"*.';2<
4#.3&-0;#$<0;0-0'1$)1<$&)1<'*$.'+'&)/0'1$%&'<3.#<$/"0-$<#.'&)/0;#
!"0% !"#$%""&% ',-6,)7% 0:'(-,0$% ;/-4<$% 4)70&% /-(-,$9% =0,0% ),0% 3"0 0*)(#:
3+043*%$")&0$%-2%3"0%%#'()*+!,-.%".09
!"#$%&'&($)*+,#$)-(.+,#
!"#$% &#$'()*% +,% )-)#().(/
0+(+1$%0+2/$%,1+2%,()33/45
#46%)4&%$31/30"#46%3"/%$715
,)0/%+,%3"/%&+7.(/%0+4/%+,
89:% 0+(+1% $')0/;% <)13#)((*
$)371)3/&% 0+(+1$% ,1+2% 3"/
#43/1#+1%)1/%4+3%$"+=4;
/'+0#$123
!"# !"#)-(.+,#
!"/%!"%')(/33/%0+43)#4$%>7$3
?@%&#$01/3/%0+(+1$A%+,%="#0"
BC%(#/%+4%3"/%$71,)0/%+,%0+(+1
$')0/;%D+3#0/%3"/%/E31)%0+(5
+1$%#4%3"/%.1+=4%)4&%+1)46/
1/6#+4$;
/'+0#$124
!"#$!$#)-(.+,#
!"/%!#$')(/33/A%=#3"%)%3+3)(
+,%CFG%0+(+1$A%'1+-#&/$%,#4/1
61)47()1#3*;%H3$%GIC%$71,)0/
0+(+1$%)1/%&#$31#.73/&%$*25
2/31#0)((*;
!"#"$%&'#())(* &#')(%+,-
!"#$%&#'((&)#*&+$,#$%&#!"#-.*.)#(/*&$$&#/((&/)01#2&*.3,#$%&#!$#$%).'4%#!%
)&4'*/)#-.*.)#(/*&$$&0#/)�%.3"1 /'+0#$125
!"#$%&'(")%*+(",-$./0 1'*.("2342
!"#$%&'($)*+,$)-%)-#.'/%0'1$./(0.$%#&,2)$%($#12%,%3,4)..)5%$"'61%,.
.")%4)7.8%!")%."/))%&'($)%+(..'1$%",9)%+))1%,$$#21)-%-#77)/)1.%$",-)$
'7%+4()8 !"#$%&'()'*$+,%(*#"- .#"/$+0010
!"#$% &'()'*+% #$% ,"-% $./0-1,% (2% *% 3-,*#4-3% 1*$-% $,.35% #6% 7"*&,-'$% 88
,"'(.)"%89:
5/67(6.0*.$/6"8*%( 1'*.("2349
2%3+.")4%35 .#"/$+0610
!")%:0'1%3/'2/,&%4#+/,/;%#104(-)$%.")%3/'0)-(/)$%7'/%-/,6#12%34,;#12
;-<-'*4%*4)('#,"+$%2('%/#6%&*1=#6)%*'-%3-+(6$,'*,-3%/5%,"#$%&'()'*+:
0,/-$%($)-%+;%."#$%<'10)1./,.#'1%2,&)5%,$%6)44%,$%.")%2,&)%#.$)478
>-'-?%*%2#'$,@2#,%&*1=#6)%#$%$"(A6:
Chapter 1
Introduction
The Icon Programming Language
Icon is a general-purpose programming language that emphasizes ease
of programming. Although similar in appearance to languages like Pascal and
C, Icon has much more to offer. Here are some of its distinctive aspects:
• Strings are atomic values, not arrays of characters. An extensive and
sophisticated set of operations supports analysis, synthesis, and
manipulation of strings.
• List operations generalize the concept of an array to implement stacks,
queues, deques, and similar structures.
• True polymorphism allows values of any type in data structures,
argument lists, table indices, and other roles.
• Sets and tables provide quick lookup using index values of any type.
• Programmer-defined record types allow representation of complex
data relationships.
• Underneath Icon's conventional syntax lies a powerful goal-directed
evaluation mechanism that searches for a result among multiple
computational paths.
• Automatic storage management, with garbage collection, eliminates
tedious and error-prone manual allocation.
These features combine to make programming substantially easier in Icon than
in other languages.
!"#"$%&'()*+',+-%.& /#-0.'1234
Graphics in Icon
!"#$%#&'()*+'#,(%-)./)*0%-).12+($%'#3#&/$%2$#&/%)(+'4
*&/3($%$(3(+'(1%5).0%*&%#0*/(6%7,(&%'"(%0.$'%02&1*&(
Icon's graphics facilities also emphasize programming ease. Many graph-
#0*/(%+*&%8#(31%#&'()($'#&/%-*''()&$6%!"(%-*''()&$%*'%'"(
)#/"'%9()(%'*:(&%5).0%'"(%.'"()%+.3.)%-3*'($6%;*&%8.2
ics systems require that a program be able to redraw the contents of a window
#1(&'#58%'"(%$.2)+($< upon demand - for example, when the user moves another obscuring window
out of the way. In Icon, this is handled automatically. Once something is drawn
to the window, it stays drawn, and any necessary refreshing is handled by the
Icon system without involving the application program.
1
2 Introduction Chapter 1 Chapter 1 Introduction 3
Many systems impose an event-driven paradigm in which a graphics Almost all of the Icon language is covered in Chapter 2. To keep things
program acts only in response to user or system requests. While such an simpler, we have omitted a few features that are used only in special situations
approach is often best, there are many situations where a procedural view is and are not needed for graphics. See The Icon Programming Language by Griswold
sufficient - and much simpler. Icon allows both approaches. and Griswold (1996) for a complete description of Icon.
A friendly programming interface does not preclude a wide range of Chapter 3 discusses the basic concepts of Icon graphics: the coordinate
features. From the perspective of a programmer, Icon offers the following kinds system, window attributes, and the input model. The structure of a graphics
of graphics capabilities: program is outlined, and the customary "Hello World" program is presented.
• Windows can be opened and closed as desired. The next five chapters cover fundamental aspects of graphical output to
• Points, lines, polygons, circles, arcs, curves, and text can be drawn. a window. Chapter 4 presents traditional drawing operations: lines, points, arcs,
and the like. Chapter 5 introduces Icon's "turtle graphics" procedures, inspired
• Color can be specified by numeric value or descriptive phrase.
by those of Logo (Abelson and diSessa, 1980). Chapter 6 discusses facilities for
• Windows can be treated as files for reading and writing text. reading and writing strings of text. Chapter 7 covers the use of color, and
• Fixed and proportionally spaced type faces can be used. Chapter 8 deals with patterns and images.
• Characters from the keyboard can be processed as they are typed. Chapter 9 discusses the use of multiple windows, the use and sharing of
• Images can be read from files and written to files. graphics contexts, and interaction with the underlying graphics window sys-
tem.
• Buttons, sliders, and other interface tools are available.
Input events are described in Chapter 10. The chapter covers polling and
blocking, synchronization with output, and complications raised by multiple
Organization of the Book windows. The use of higher-level dialog boxes for input is also discussed.
At this point, all of Icon's basic graphics operations have been presented,
The Chapters
and a complex interactive application with a nontrivial graphical interface can
The body of the book explains how to construct graphics programs using be examined. The next three chapters illustrate the use of Icon's interface builder
Icon. Similar features are grouped together, and the discussion generally moves by constructing a "kaleidoscope" program.
from the simple to the more complex. The chapters are arranged to minimize the Chapter 11 begins with an overview of the program from the user's
inevitable forward references to concepts not yet covered. perspective. It discusses the interface components (buttons, sliders, and so on)
Two common sections appear at the ends of many chapters. Library available for building interfaces, and it explains how callbacks communicate
Resource sections point to components in the Icon program library, including interface actions to the program.
procedures related to the chapter topic and programs for experimenting with Chapter 12 presents VIB, Icon's interactive interface builder. The kalei-
related features. Tips, Techniques, and Examples sections show how the doscope interface is constructed, step by step, followed by a discussion of a few
features introduced in a chapter can be used, often in ways not obvious, to good issues that did not arise in that particular program.
effect.
Chapter 13 completes the program construction, showing how the
This introductory chapter outlines the book's framework and sets the interface builder code is combined with additional Icon code to produce the
stage for the text to follow. finished product.
Chapter 2 introduces the Icon language. It presupposes only an under- Chapter 14 discusses additional dialogs - simple ones that can be
standing of programming concepts and does not assume any prior exposure to produced by procedure calls and custom dialog boxes that are constructed using
Icon. Experienced Icon programmers may skip this chapter, although those who the interface builder.
are unfamiliar with the Icon program library should read that subsection.
The final two chapters are case studies of two more actual applications,
complete with source code, discussing various issues that arise along the way.
Chapter 1 Introduction 5
4 Introduction Chapter 1
Appendix L summarizes the fields, states, callbacks, and behavior of
Chapter 15 presents a pattern editor, and Chapter 16 presents a caricature interface vidgets. Appendix M is a reference manual for the interface builder.
generator.
The features that differ among implementations of Icon are listed in
The Appendices Appendix N. Appendix a explains how to build and run Icon programs, another
system-dependent topic.
A significant portion of the book is filled by the many appendices that
Appendix P lists additional resources related to Icon - books, newslet-
serve as a reference manual for the Icon language and its graphics facilities. In
ters, and the Icon home on the Internet.
contrast with the sequential nature of the main text, the appendices are designed
for quick access. Appendix Q describes the accompanying CD-ROM, which includes Icon
implementations, documentation, examples, the Icon program library, and
The first six appendices cover Icon in general; most of the rest deal
much more.
specifically with aspects of graphics. Some standard nongraphical parts of Icon,
such as additional I/O procedures and keywords, are included for reference in
the appendices although they are not discussed in the body of the book. How to Use This Book
Appendix A presents the syntax of the Icon language in outline form. Nothing substitutes for actual programming experience. To get the most
Control structures, operators, keywords, escape sequences, and reserved iden- from this book, you should run some Icon programs as you go along.
tifiers are listed.
Implementations of Icon for Microsoft Windows and for UNIX, along
The Icon preprocessor, which is used mainly for manifest constants and with installation instructions, are included on the CD-ROM that accompanies
conditional code, is described in Appendix B. this book. If Icon is not already installed on your machine, it will be worth the
Appendices C and D describe control structures and operators in detail. time to take care of that now.
Appendix E covers predefined Icon procedures, including both built-in As you learn about Icon and read the examples, you may sometimes
and library procedures. Calling sequences, default values, return values, and wonder, "But what if ....". That is a good time to run an experiment on your own.
cross references accompany the procedure descriptions. Source code for all of the examples is included on the CD-ROM, and you can edit
it to try variations.
Icon keywords are described in Appendix F. (In Icon, keywords are
special global variables, not reserved identifiers.) After you become somewhat comfortable with Icon, you may wish to see
more and larger examples. The Icon program library, again on the CD-ROM,
Appendix G summarizes all the graphics attributes in one place. Initial contains a large amount of code.
and acceptable values are indicated where appropriate, along with brief descrip-
tions and cross references. The two classes of attributes are distinguished, and Don't forget the appendices - they are there for reference and they can
readable and writable attributes are so indicated. help answer questions that may arise as you read the text. In addition to the
language reference appendices, Appendices a (Running Icon) and Q (About
The standard color palettes that are used for drawing pixel-based images the CD-ROM) may be particularly useful at the start.
and optionally when reading images are described in Appendix H. Plate 8.1
shows the standard palettes in color.
Appendix I describes the details of exactly which pixels are set by the
drawing operations - and indicates the details that aren't guaranteed to be
consistent on all platforms.
AppendixJ lists the symbols used for encoding outboard keys such as the
"page down" key in Icon events. Appendix K documents the structure of an Icon
event queue for the rare program that accesses the queue directly.
Chapter 2
Icon
This chapter addresses the basic concepts of Icon and describes many of its
features. The chapter lays a foundation for chapters that follow, which describe
Icon's graphics facilities. Some important aspects of Icon that do not fit well into
a general description of its facilities are given in the Special Topics section at the
end of this chapter. Be sure to read these.
You probably won't want to try to absorb everything in this chapter in
one reading. Try reading through the chapter once without worrying about all
the details, in order to get a "feel" for Icon. Later you may wish to read some
sections more carefully or refer to them when reading the rest of this book or
writing your own programs.
The appendices at the end of this book contain detailed reference
material, including parts of Icon's computational repertoire that are not de-
scribed elsewhere in this book. If you need to do something that we don't
describe here, look in the appendices on operations and procedures or pick up
a copy of The Icon Programming Language (Griswold and Griswold, 1996).
Getting Started
Icon is an imperative, procedural programming language, similar in
many respects to Pascal or C. A traditional first program that writes a greeting
looks like this in Icon:
procedure mainO
write("Hi there!") # write a greeting
end
The words procedure and end are reserved words that indicate the beginning
and ending of a procedure. Every program must have a procedure named main,
7
8 Icon Chapter 2 Chapter 2 Icon 9
which is where execution begins. The word write is the name of a built-in In Pascal, the following line is a statement:
procedure. When it is called, as it is here, it writes out its argument. Parentheses
if switch = on then write('on') else write('off');
indicate a call and enclose arguments. In this case there is one argument, which
is a string of characters indicated by the delimiting quotation marks. The In this statement, switch = on is an expression whose value determines what is
character # begins a comment that continues to the end of the line. written.
Most Icon programs contain many procedures. The procedures declared Icon is different in this regard. Icon has no statements, only expressions.
in a program are on a par with built-in procedures and are called the same way, Icon expressions look like statements in Pascal and do similar things. Every Icon
as in expression, however, returns a value, and the value often is useful. In Icon, that
statement could be cast as either of these expressions:
procedure mainO
if switch = on then write("on") else write("off")
greet(IGeorge")
write(if switch = on then "on" else "off")
end
Although the second form is not better than the first from a stylistic standpoint,
procedure greet(name) it shows that even if-then-else is an expression.
write("Hi there!") For the most part, when writing Icon programs, you'll just use expres-
write("My name is ", name, ".") sions in natural ways without worrying about the difference between state-
end ments and expressions.
which writes
Expression Evaluation
Hi there!
My name is George. At first glance, expression evaluation in Icon may appear to be the same
as in other imperative programming languages. Although expression evalua-
The word name is a parameter of the procedure greetO; its value is set when
tion in Icon has some aspects of expression evaluation in other imperative
greetO is called. In the example above, "George" becomes the value of name in
languages, there's much more to Icon, as we'll show soon.
greetO. Note that the second use of writeO has three arguments separated by
commas. When writeO is given several arguments, it writes them one after Sequential Evaluation
another on the same line.
You may wonder why there are no semicolons following lines of code. In Icon, as in most other imperative languages, expressions are evaluated
Icon allows them but doesn't require them - it supplies them automatically so in the order in which they are given, as in
that you don't have to worry about them. The procedure greetO could be written name1 := readO
as name2 := readO
procedure greet(name); write("The first two names are ", name1, and ", name2, ".")
II
write("Hi there!"); which reads two lines of input and writes out an informative line containing the
write("My name is ", name, "."); results.
end;
The sequential order of evaluation can be changed by a control structure,
but the semicolons are unnecessary. as in
We've been careful in our choice of words about semicolons. In Pascal,
semicolons separate statements that do things, while expressions perform compu- scount := scount + 1
tations. write("The names are the same.")
}
10 Icon Chapter 2 Chapter 2 Icon 11
else { a logical one, can be used in a control structure. In addition, the notion of
dcount := dcount + 1 attempting a computation that may succeed or fail also is a natural analogy to the
write("The names are different.") way we carry out our daily activities in getting around in the world.
}
If you're used to using logical expressions in programming, the success-
The expression name1 == name2 performs string comparison on the values of and-failure approach may appear strange at first. As you get accustomed to it,
name1 and name2. Which count is incremented and what is written depends on you'll find it both natural and powerful.
whether or not the values are the same. The braces enclose compound expres-
As you learn Icon, pay attention to the situations in which expressions
sions. In this example, there are two expressions to evaluate in each"arm" of the
may fail. We've given two examples so far: comparison and reading input. There
control structure.
are many others, which we'll mention as we go along.
Success and Failure The general criterion for expression failure is a computation that is
meaningful but can't be carried out in a particular instance. Some computations,
If you are familiar with Pascal, you might think that the comparison however, are simply erroneous. An example is
expression name1 == name2 produces true or false, which is then used by if-
i :=i+"a"
then-else to determine what to do.
which is an error and terminates program execution because a is not a number.
II II
In Icon, such a comparison expression does not produce a logical value;
instead, it either succeeds or fails. The effect is the same in the example above, but What Icon considers an error as opposed to failure is a matter oflanguage
the difference between logical values and success or failure is fundamental and design that tries to strike a balance between convenience and error detection.
important. Once you get used to Icon, you won't have to worry about this. Instead, you'll
find that failure is a convenient way of making decisions and controlling loops.
The idea behind success and failure is that sometimes a perfectly reason-
ably computational expression may not be able to produce a result. As an Success and failure of expressions in combination can be tested using
analogy, imagine turning a doorknob to open a door. If it opens, your attempt conjunction and alternation. Both have a familiar appearance. Conjunction is
succeeds; it the door is locked, your attempt fails. expressed as
An example in programming is attempting to read a line from a file. In expr1 & expr2
Icon, such an attempt succeeds if there is a line remaining in the file but fails if
which succeeds only if both expr1 and expr2 succeed. For example,
there are no more lines. For example,
if (max> 0) & (min < 0) then write("The bounds are bracketed.")
while line := readO do
write(line) writes a line only if max is greater than zero and min is less than zero.
reads and writes lines until the end of the input file is reached. At that point, Alternation is expressed as
readO fails. When readO fails, there is no value to assign to line, no assignment expr1 I expr2
is performed, the assignment fails, and the while-do loop is terminated. Note
that the failure of readO is "inherited" by assignment - an assignment can't be which succeeds if either expr1 or expr2 succeeds. For example,
performed if there's nothing to assign. if (pet < 0) I (pet> 100) then write("lnvalid percentage.")
Since failure is inherited, this loop can be written more compactly as writes a line only if pet is less than a or greater than 100.
while write(read())
Control Structures
The do clause and the auxiliary identifier are not needed.
One of the advantages of using success and failure instead of logical Icon has several control structures that can be used to determine which
values to control the order of program execution is that any expression, not just expressions are evaluated and in what order. Most control structures, including
12 Icon Chapter 2 Chapter 2 Icon 13
if-then-else and while-do in the preceding section, use success or failure to Icon has one control structure in which the expression to evaluate is
determine what to do. based on a value rather than success or failure:
There are two looping control structures in addition to while-do: case expr of {
case clause
until expr1 do expr2
case clause
repeat expr
The control structure until-do is the opposite of while-do; it loops until expr1 }
succeeds. The control structure repeat evaluates expr repeatedly; it does not
The value of expr is used to select a case clause and an expression to evaluate.
matter whether exprsucceeds or fails.
Case clauses are evaluated in the order they are given. A case clause has the form
You can terminate any loop by using break, which exits the loop and
allows evaluation to continue at the point immediately after the loop. For expr1 : expr2
example, where the value of expr1 is compared to the value of expr at the beginning of the
while line := readO do case expression. If the value of expr1 in a case clause is the same as the value of
if line == "stop" then break expr, expr2 is evaluated and control goes to the point immediately after the end
else write(line) of the case expression. Otherwise, the next case clause is tried. For example, in
writes lines until one that is "stop" is encountered, at which point the loop is case title of {
terminated. "president": write("Hail to the chief!")
"umpire": write("Throw the bum out!")
It's also possible to go directly to the next iteration of a loop without default: write("Who is this guy?")
evaluating the remaining portion of the do clause. This is done with next. For }
example,
if the value of title is "president" or "umpire", a corresponding line is written. If
while line := readO do the value of title is neither of these strings, the default case is selected. The default
if line == "skip" then next
case is optional; if it is absent and no case clause is selected, nothing happens.
else write(line)
Once a case clause is selected, no other case clauses are tried; unlike C's
which doesn't write lines that are "skip". There's a better way to do this without
switch statement, control never passes from a selected case to the next one.
using next:
Alternation can be used to let one of several values select the same case clause,
while line := readO do as in:
if line -== ·skip· then write(line) case title of {
The operator -== is the opposite of ==; s1 -== s2 succeeds if s1 differs from s2 "president" I "prime minister": write("Haii to the chief!")
but fails otherwise. Although next is not needed in the loop shown above, in "umpire" I "referee" I "linesman": write("Throw the bum out!")
more complicated situations next often provides the best method of getting default: write("Who is this guy?")
directly to the next iteration of a loop. }
The control structure
Generators
not expr
Now for something fun: Imagine you are in a room with three closed
succeeds if expr fails but fails if expr succeeds. In other words, not reverses doors and no other way out. Suppose you try a door and find it locked. You'd
success and failure. This control structure could have been called cant to probably try another door. In other words, you are presented with three
emphasize the use of success and failure in expression evaluation in Icon.
alternatives. If you try one and fail to get out ofthe room, you'd try another door,
and, if necessary the third one.
14 Icon Chapter 2 Chapter 2 Icon 15
Analogous situations are common in programming problems, but most findO. But what about the other two positions? Suppose you want to know what
programming languages don't provide much help. Icon does. they are?
Consider the problem of locating the positions at which one string occurs Generators wouldn't be much good if there weren't ways to get more
as a substring in another. Suppose you're looking for the string "the" in a line of than a first value. There are two ways, however: iteration and goal-directed
text. Consider three possible lines: evaluation.
"He saw the burglar jump down." Iteration
"He saw a burglar jump down."
The control structure
"He saw the burglar jump over the bench and climb the wall."
every expr1 do expr2
In the first line, there is one instance of "the", as shown by the underline. In the
second, there is none, but in the third, there are three. requests every value that expr1 can produce, evaluating expr2 for each one. For
example,
If you are looking for "the" in the first line, you would be successful. In
the second line, you would fail- a situation we've already covered. But what every i := find("the", line3) do
about the third line, where there are three instances of "the"? Certainly your write(i)
attempt to find "the" should be successful. Finding the first (left-most) one would writes 8,30, and 50. The loop is terminated when find() has no more values to
be natural. But what about the two remaining alternatives? Icon provides help produce.
with this kind of situation with generators, which are expressions that can
produce more than one value. Generation, like failure, is "inherited". The loop above can be written
more compactly as
Icon has a procedure for finding the location of substrings: find(s1, 52),
which produces (generates) the positions at which 51 occurs in 52. Suppose we every write(find("the", line3))
name the lines above Iine1, line2, and line3. Then find("the", line1) produces 8 You might try to write the equivalent computation in Pascal or C - that will
(we'll explain how Icon numbers the positions in strings later). On the other show you the power of generators.
hand, find("the", line2) fails, since there is no occurrence of "the" in line2.
find("the", line3) produces 8, then 30, and finally 50. Although every requests all the values of a generator, you can put a limit
on the number of values a generator can produce. The limitation control struc-
A generator does not produce several values all at once. Instead, a ture,
generator produces a value only when one is needed. For example, in
expr\ i
i := find("the", line1)
limits exprto at most i results. For example,
find("the", line1) produces 8 because a value is needed for the assignment. As a
result, 8 is assigned to i. On the other hand, in every write(find("the", line3)) \ 2
i := find("the", line2) writes only 8 and 30.
since find("the", line2) fails, the assignment is not done, and i is not changed. A word of warning: It's easy to confuse while-do with every-do because
Incidentally, it's a good idea to provide a test when there is a possibility of failure; they appear to be so similar. The difference is that
otherwise you have no way of knowing if a computation was done. while expr1 do expr2
Now let's consider the third line. In repeatedly evaluates expr1, requesting only its first value each time through the
i := find("the", line3) loop, while
the first position, 8, is assigned to i; findO works from left to right as you'd expect. every expr1 do expr2
Since assignment needs only one value to assign, only one value is produced by requests all the values expr1 has. For example, if you write
16 Icon Chapter 2 Chapter 2 Icon 17
while write(find("the", line3)) from findO. In the case here, the next value is 30, the comparison succeeds, and
the value 8 is written over and over, in an endless loop. You'll probably not make a notification is written. All this happens automatically; it's part of expression
this mistake often, but it may be helpful to know what to look for if you get such evaluation in Icon.
a symptom. You may have a lot of questions at this point, such as "What happens if
The other mistake is to use every-do when you want to repeatedly there is more than one generator in a complicated expression?" and "Can't goal-
evaluate an expression, as in directed evaluation result in a lot of unnecessary computation?"
every write(read()) We won't go into multiple generators here, except to say that all possible
combinations of generators in an expression are tried if necessary. This sounds
which writes (at most) one line, since readO is not a generator and can produce like an expensive process, but it's not a problem in practice. See Griswold and
only one value. (If you're wondering why readO is not a generator, there's no Griswold (1996) for a detailed discussion of multiple generators.
need for it to be, since every time it is evaluated, it reads a line.)
Reversible Assignment
Goal-Directed Evaluation
When goal-directed evaluation results inbacktracking, expression evalu-
As mentioned earlier, there is a second way in which a generator can ation returns to previously evaluated expressions to see if they have alternatives.
produce more than one value. It's called goal-directed evaluation, and unlike
iteration, it's done automatically. Backtracking does not reverse the effects of assignment. For example, in
Suppose you choose a door in the imaginary room, but find that it opens (i := 5) & (find("the", line) > 5)
to a closet with no exit. What you'd normally do is back out and try another door. if findO fails, backtracking to the assignment does not change the value assigned
You can imagine other, more complicated, situations in which you open a door to i. It remains 5.
into another room, it also has several doors, and so on, but you eventually wind
up in a closet again. Icon provides reversible assignment, represented by <-. In
The usual way to solve such problems is to be goal-directed; if something (i <- 5) & (find("the", line) > 5)
doesn't work, try something else until you succeed or exhaust all alternatives. If if findO fails, backtracking to the reversible assignment causes the value of ito be
you are successful in solving a sub-goal (such as finding an unlocked door in the restored to whatever it was previously.
room you're currently in), but that doesn't lead to your ultimate goal (such as
getting out of the place altogether), you go back and try another alternative Other Generators
(called backtracking). Of course, you have to keep track ofwhat you've tried and
not wind up repeating the same futile attempts. This can quickly become a As you might imagine, Icon has several generators as well as a way for
problem, as in a maze. you to write your own. We'll mention generators that are specific to particular
In Icon, if a value produced by a generator does not lead to success in the kinds of computation as we get to other parts of Icon. There are two generally
expression that needed the value, the generator is automatically requested to useful generators that we'll describe here.
produce another value (that is, to provide an alternative). One is
For example, suppose you want to know if "the" occurs in line3 at a ito j
position greater than 10. You can write
which generates the integers from i to j in sequence. For example,
if find("the", Iine3) > 10 then write("Found it!")
every i := 1 to 100 do
As shown above, find() first produces 8. Since 8 is not greater than 10, the lookup(i)
comparison fails. Things do not stop there, however. Goal-directed evaluation
evaluates lookup(1), lookup(2), ..., lookup(1 00). This can be written more com-
seeks success. The failure of the comparison results in a request for another value
pactlyas
18 Icon Chapter 2 Chapter 2 Icon 19
In this example, the arguments of alternation are just integers and produce only x := "Hello world"
one value each. As suggested above, the expressions in alternation can them-
selves be generators. Going back to an earlier example, Although Icon lets you do this, it's generally better style to use variables
ina type-eonsistentway. There are situations, which we will describe later, when
every write(find("the", Iine1) I find("the", line2) I find("the", line3» the flexibility that Icon offers in this regard is very useful.
writes 8 (from line1), nothing from line2, and then 8,30, and 50 from Iine3. This
expression can be written more compactly by putting the alternation in the Keywords
second argument of findO:
Icon keywords, identified by names beginning with an ampersand, play
every write(find("the", Iine1 I Iine2 Iline3)) a variety of special roles. Some, such as &pi and &e, provide constant values-
in this case the mathematical constants 1t and e. Others, such as &date and
&version, supply environmental information. A few keywords can be assigned
Types, Values, and Variables values; an example is &random, the seed for random numbers. Keywords are
listed in Appendix F.
Data types
Assignment
Icon supports several kinds of data. Integers and real (floating-point)
numbers are familiar. In Icon, strings - sequences of characters - also are a As shown in earlier examples, := is Icon's assignment operator. Aug-
type of data. Strings are a fundamental data type that can be arbitrarily long. mented assignment combines assignment with another operation. For example,
Strings in Icon are not arrays of characters as they are in most programming i :=i+3
languages. Icon also has a data type for sets of characters in which the concept
of membership is important. In Icon, several kinds of structures also are data can be written as
values. We'll say more about the different types of data as we go along. i+:=3
Most binary operations can be combined with assignment in this manner.
20 Icon Chapter 2 Chapter 2 Icon 21
The exchange operator, :=:, interchanges the values of two variables. The procedure integerO converts its argument to an integer if possible. If the
After execution of conversion can't be performed, integerO fails, as you should expect from our
earlier discussion of the situations in which failure can occur.
x:=:y
x contains the previous value of y and y contains the previous value of x. There are similar procedures for other data types. See Appendix E.
Since variables are not typed, there are no type declarations in Icon. This The null valueisaspecial value that serves several purposes. It has a type
has advantages; it saves writing when you're putting a program together. On the of its own and cannot be converted to any other type. The keyword &null has the
other hand, without type declarations, errors in type usage may go unnoticed. null value.
Although Icon does not have type declarations, it's a strongly typed The null value can be assigned to a variable, but it is illegal in most
language. During program execution, every value is checked to be sure that it is computations. Variables are initialized to the null value, so the use of a variable
appropriate for the context in which it is used. For example, as mentioned earlier, before another value has been assigned to it generally results in an error.
an expression like The operations Ix and \x can be used to test for the null value. Ix succeeds
i:= i + "a" and produces x if x has the null value. \x succeeds and produces x if x has a
nonnull value. Since these operations produce variables, assignment can be
results in an error when executed because "a" cannot be converted to a number. made to them. For example,
Icon does more than just check types during program execution. When Ix:= 0
necessary, Icon automatically converts a value that is not of the expected type to
the type that is expected. Real, integer, string, and character set values are assigns 0 to x if x has the null value, and
converted in this manner. For example, in \x:= 0
i:= i + "1" assigns 0 to x if x has a nonnull value.
the string "1" is automatically converted to the integer 1, since addition requires
numbers. Numerical Computation
While you're not likely to write such expressions explicitly, there are
Graphics programming, even for simple drawings, involves a lot of
many situations in which automatic type conversion is convenient and saves
numerical computation. Icon has the usual facilities for this.
you the trouble of having to write an explicit conversion. We've used that earlier
in this chapter without comment. Suppose you want to count something and Integer and Real Arithmetic
then write out the results. You can do it like this:
count := 0 Integers in Icon are what you'd expect, except possibly for the fact that
# count items there is no limit on the magnitude of integers. You probably won't have much
write(count) occasion to use integers that are a thousand digits long, but it may be helpful to
know that you don't have to worry about integer overflow.
The procedure writeO expects a string, so the integer value of count is automati-
cally converted to a string. Real numbers are represented by floating-point values, and hence their
magnitudes and precision depend somewhat on the platfonn you're using.
It's also possible to convert one type to another explicitly, as in
Integers can be represented literally in the ways we've shown earlier.
i := integer(x)
Real numbers can be represented literally in either decimal or exponential form,
as in 0.5 and 5E-1.
22 Icon Chapter 2 Chapter 2 Icon 23
The standard mathematical operations are provided for both integer and sqrt(r) square root of r
real arithmetic: exp(r) e raised to the power r
-n negative of n log(r1, r2) logarithm of r1 to the base r2
n1 + n2 sum of n1 and n2
sin(r) sine of r in radians
n1 - n2 difference of n1 and n2
cos(r) cosine of r in radians
n1 * n2 product of n1 and n2
n1 / n2 quotient of n1 and n2 tan(r) tangent of r in radians
n1 % n2 remainder of n1 divided by n2 asin(r) arc sine of r in the range -rt/2 to rt/2
n1 1\ n2 n1 raised to the power n2 acos( r) arc cosine of r in the range 0 to rt
In "mixed-mode" arithmetic, in which one operand is an integer and the atan(r1, r2) arc tangent of r1 / r2 in the range -rt to rt
other is a real number, the integer is converted to a real number automatically dtor(r) radian equivalent of r degrees
and the result is a real number. rtod(r) degree equivalent of r radians
It's worth noting that the sign of n1 % n2 is the sign of n1. See Appendix E for details.
Arithmetic operations group in the usual way, so that a * b + C / d is
Random Numbers
interpreted as (a * b) + (c/ d). Grouping is discussed in more detail under Special
Topics at the end of this chapter.
Random numbers often are useful for providing a Ii ttle variety or a touch
Division by zero is an error, as are expressions such as of the unexpected in otherwise mundane operations.
-11\0.5 The operation ?i produces a random number. If i is positive, the result is
an integer in the range 1 j i. If i is 0, the result is a real number in the range 0.0
which would produce an imaginary result.
r < 1.0. Random numbers in this range provide a convenient basis for scaling
The standard numerical comparison operations are available also: to any range.
=
n1 n2 n1 equal to n2 Icon also has ways of randomly selecting from a collection of values.
n1 > n2 n1 greater than n2 We'll mention these in the sections that follow.
n1 >= n2 n1 greater than or equal to n2
n1 < n2 n1 less than n2 Structures
n1 <= n2 n1 less than or equal to n2
n1 -= n2 n1 not equal to n2 In Icon, a structure is a collection of values. Different kinds of structures
provide different organizations and different ways of accessing values. Icon has
A successful comparison operation returns the value of its right operand. four kinds of structures: records, lists, sets, and tables.
Consequently, the expression
i<j<k Records
succeeds and produces the value of k if and only if j is strictly between i and k. Icon's records are similar in some respects to Pascal records and C
structs. A record has a fixed number of fields whose values are accessed by name.
Mathematical Procedures A record type must be declared, as in
Many drawings, even simple ones, require mathematical computations. record point(x, y)
Icon provides several procedures for performing trigonometric and other com- which declares point to be a record type with two fields, x and y. This declaration
mon mathematical computations: also creates a record constructor, which is a procedure that creates instances of the
24 Icon Chapter 2 Chapter 2 Icon 25
record. For example, The operator *L produces the size of a list (the number of elements in it).
For example, *colors produces 4.
P := point(O, 100)
The value of an element can be obtained by subscripting it by position,
creates a "point" whose x field is a and whose y field is 100 and then assigns the as in
result to P. A record declaration also adds a type to Icon's built-in repertoire, so
that you can tell what the type of a record is. For example, write(colors[3])
write(type(P)) which writes yellow, the third element of colors. Note that Icon numbers list
elements starting at 1. The value of an element of a list can be set by assigning to
writes point. the subscripting expression, as in
A field of a record is accessed by following the record with a dot and the coordinates[137] := 500
field name, as in
which sets the 137th element of coordinates to 500. A subscripting expression
P.x:= 300 fails if the subscript is out of range. For example, colors[5] fails.
which changes the x field of P to 300. The element-generation operator, !L, generates all the elements in Lfrom
A record can contain any number of fields, and a program can contain first to last. For example,
any number of record declarations. Different record types can have the same every write(!colors)
field names, as in
writes cyan, magenta, yellow, and black. You can even use the element-genera-
record square(label, x, y, w, h) tion operator to set the elements in a list, as in
Icon determines the correct field from the type at execution time. For every !coordinates := 100
example, obj.x references the first field if obj is a point but the second field if obj
is a square. which sets all of the elements in coordinates to 100.
Another operation that sometimes is convenient is ?L, which selects an
Lists element of the list L at random. For example,
In Icon, a list is a sequence of values - a one-dimensional array or a write(?colors)
vector. Icon's list data type is very flexible and is particularly useful in graphics writes one of the elements of colors.
programming.
An unusual butvery useful feature oflists in Icon is that you can use them
You can create a list by specifying the values (elements) that the list as stacks and queues, adding and deleting elements from their ends. When these
contains, as in procedures are used, the size of a list increases and decreases automatically.
colors := ["cyan", "magenta", "yellow", "black"] There are five procedures that access lists in these ways:
which creates a list with the four elements shown. put(L, x1, x2, ... xn) puts x1, x2, ... xn on the right end of L. The
You also can create a list of a specified size and provide a single value for elements are appended in the order given,
every element, as in so xn becomes the last element of L.
coordinates := list(1000, 0) push(L, x1, x2, ... xn) pushes x1, x2, ... xn onto the left end of L.
The elements are prepended in the order
which creates a list of 1000 elements, all of which are zero. List size is limited only given, so that xn becomes the first element
by the amount of available memory. of L.
Both [] and list(O) create an empty list with no elements. We'll show why
get(L) removes the left-most element of Land pro-
you might want an empty list later.
duces its value. getO fails if L is empty.
26 Icon Chapter 2 Chapter 2 Icon 27
pop(L) popO is a synonym for getO. You can determine if a value is a member of a set as follows:
pull(L) removes the right-most element of L and member(shapes, "square")
produces its value. pullO fails if L is empty.
succeeds if "square" is in shapes but fails otherwise. You also can delete a
The relationships among these procedures are shown in the following member from a set, as in
diagram: delete(shapes, "triangle")
Attempting to delete a member that is not in a set succeeds but does not change
push() pull()
the set.
popO The following set operations are available:
get() L . . - _ - ' - - _ - - L _ . . . . . - I_ _.L-_....J-._---L._----I
putO
81 ++ 82 produces a new set with the members that are in either
We mentioned empty lists earlier. If you want to implement a stack, you 81 or 82 (union)
can start with an empty list and use pushO and popO on it. You can tell the stack 81 ** 82 produces a new set with members that are in both 81
is empty when popO fails. To implement a queue, you also can start with an and 82 (intersection)
empty listbut use putO and getO. You do not need to worry about overflow, since
there is no limit to the size of a list. 81 -- 82 produces anew set with themembersof81 thatarenot
in 82 (difference)
These procedures also are useful even when you're notthinking of stacks
and queues. For example, suppose you want to create a list of the lines from a file. Many of the operations on lists also apply to sets: *8 is the number of
All that's needed is members in 8, !8 generates the members of 8 (in no predictable order), and ?8
produces a randomly selected member of 8.
lines := []
while put(lines, read()) Tables
You don't need to know in advance how many lines are in the file.
Tables are much like sets, except that an element of a table consists of a
Sets key and an associated value. A key is like a member of a set - all the keys in a
table are distinct. The values of different keys can be the same, however.
A set in Icon is a collection of distinct values. In a set, unlike in a list, there A table is created as follows:
is no concept of order and no possibility of duplicate values; only membership
counts. attributes := tableO
A set is created as follows: which assigns an empty table (one with no keys) to attributes. Elements can be
added to a table by subscripting it with a key, as in
shapes := setO
attributes["width"] := 500
assigns to shapes an empty set (one with no members). Members can be added
to a set, as in which associates the value 500 with the key "width" in the table attributes. A new
element is created if the key is not already present in the table. Note that this is
insert(shapes, "triangle") much like assigning a value to an element of a list, except that the subscript here
which adds the string "triangle" to shapes. The size of a set increases automati- is a string, not a position. A table automatically grows as values are assigned to
cally as new members are added to it. Attempted insertion of a duplicate value new keys. There is no limit to the size of a table except the amount of available
succeeds without changing the set. There is no limit to the size of a set except the memory.
amount of available memory. As you'd expect, you can get the value corresponding to a key by
subscripting. For example,
28 Icon Chapter 2 Chapter 2 Icon 29
an integer is a value. Strings can be constructed as needed during program building up a string by concatenation. For example,
execution. Space for strings is provided automatically, and strings can be text := 111I
Thus, "Medusa"[-5:-2] is another way of specifying the substring "edu". In The parsing is likely to require many operations on the sentence to
subscripting, a position can be given in either positive or nonpositive form and identify its components.
the positions do not have to be in order - it's the characters between two 2. Many analysis operations occur at a particular place in a string, and
positions that count. the place typically changes as analysis continues. Again, think of
You can assign to a substring of a string to change those characters. parsing a sentence. Parsing typically starts at the beginning of the
Suppose, for example, the value of name is "George". Then sentence and progresses toward the end as components are identified.
Of course, if an initial analysis proves to be incorrect later on, the
name[1 :3] := "J"
analysis may go back to an earlier position and look for an alternative
changes name to "Jorge". Assignment to the substring creates a new string, of (backtracking).
different length, which then is assigned to name. The expression above really is
To simplify string analysis, string scanning maintains a subject on which
just shorthand for
analysis operations can be performed without explicitly mentioning the string
name := "J" II name[3:0] being analyzed. String scanning also automatically maintains a position that
serves as a focus of attention in the subject as the analysis proceeds.
Unlike programming languages in which strings are arrays of characters, Icon
doesn't really change the characters of a string; it always creates a new string in A string scanning expression has the form
such situations.
s? expr
Strings can be compared in a manner similar to the comparison of
where s is the subject string and expr is a scanning expression that analyzes
numbers, but the operators are different and comparison is by character code
(scans) it. When a string-scanning expression is evaluated, the subject is set to s
from the left - by lexical order. The string comparison operations are:
and the position is set to 1, the beginning of the subject. The scanning expression
s 1 == s2 s 1 lexically equal to s2 expr often consists of several subexpressions.
s1 » s2 s1 lexically greater than s2 There are two procedures that change the position in the subject:
s1 »= s2 s1 lexically greater than or equal to s2
tab(i) set position to i
s1 « s2 s1 lexically less than s2
s1 «= s2 s1 lexically less than or equal to s2 move(i) increment the position by i
s1 -== s2 s1 lexically not equal to s2 Both of these procedures produce the substring of the subject between the
The operation s1 == s2 succeeds if and only if s1 and s2 have the same position prior to their evaluation and the position after their evaluation. Both of
size and are the same, character by character. In determining if one string is these procedures fail and leave the position unchanged if the specified position
greater than another, the codes for the characters in the two strings are compared is out of the range of the subject. This failure can be used for loop control, as in
from left to right. For example, "apple" is lexically greater than Apple" because
II
text? {
the character code for "a" is greater than the character code for A". If two strings
II
while write(move(1)) do # write a character
have the same initial characters, but one is longer than the other, the longer string move(1) # skip a character
is lexically greater than the shorter one: "apples" is lexically greater than "apple".
String Scanning which writes the odd-numbered characters of text, one per line.
It is good practice to enclose the scanning expression in braces, as shown
Icon has a high-level facility for analyzing strings, called string scanning. above, even if they are not necessary. This allows a scanning expression to be
String scanning is based on two observations about the nature of most string extended easily and prevents unanticipated problems as a result of grouping
analysis: with other expressions.
1. It is typical for many analysis operations to be performed on the same
You can't do much with justthe procedures shown above. String analysis
string. Imagine parsing an English-language sentence, for example.
34 Icon Chapter 2 Chapter 2 Icon 35
procedures, which produce positions that are set by tabO, are necessary for most
Procedures and Scope
string scanning. The most useful analysis procedures are:
find(s) return the position at which s occurs in the subject Procedure Declarations
upto(c) return the position at which a character of c occurs in Procedures are the computational building blocks from which programs
the subject are composed. Procedures allow you to organize computation and divide your
many(c) return the position after a sequence of characters of c program into logical components.
These procedures all examine the subject starting at the current position and As illustrated by the examples given earlier in this chapter, procedure
look to the right. For example, find("the") produces the position of the first declarations are bracketed by procedure and end. Within the declaration, there
occurrence of lithe either at the current position or to its right. As you'd expect,
II can be declarations for variables that are local to the procedure, expressions to
analysis procedures fail if what's being looked for doesn't exist. be evaluated on the first call of the procedure, and expressions comprising the
body of the procedure that are executed whenever the procedure is called:
Analysis procedures produce positions; they do not change the position
- tabO is used for this. For example, the "words" in a string can be written out procedure name(parameters)
as follows: local declarations
initial clause
text? {
procedure body
while tab(upto(&letters)) do
end
write(tab(many(&Ietters)))
} The parameters provide variables to which values are assigned when the
procedure is called, For example in
In this string scanning expression, upto(&letters) produces the position of the
first letter in the subject and provides the argument for tabO, which moves the procedure max(i, j)
position to that letter. Next, tab(many(&letters)) moves the position to the end of if i > j then return i else return j
the sequence of letters and produces that substring of the subject, which is
written. (Our definition of a "word" is overly simple, but it illustrates the general end
method of string scanning.) the parameters of maxO are i and j. When the procedure is called, values are
Another useful scanning operation is assigned to these parameters, as in
=s write(max(count, limit))
which sets the position in the subject to the end of s, provided s occurs at the which assigns the value of count to i and the value of limit to j, as if the expressions
current position. It fails otherwise. For example, to analyze only lines of input i := count
that begin with a colon, the following approach can be used: j:= limit
while line := readO do { had been evaluated.
line? {
if =":" then The return expressions in this procedure return either the value of i or the
# analyze rest of the line value of j, depending on their relative magnitudes. The value returned becomes
} the value of the procedure call. In the example above, this value is written.
} When a procedure call omits the value for a parameter, the null value is
used. The procedure can check for a null value and assign an appropriate
There is more to string scanning than we have described here. !fyou need default.
to do a lot of complex string analysis, see Griswold and Griswold (1996) for more
information.
36 Icon Chapter 2 Chapter 2 Icon 37
Parameters are local to a procedure call. That is, when maxO is called, the declares status and cycle to be global and hence accessible to all procedures in
variables i and j are created for use in the call only. Their values do not affect any the program.
variables i and j that might exist outside the procedure call. Global declarations must be outside procedure declarations. It is good
Additional local variables are declared using the reserved words local practice to put them before the first procedure declaration in a program so that
and static. Variables declared as local are initialized to the null value every time they are easy to locate when reading the program.
the procedure is called. Variables declared as static are initialized to the null In the absence of a global declaration for a variable, the variable is local
value on the first call, but they retain values assigned to them from call to call. to the procedure in which it appears. A local declaration is not required for the
Expressions in an initial clause are evaluated only once, when the variable. Although local declarations are not required in such cases, it is good
procedure is called for the firsttime. An initial clause often is used to assign static practice to use them. It makes their scope clear and prevents an undeclared
variables a first-time value. variable from accidentally being global because of an overlooked global decla-
ration.
The following example illustrates the use oflocal and static variables and
an initial clause: Calling Procedures
procedure alpha_count(s)
local count Procedures are values, much like lists and sets are values. The names of
static alphnum procedures, both built-in and declared, are global variables. Unlike declared
global variables, these variables do not have null values initially; instead they
initial alphnum := &Ietters ++ &digits have procedure values. When you call a procedure, as in
count:= 0 max(count, limit)
s?{ it's the value of max that determines which procedure is called. Since max is a
while tab(upto(alphnum)) do {
declared procedure, the value of max is that procedure, which is called.
count := count + 1
move(1) When a procedure is called, the arguments in the call are passed byvalue.
} That is, the values of count and limit in the call above that are assigned to the
} variables i and j in max. The procedure max does not have access to the variables
count and limit and cannot change their values.
return count
In the examples shown so far, the values passed to a procedure are given
end explicitly in argument lists in the calls. Sometimes it's useful to pass values
In this procedure, the value for alphnum is computed the first time alpha_countO contained in an Icon list to a procedure. This is especially useful for procedures
is called, but it is available to subsequent calls of the procedure. like writeO that can take an arbitrary number of arguments. Suppose, for
example, that you do not know when you're writing a program how many
Scope arguments there should be in a call of writeO. This might occur if lines to be
written consist of fixed-width fields, but you don't know in advance how many
The term scope refers to the portion of a program within which a variable fields there will be.
is accessible. As explained earlier, parameters and declared local variables are
accessible only within a call of the procedure in which they are declared. In such cases, a procedure can be called with an (Icon) list of values
instead of explicit arguments. This form of call is
Variables also can be declared to be global, in which case they are
accessible to the entire program. For example p!L
global status, cycle where p is a procedure and L is a list containing the arguments for p. For the
situation above, this might have the form
38 Icon Chapter 2 Chapter 2 Icon 39
Since procedures are values, they can be assigned to variables. For return
example, if end
format := [left, right, center] If return has no argument, the null value is returned.
then A procedure also can generate a sequence of values in the manner of a
format[i](data, j) built-in operation. This is done using the expression suspend, which returns a
value but leaves the procedure call intact so that it can be resumed to produce
calls leftO, rightO, or centerO depending on whether i is 1,2, or 3. another value. An example is
A declared procedure also can fail (produce no value) just as a built-in end
operation can fail. This is done by using the expression fail instead of return. For This procedure generates successive n-character substrings of s. For example,
example, in
every write(segment("stereopticon"), 3)
procedure between(i, j, k)
writes
if i < j < k then return j
else fail ste
reo
end pti
the value of j is returned if it is strictly between i and k, but the procedure call fails con
otherwise. When the scanning expression terminates because move(n) fails, control flows
A procedure call also fails if control flows off the end, as in off the end of the procedure and no more results are generated; that is, it fails
when resumed for another value. A fail expression could be added at the end of
procedure greet(name) this procedure, but it is conventional when writing generating procedures to
write("Hi there!") omit the fail.
write("My name is ", name, ".")
end File Input and Output
Two lines are written and then the procedure call fails. It's good practice in such
cases to include an explicit return to prevent failure from causing unexpected Files
results at the place the procedure is called. The previous procedure might better
be written On most platforms, a file is just a string of characters stored on a disk or
entered from the keyboard. A text file consists of lines that end with line
terminators. When reading a line, the characters up to a line terminator are
40 Icon Chapter 2 Chapter 2 Icon 41
returned as a string and the line terminator is discarded. When a line is written, poem := open("thanotopsis.txt")
a line terminator is appended. Line terminators vary from platform to platform, opens the file thanotopsis.txt for reading and assigns the corresponding file
but since they are discarded and added automatically, you usually don't have value to poem. This file value then can be used as the argument for readO, as in
to worry about them.
while line := read(poem) do
It's also possible to read and write characters in "binary" mode without process(line)
regard to line terminators. Most graphics applications deal with text files, but if
you need to deal with binary data, see the description of openO in Appendix E. Note that the word file is used in two different ways: as the name for a file
that the operating system understands and as an Icon value.
We've illustrated reading and writing lines of text in preceding examples
without mentioning files. Three files always are available. They are the values of The procedure openO fails if the file cannot be opened in the specified
keywords: mode. This may happen for a variety of reasons. For example, if thanotopsis.txt
does not exist or if it's protected against reading, the use of openO above fails.
&input standard input If this happens, no value is assigned to poem. If no other value has been assigned
&output standard output to poem, its value is null. A null value and an omitted argument are the same in
&errout standard error output Icon, so read(poem) is equivalent to readO. This is not an error; instead lines are
When readO is called without an argument, it reads lines from standard read from standard input, which may have mysterious consequences. It there-
input. You also can use &input as the argument to readO, as in read(&input). fore is very important when opening a file to provide an alternative in case
Standard input usually comes from the keyboard but also can come from a disk openO fails, as in
file. The method of specifying a file for standard input depends on the platform. poem := open("thanotoposis.txt") I stop("*** cannot open input file")
When writeO is called without specifying a file, lines are written to The procedure stopO writes its argument to standard error output and then
standard output. You also can specify &output as the first argument of writeO, as terminates program execution. It is the standard way to handle errors that
in prevent further program execution.
write(&output, "Hello, world!")
Writing Lines
Standard output usually goes to the screen of your monitor, but there are ways
of having it stored for later use. As illustrated by previous examples, if writeO has several string argu-
Standard error output by convention is where error messages, diagnos- ments, they are written in succession on one line. A line terminator is appended
tics, and so forth are written. To write to standard error output, use &errout as after the last string to produce a complete line.
the first argument of writeO, as in Sometimes it's useful to write the components of a line in the midst of
write(&errout, "Your data is inconsistent.") performing other computations. For example, if you want to see the pattern of
word lengths in a line, you might decide to replace every word by its length:
Like standard output, standard error output usually goes to the screen, but most
sizes:=
platforms provide a way to separate standard output from standard error 1111
output. line? {
You also can open other files for reading and writing. The procedure while tab(upto(&letters)) do
open(name, mode) opens the named file in the mode specified. The most sizes 11:= *tab(many(&letters)) II II II
You can avoid the concatenation by using the procedure writesO, which the program, 8 is substituted. For example, the line
is like writeO, except that it does not append a line terminator. The code fragment
x:= Margin
above could be recast using writesO as follows:
is interpreted as if it had been written
line? {
while tab(upto(&letters» do x:= 8
writes(*tab(many(&letters», II " )
A definition can be removed, as in
}
$undef Margin
writeO
which removes the definition of Margin. A name can be redefined, but it must
The sizes and separating blanks are written successively, but without line be undefined first, as in
terminators. The final writeO with no argument provides the line terminator to
complete the line. $undef Margin
$define Margin 12
Closing Files In all cases, a definition affects only the portion of the file following the
place it appears.
The procedure c1ose(name) closes the named file. Closing a file that is
open for output assures that any data that may be in internal buffers is written There are a number of predefined names that depend on the platform on
to complete the file. It also prevents additional data being written to that file until which you are running. For example, _MS_WINDOWS is defined if you're
it is opened again. Closing a file that is open for reading prevents further data running on a Microsoft Windows platform.
from being read from that file. The directive $ifdef name enables subsequent lines of code up to $endif
When program execution terminates, whether normally by returning only if name is defined. There may be a $else directive between the $ifdef and
from the main procedure, because of stopO, or as the result of a run-time error, $endif directives to enable code if name is not defined. For example,
all files are closed automatically. It therefore is unnecessary to close files $ifdef _MS_WINDOWS
explicitly before terminating program execution. pathsym := "\\"
Most platforms, however, limit the number of files that can be open $else
simultaneously. If you exceed this limit, openO fails. If you're using many files pathsym := "/"
in a program, it therefore is important to close a file when you're through with $endif
it. enables
pathsym := "\\"
Preprocessing
if _MS_WINDOWS is defined but
Icon provides a preprocessor that performs simple editing tasks as a
pathsym := "/"
source program is read. Values or code fragments can be substituted wherever
a chosen name appears. Lines of code can be enabled or disabled conditionally, otherwise.
and additional source files can be imported. The preprocessor is so named The $include directive copies a specified file into the program at the
because all this editing takes place before the source code is compiled. place where the $include appears. For example,
Preprocessor directives are identified by a $ as the first character of a line, $include "consticn"
followed by a directive name. For example,
inserts the contents of the file consticn to replace the $include directive. File
$define Margin 8
names that do not have the syntax of an Icon identifier must be enclosed in
defines the value of Margin to be 8. Whenever Margin appears subsequently in quotation marks, as shown above.
44 Icon Chapter 2 Chapter 2 Icon 45
Running Icon Programs As illustrated earlier in this chapter, procedures can be declared to
augment Icon's built-in repertoire. Such procedures can be placed in libraries so
that they are available to different programs. Libraries play an important part in
Compilation and Execution graphics programming, and many of the graphics procedures described in
subsequent chapters are contained in libraries rather than being built into Icon.
Running an Icon program involves two steps: compiling the program to
produce an executable file and then executing that file. A library is included in a program by using a link declaration. For
example,
The way that these two steps are performed depends on the platform on
which Icon is run. On some platforms, Icon runs from a visual interface using link graphics
menus and so forth. On other platforms, Icon is run from the command line. links the procedures needed for most graphics applications.
User's manuals that describe how to run Icon are available for the different
platforms. We'll use a command-line environment here to illustrate what's Link declarations can be placed anywhere in a program except inside
involved and the options that are available. procedure declarations. It is good practice to place them at the beginning of a
program where they are easy to find.
On the command line, compilation is performed by the program icont,
which processes an Icon source file and produces an executable icode file, as in You can make your own libraries. To do this, you need to compile the
files containing the procedures by telling icont to save its result in library format,
icont app.icn called ucode. This is done with the command-line option -c, as in
which compiles the program app.icn (files containing Icon programs must have icont -c drawlib
the suffix .icn). Specifying the .icn suffix is optional; the following works just as
well as the example above: which produces a pair of ucode files named drawlib.u1 and drawlib.u2. (The .u1
file contains code for the procedures, while the .u2 file contains global informa-
icont app tion). This pair of files then can be linked by
The name of the icode file produced by compiling an Icon program is link drawlib
based on the name of the Icon file. On UNIX platforms, the name is obtained by
removing the suffix and is just app for the example above. For Microsoft in the program that needs procedures in drawlib.
Windows platforms, the .icn suffix is replaced by .bat, producing app.bat for the Only the procedures that are needed by a program are linked into it; you
example above. can make libraries that contain many procedures without worrying about the
A program can be compiled and executed in one step by following the space they might take in programs that don't need all of them.
program name by -x, as in
Environment Variables
icont app.icn -x
There are several command-line options that can be used to control icont. Icon's compilation environment can be customized using environment
For example,
variables. These variables, which are setbefore icont is run, tell Icon where to look
for things like libraries specified in link declarations.
icont -0 rotor app
The way that environment variables are set depends on the platform on
causes the icode file to be named rotor (or rotor.cmd on Windows platforms). which you are running. In a UNIX command-line environment, the wayan
Such options must appear before the file name, unlike -x. environment variable typically is set is illustrated by
See Appendix a for more information about compiling and executing setenv IPATH "/usr/local/lib/ilib /usr/icon/ilib"
Icon programs.
which sets the environment variable IPATH.
46 Icon Chapter 2 Chapter 2 Icon 47
n
IPATH is used to locate library files given in link declarations. In this
example, Icon looks in the directories
/usr/local/lib/ilib
and
data docs packs procs progs gdata gdocs gpacks gprocs gprogs
/usr/icon/ilib
in that order. Icon always looks in the current directory first, so if your library
ucode files are there, IPATH need not contain that directory.
basic graphics
The environment variable LPATH is similar to IPATH, but LPATH tells
Icon where to look for files mentioned in $include preprocessor directives. (You
may notice that the names IPATH and LPATH seem backward - IPATH for
library files and LPATH for include files. The source of this potential confusion Icon Program Library Hierarchy Figure 2.1
has historical origins and it's now too late to correct it.). The library has two main parts: basic material and graphics material.
Other environment variables are read when an Icon program begins The initial character 9 indicates graphics material.
execution to configure memory and other aspects of execution. Consult the
user's manual for your platform. The source code for procedure modules is in the directories procs and
gprocs. As one might expect, the source code for graphics is in gprocs. The
See Appendix 0 for more information about environment variables. directories progs and gprogs contain complete programs. The directories packs
and gpacks contain large packages.
The Icon Program Library
Core Modules
The Icon program library is a free collection of programs, procedures,
documentation, data, and support tools that is available to all Icon program- The directories procs and gprocs contain hundreds of files, and in these
mers. See Appendix P for instructions about obtaining the library. there are thousands of procedures. Some procedures are useful only for special-
ized applications. Others provide commonly used facilities and are designated
Organization as "core" procedures. The core modules for the basic part of the library are:
convert type conversion and formatting procedures
The main directories in the Icon program library hierarchy are shown in
datetime date and time procedures
Figure 2.1.
factors procedures related to factoring and prime numbers
io procedures related to input and output
lists list manipulation procedures
math procedures for mathematical computation
numbers procedures for numerical computation and formatting
random procedures related to random numbers
records record manipulation procedures
scan scanning procedures
sets set manipulation procedures
sort sorting procedures
strings string manipulation procedures
tables table manipulation procedures
48 Icon Chapter 2 Chapter 2 Icon 49
Special Topics by relying on precedences for grouping. Instead, it's wise to use parentheses for
the less-familiar operations, as in
This section contains information about aspects of Icon that may help
heading II (count + 1)
you in writing and understanding Icon programs.
The use of parentheses also makes it easier to read a program, even if you know
Syntactic Considerations what the precedences are.
As in all programming languages, there are rules that you can follow to Two common cases are worth remembering. Assignment has low prece-
avoid syntactic problems. The worst problems are not those that produce syntax dence, so it's safe to write
errors but those that produce unexpected results. The following sections deal i:= j + k
with the most common sources of such problems in Icon programs.
knowing it groups as
Precedence and Associativity
i := U+ k)
Icon has many operators - more than most programming languages.
In addition, conjunction has the lowest precedence of all operators, so it's
The way that operators group in complex expressions in the absence of specific
safe to write
groupings provided by parentheses and braces depends on the precedences and
associativities of the operators in such expressions. i>j&m>n
Precedence determines which of two operators adjacent to an operand knowing it groups as
gets the operand. With one exception, prefix operators that come before their
(i > j) & (m > n)
operands have precedence over infix operators that stand between their oper-
ands. For example, A word of warning: The string scanning operator has higher precedence
than conjunction. Therefore
-text + i
text? tab{find{header)) & move{1 0)
groups as
groups as
(-text) + i
(text? tab{find{header))) & move(10)
The exception is record field references, in which the infix field operator
has highest of all precedence. Consequently, which probably is not what's intended.
-box.line As a general rule, it's wise to enclose scanning expressions in braces to
avoid such problems, as in
groups as
text? {
-(box.line)
tab{find{header)) & move{10)
Different infix operators have different precedences. The precedences of }
infix arithmetic operators are conventional, with exponentiation (1\) having the
This approach also makes it easy to add to scanning expressions and makes the
highest precedence; multiplication (*), division (I), and remaindering (%) the
scope of scanning clear.
next highest; and addition (+) and subtraction (-) the lowest. Consequently,
Associativity determines which of two infix operators gets an operand
i *j +k between them. Most infix operators are left associative. For example,
groups as i- j- k
(i * j) + k groups as
Icon has many infix operators, and it's easy to get an unintended result
50 Icon Chapter 2 Chapter 2 Icon 51
(i - j) - k
Preprocessing
(as is necessary for subtraction to work correctly).
Icon's preprocessor allows a name to be assigned to an arbitrarily
The exceptions to left associativity are exponentiation and assignment. complicated expression. A simple example is
Thus,
$define SIZE width + offset
jl\jl\k
When SIZE is used subsequently in the program, width + offset is
groups as substituted for it.
i 1\ (j 1\ k) Suppose SIZE is used as follows:
as is conventional in mathematical notation. dimension := SIZE *3
Assignment also is right associative, so that This groups as
I :=J:= k dimension := width + (offset * 3)
groups as where the obvious intention was
i := (j := k) dimension := (width + offset) *3
This allows a value to be assigned to several variables in a single compound The value assigned to dimension almost certainly will be incorrect and
assignment expression. result in a bug that may be hard to find - after all
Line Breaks dimension := SIZE *3
As mentioned earlier, the Icon compiler automatically inserts semico- looks correct.
lons between expressions on successive lines.
The solution is easy: Use parentheses in the definition, as in
You can, however, continue an expression from one line to the next. To
$define SIZE (width + offset)
do this, you need to know how the compiler decides to insert semicolons. The
rule is simple: If the current line ends a complete expression and the next line Then
begins an expression, a semicolon is inserted. To continue an expression from
dimension := SIZE *3
one line to the next, just write it so that it's not complete on the current line. For
example, in is equivalent to
j :=j- dimension := (width + 3) *3
k as intended.
the expression is continued to the second line, since the expression on the first
line is not complete (an expression cannot end with an operator). On the other Polymorphous Operations
hand, in
Icon has a number of polymorphous operations; that is, operations that
j := j apply to more than one data type. For example, the prefix size operator, *,
-k applies to many different data types: *X produces the size of X whether the X is
a semicolon is inserted between the two lines, since the first line contains a a string, list, set, table, or record. Similarly, ?X produces a randomly selected
complete expression and a minus sign is a valid way to begin a new expression. element of Xfor these types, IX generates all the elements of X, and sortO works
for several different types of data.
A useful guideline when you want to continue an expression from one
line to the next is to break the expression after an infix operator, comma, or left Polymorphism simplifies the expression of computations that are com-
parenthesis. mon to different types of data. It's worth keeping this in mind when writing
52 Icon Chapter 2 Chapter 2 Icon 53
procedures; a procedure often can be written to work on different kinds of data. Because the parameter 1st points to the same data as the variable nums, rotateO
An example is this procedure for shuffling values: modifies the contents of nums.
procedure shuffle(X) Sometimes the sharing of data is not wanted. For example, in
every i := *X to 2 by -1 do Tucson := ["Arizona", "Pima", 1883]
X[?i] :=: X[i] City := Tucson
return X both Tucson and City point to the same structure. Consequently, assigning to an
end element of City changes an element of Tucson, and vice versa. That may not be
the intention.
This procedure works for shuffling the characters of a string or the elements of
a list or record. The procedure copy(x) makes a copy of the structure x by duplicating the
values to which it points. For example, after
Pointer Semantics City := copy(Tucson)
Icon's structures (records, lists, sets, and tables) have pointer semantics. A there are two different lists that can be modified independently.
structure value is represented internally by a pointer - a "handle" that refer- The procedure copyO works this way only at the top level: Any struc-
ences the data in the structure. When a structure is assigned or passed through tures in the data pointed to by x are not copied and remain shared by the original
a parameter, the pointer is copied but not the data to which it points. This is as structure and its copy.
fast as assigning an integer.
Another important ramification of pointer semantics structures is that (a
Consider the procedure rotateO, which moves a value from the front of pointer to) a structure can be an element of a structure. An example is
a list and places it at the end:
dates := [1492,1776,1812]
procedure rotate(lst) labels := ["discovery", "revolution", "war"]
local v lookup := [dates, labels]
v := pop(lst) in which lookup is a list that contains (points to) two other lists.
put(list, v)
Pointers can be used to represent structures such as trees and graphs. For
return example, a node in a binary tree might be represented using a record declaration
end such as
Then record node(symbol, Itree, rtree)
nums : = [2, 3, 5, 7] The field symbol contains a string for the contents of a node, while Itree and rtree
rotate(nums) are used as pointers to nodes for the left and right subtrees. For example,
every write(!nums) expr := node("+", node("i"), node("-", node("j"), node("k")))
writes produces a binary tree. The omitted arguments default to null values and serve
3 as "null pointers" in cases where there are no subtrees.
5 The structure that results can be visualized as shown in Figure 2.2.
7
2
54 Icon Chapter 2 Chapter 2 Icon 55
A Tree of Records
expr "+11
In this diagram, the details are omitted, leaving
II i" only what's needed to understand the struc-
ture.
II
- II
IIj"
Figure 2.3
Library Resources
The program library includes a whole directory full of nongraphical
procedures. We can't even provide a concise summary, but here's a small
sampling of what is available.
4 "k"
The strings module includes many procedures for manipulating strings,
such as these:
replace(s1, s2, s3) replace all occurrences of s2 in s1 by s3
rotate(s, i) rotate s by i characters
The numbers module deals with things numerical:
A Record Structure Figure 2.2
gcd(i, j) return greatest common divisor of i and j
The arrows emphasize the fact that structure values are pointers to
blocks of data.
roman(i) convert i to roman numerals
Debugging
If you do use writeO expressions to get information about what is going Evaluating Icon Expressions Interactively
on in a program, you may find it useful to use image(x) in the arguments of
writeO. The procedure image(x) produces a string representation showing the Although Icon itself does not provide a way to enter and evaluate
value and type of x. Using imageO also is safe; write(image(x» never produces individual expressions interactively, there is a program, qei, in the Icon program
an error, although write(x) will if x is not a string or a value convertible to a string. library that does. This program lets you enter an expression and see the result of
its evaluation. Successive expressions accumulate, and results are assigned to
Although adding writeO expressions seems easy, you can get a lot of
variables so that previous results can be used in subsequent computations.
information about a program by tracing procedures. The keyword &trace can be
used to give you information about procedure calls and returns. Setting &trace At the> prompt, an expression can be entered, followed by a semicolon
to -1 turns on procedure tracing and setting &trace to 0 turns it off. A word of and a return. (If a semicolon is not provided, subsequent lines are included until
warning: Trace output may be voluminous, especially in graphics programs that there is a semicolon.) The computation is then performed and the result is shown
use library procedures. as an assignment to a variable, starting with r1_ and continuing with r2-f r3-f
and so on. Here is an example of a simple interaction:
Another way to get information is to set &dump to -1. This gives a listing
of variables and values when program execution ends. > 2 + 3.0;
r1_ := 5.0
Even if you don't tum on procedure tracing or the termination dump, a
> r1_ * 3;
run-time error produces a traceback of procedure calls leading to the expression
r2_:= 15.0
in which the error occurred. It's often worth examining this traceback, rather
than immediately looking in the program at the place the error occurred. If an expression fails, qei responds with Failure, as in
Often a more cerebral approach to debugging is faster and more effective > 1.0 = 0;
than simply producing a lot of information in hopes of seeing something helpful. Failure
For Icon, there are a few common causes of errors that have recognizable The program has several other useful features, such as optionally show-
symptoms that are worth checking before adding writeO expressions or turning ing the types of results. To get a brief summary of qei's features and how to use
on tracing and the termination dump. them, enter :help followed by a return.
Incorrect data types are common causes of errors. In such cases, the error
message on termination indicates the expected type and the actual value. The
message procedure or integer expected accompanied by an "offending value"
of &null usually occurs as a result of misspelling a procedure name, as in
wi rte(message)
Since wirte presumably is a misspelling of write, wirte most likely is an unde-
clared identifier that has the null value when wirte(message) is evaluated.
Hence the error.
You can go a long way toward avoiding this kind of error by doing two
things: (1) declaring all local identifiers, and (2) using the -u option for icont, as
in
icont -u app
This option produces warning messages for undeclared identifiers. In the
example above, wirte probably will show up icont is run, allowing you to
fix the program before it is run.
Chapter 3
Graphics
In the previous chapter, we described the features of Icon that are associated
with ordinary computation as well as facilities that make it easy to process
strings and complicated structures. The rest of this book is about graphics.
The term graphics as used here means more than just drawing. It
includes all kinds of operations that are associated with windows displayed on
the screen of your monitor. You can create windows; draw points, lines, and
various shapes; display text in a variety of sizes and type faces; accept input
directly from the keyboard; determine the position of the mouse when buttons
are pressed and released; and so forth. Plate 3.1 shows some of the effects that
can be produced.
y Figure 3.1
Drawing (lines, shapes, and so forth) is done by other procedures. For
example, the following call of DrawRectangleO draws a rectangle 50 pixels wide
Suppose you want to create a 400-by-300 pixel window on the screen. and 20 pixels high with its upper-left corner at position (60,80) in the window:
This is done with the WOpenO procedure. Arguments give the initial values of DrawRectangle(60, 80, 50, 20)
attributes associated with the window, such as its size. In the case above, this
might be: The result is shown in Figure 3.4.
WOpen("size=400,300")
By convention, the width precedes the height. The result of the WOpenO is a
blank window, as shown in Figure 3.2.
62 Graphics Chapter 3 Chapter 3 Graphics 63
Although it's not shown here, several rectangles can be drawn with one The attributes associated with a window can be changed after the
call of DrawRectangleO, which takes an arbitrary number of arguments that window is opened. For example,
specify successive quadruples of x-y coordinates, width, and height. This is true Fg("white")
for most drawing procedures. Bg("black")
When the program terminates, the window disappears. The easiest way changes the foreground color to white and the background color to black. The
to keep this from happening immediately is to call WDoneO, which waits until current window appearance is not altered, but subsequent drawing operations
a q (for"quit") is typed. Only then does WDoneO return. After that, the program are affected.
terminates and the window vanishes.
The procedure WAttribO can be used to set or get the values of attributes.
Several attributes can be set in one call. For example,
Window Attributes
WAttrib(lfg=white", "bg=black")
A window has numerous attributes; a full list is given in Appendix G.
has the same effect as
Two important attributes are the background and foreground colors of a
window. A window is filled with the background color when it is opened. Text, Fg("white")
points, and lines are drawn in the foreground color. As indicated in the Bg("black")
preceding example, the default background color is white and the default If the equal sign and value are omitted in the argument to WAttribO, the
foreground is black. Either or both of these can be changed by adding arguments value of the attribute is returned. Numeric attributes produce integers; most
to the WOpenO call. For example, other attributes are strings. For example,
WOpen(ls ize=400,300", "bg=light gray") foreground := WAttrib(lfg")
WWrite(" Hello world!")
DrawRectangle(60, 80, 50, 20) assigns the foreground color to the variable foreground.
produces a window such as the one shown in Figure 3.5. Windows in Icon have many other attributes. For example, the attribute
Iinewidth can be set to control the thickness of drawn lines. Thus,
WAttrib(llinewidth=3")
causes subsequent DrawRectangleO calls to produce borders that are three
pixels thick.
64 Graphics Chapter 3 Chapter 3 Graphics 65
Some procedures draw shapes filled in the foreground color rather than repeat {
outlines. For example, w:= ?Width
h := ?Height
Fg(" white")
x := ?Width - w / 2
FiIIRectangle(200, 100, 50, 50)
Y:= ?Height - h / 2
draws a solid white square, as shown in Figure 3.6. DrawRectangle(x, y, w, h)
WDelay(300)
Hello world! Legibility }
A gray background can soften the vi-
sual appearance of a window, but it end
also reduces legibility. In particular, When the sizes and positions of the rectangles are selected in this way, portions
white on gray often is difficult to dis- of them may fall outside the window. Such portions are "dipped" and not
tinguish.
drawn. The procedure WDelay(300) delays program execution 300 millisec-
onds. This prevents the drawing from proceeding too rapidly to appreciate.
A typical display from this program is shown in Figure 3.7.
Random Rectangles
Figure 3.6
Mindless, random draw-
ings like this are easy to pro-
EraseAreaO is like FiIIRectangleO except that it fills with the background duce and sometimes are at-
color. EraseAreaO typically is called with no arguments, which erases the entire -
tractive. We'll show more
window. sophisticated applications
of this technique later in the
Example - Random Rectangles book.
c::J
display rectangles of randomly selected colors and sizes - a (poor) sort of Figure 3.7
"modem art".
Here we'll use a window 500 pixels wide and 300 pixels high and draw We can make the results more interesting by allowing for filled rect-
outlines of rectangles. The dimensions of the rectangles will be selected ran- angles as well as outlines and by providing a selection of colors. A typical result
domly from between one pixel and the window dimensions. Their positions will is shown in Figure 3.8.
be randomly selected also. colors := ["red", "blue", "green", "yellow", "purple", "white", "black"]
$define Width 500 Rect := [FiIIRectangle, DrawRectangle]
$define Height 300 WOpen("size=" II Width II"," II Height)
link graphics repeat {
procedure mainO w:= ?Width
local x, y, w, h h := ?Height
x := ?Width - w / 2
WOpen("size=" II Width II "," II Height) Y:= ?Height - h / 2
66 Graphics Chapter 3 Chapter 3 Graphics 67
Pressing and releasing the right mouse button could be used to cause the
Fg(?colors)
drawing program given earlier to stop and start. Similarly, pressing the q key on
(?Rect)(x. y, w, h)
the keyboard could be used to cause the program to terminate.
WDelay(300)
} To illustrate this, a check for events can be added at the end of the
drawing loop:
More Random Rectangles
repeat {
You will, of course, have to
imagine the colors. All we
can do here is represent Fg(?colors)
them by shades of gray. (?Rect)(x, y, w, h)
We'll have more to say about WDelay(300)
this later.
while *PendingO > 0 do {
case EventO of {
&rpress: {
until EventO === &rrelease
Figure 3.8 }
"q": exitO
Events }
}
When you run the program shown above, the shapes change and go by, }
beyond your control. You might want to be able to stop the drawing process to
The while loop continues as long as there is a pending event. If the
examine the results more closely, as we did to get the images shown in the
pending event is a q, program execution is terminated via exitO. (The window
preceding figures. This can be done by having the program look for events.
is closed and vanishes in such a case.) If the right mouse button is pressed, control
When the mouse pointer is in a window, an event is produced by drops into another loop waiting for the button to be released. All other events are
pressing a key or a mouse button, moving the mouse with a button pressed, or ignored.
releasing a mouse button.
Events are queued so that they are not lost if it takes a while for the Window Management
program to get around to processing them. The queue is an Icon list that is the
value of PendingO. For example, The graphics system determines the appearance of a window and allows
the user to control its size and location on the screen. The appearance of a
*PendingO > 0
window and how it is manipulated depend on the particular graphics system.
succeeds if an event is pending.
Most graphics systems provide a title bar that contains an identifying
The procedure EventO produces the next event and removes it from the label for the window. The label can be set when the window is opened using the
queue. If there is no pending event, EventO simply waits for one. When EventO label attribute, as in
removes an event from the queue, the position on the screen at which the event
WOpenC'label=Help")
occurred is recorded in keywords.
There usually is a border that frames the window and sets it off from
The value of a keyboard event is a one-character string corresponding to
other items on the screen. Some graphics systems provide control areas at the
the key pressed. Mouse events are integers for which there are corresponding
comers that allow the user to manipulate the window. Using these control areas,
keywords. For example, &rpress and &rrelease are the values for the events that
the user can move the window, resize it, and so forth. In this way, the user can
occur when the right mouse button is pressed and released, respectively.
68 Graphics Chapter 3 Chapter 3 Graphics 69
manipulate the window without any action on the part of the program that The library also contains a collection of utilities, demonstrations, and
created the window. Typical windows are shown in Figure 3.9. other graphics programs. These are useful not just for the tasks they performbut
also as programming examples. Studying these can provide additional insight
into Icon graphics.
.: ,iJ.1I: '.
or setting their attributes. For example, the following lists contain different
til d< on one or tho 0G'ds abcMo to got ..."....
Inf'""""tlon about tho operation. T. 11 • •'&0\ • • •111.• • 1: • .." . . . . M1, ,_,•• -n.
T t ltt.. Jul, ..,..•••••,.. •••
attributes for use in opening windows for different situations:
To retlrn to tho preIIl.... help pago. tyle ESC.
To exit f .... tho help S\lStoIo. tyle q. normal := ["bg=white", "fg=black"]
notice := ["bg=red", "fg=white"]
pasteboard := ["bg=gray", "fg=black", "size=640,480"]
Typical Windows Figure 3.9 Then a window with the attributes given in normal can be opened by
Different graphics systems provide different appearances and different WOpen ! normal
ways of manipulating windows. Which manipulations are allowed and
how they are done contribute to the "look and feel" of the graphics a window with the attribute given in notice by
system. The window on the left is typical of a platform using the X WOpen ! notice
Window System and the Motif Window Manager. The window on the
right is from Windows 95. and so on. Note that this allows windows to be opened with different numbers
of attributes without having to specify them in the text of the program.
Both the user and the program work through the graphics system. Since
graphics systems vary, it's inevitable that some graphics systems support Using the Title Bar to Show Program Status
operations that others don't. Consequently, some features that work on one
system may not work on another. The window's label attribute, which appears in its title bar, can be
changed at any time. Updating the title bar is a way to inform the user of an
By default, if the graphics system supports it, Icon prevents the user from application's status.
resizing its windows. User resizing can be enabled by using the resize attribute,
as in Some applications update the label attribute every time the user switches
to a new kind of task. Other applications use the label attribute to keep the user
WAttrib("resize=on") informed of the current time. The following section of code reads a list of files,
Most graphics systems provide a way to record a "snapshot" of a updating the title bar with the name of each file read.
window. That's how the images shown in this book were produced. every filename := !files do {
WAttrib("label=reading II II filename II "...")
Library Resources # process file
}
In later chapters, we'll use this section to highlight some of the more
useful library procedures that are related to the subject at hand.
Chapter 4
Drawing
Points
The most elementary drawing procedure is
DrawPoint(x, y)
which draws a single point at the specified x-y coordinate position. Drawing a
point sets the pixel at that location to the foreground color.
Any number of points canbe drawnina single call of DrawPointOsimply
by adding more arguments, two for each coordinate position. An example is
DrawPoint(10, 20, 12,22, 14,24, 16,26)
which draws four points to produce a short dotted diagonal line.
Many images are most naturally composed by drawing all their points,
one by one. An example is the Sierpinski triangle (also known as the Sierpinski
gasket), a simple but fascinating fractal. There are many ways to draw the
Sierpinski triangle, most of them mysterious. We won't explain the underlying
71
72 Drawing Chapter 4 Chapter 4 Drawing 73
principles here, but if you want to learn more, see Barnsley (1988) or Peitgen, Sierpinski's Triangle
Jiigens, and Saupe (1992).
Starting with a blank window,
The following code segment draws the Sierpinski triangle on a 400 x 400 Sierpinski/s triangle gradually is filled
area. The procedure WQuitO succeeds and causes the loop to terminate when the in, pixel by pixel. The process contin-
user presses the q key. An example of the result is shown in Figure 4.1. ues indefinitely, but since the window
has a finite number of pixels, the image
$define Width 400 eventually stops changing. Here's what
$define Height 400 it looks like after about 80/000 itera-
tions.
$define X1 0 # lower-left vertex
$define Y1 Height
$define X2 (Width / 2) # top vertex
$define Y2 0
$define X3 Width # lower-right vertex
$define Y3 Height Figure 4.1
x:= Width / 2 # current point
Y:= Height / 2
Lines
until WQuitO do { # loop until interrupted
case ?3 of { # pick corner randomly Since a window is composed only of pixels, any image can be produced
1: { by drawing it point by point. Usually, though, drawing individual pixels is
x := (x + X1) / 2 # move halfway to corner tedious, inefficient, and computationally awkward. Even drawing a straight line
Y := (y + Y1) / 2 between two points is painful when done pixel by pixel.
}
The procedure
2: {
x := (x + X2) / 2 # move halfway to corner DrawLine(x1 , y1 , x2, y2)
Y := (y + Y2) / 2 draws a line from the first x-y coordinate position to the second.
}
3: { Many images can be produced just by drawing lines. Here's a procedure
x := (x + X3) / 2 # move halfway to corner that draws regular polygons. Figure 4.2 shows a regular polygon drawn by this
Y := (y + Y3) / 2 procedure.
} # Draw a regular polygon with the specified number of vertices and
} # radius, centered at (cx,cy).
DrawPoint(x, y) # mark new location
} procedure rpoly(cx, cy, radius, vertices)
local theta, incr, xprev, yprev, x, y
A more complex version of this program, from the Icon program library, theta := 0 # initial angle
produced the color images seen in Plate 4.1. incr := 2 * &pi I vertices
xprev := cx + radius * cos(theta) # initial position
yprev := cy + radius * sin(theta)
74 Drawing Chapter 4 Chapter 4 Drawing 75
Regular Polygon
This octagon was drawn by Figure 4.3
rpoly(200, 200, 180, 8)
As the number of vertices increases, The preceding examples draw one line at a time. Like DrawPointO,
the corresponding polygons become DrawLineO accepts an arbitrary number of arguments, a pair for each coordinate
more circular in appearance.
position. Lines are connected, drawing from position to position. For example,
DrawLine(200, 50, 250, 150, 300, 100, 200, 50)
draws a triangle with vertices at (200, 50), (250, 150), and (300, 100).
If coordinate positions are computed during program execution, it
sometimes is more convenient to put them on a list and use list invocation to
draw the lines. Thus, the regular star program could be recast as follows, using
Figure 4.2
only a single call of DrawLineO:
theta :=°
incr := skips * 2 * &pi / vertices
Regular stars can be drawn by skipping over vertices in the drawing points := [cx + radius * cos(theta), cy + radius * sin(theta)]
process. All that's necessary is to change the angular increment accordingly:
every 1 to vertices do {
incr := skips * 2 * &pi / vertices theta +:= incr
with the procedure header rstar(cx, cy, radius, vertices, skips). put(points, cx + radius * cos(theta), cy + radius * sin(theta»
}
The most interesting figures usually occur when the number of vertices
and the number of skips are relatively prime, so that a line is drawn to every DrawLine ! points
vertex only once and each vertex is visited. Figure 4.3 shows an example. The procedure DrawSegmentO is similar to DrawLineO, but instead of
connecting lines from position to position, line segments are drawn between
successive pairs of positions. With only two positions (four arguments),
DrawLineO and DrawSegmentO produce the same results. DrawSegmentO is
useful for drawing several disconnected lines in one call.
76 Drawing Chapter 4 Chapter 4 Drawing 77
For example, the spokes of a wheel can be drawn as follows. An example Rectangles
is shown in Figure 4.4.
As shown in Chapter 3, DrawRectangleO draws a rectangle specified by
# Draw n spokes with the given radius, centered at (cx,cy).
its comer location and size:
procedure spokes(cx, cy, radius, n)
DrawRectangle(x, y, width, height)
local theta, incr, points
If width and height are positive, x and y specify the upper-left comer. However,
theta := 0
either width or height (or both) can be negative to extend the rectangle leftward
incr := 2 * &pi / n
or upward from the starting point. This is true for all procedures that specify a
points := [] rectangular area.
every 1 to n do { Here is a code segment that produces the simple design shown in Figure
put(points, cx, cy) 4.5:
put(points, cx + radius * cos(theta), cy + radius * sin(theta)) every x := 10 to 140 by 10 do
theta +:= incr
}
DrawRectangle(x, x, 300 - 2 * x, 400 - 2 * x)
DrawSegment ! points
Rectangles
return Try changing the spacing and number of rect-
end angles to see what optical effects you can get.
-
Spokes
This figure was drawn by
spokes(200, 200, 180, 25)
Notice the visual artifacts at the center.
Later in this chapter, we'll add a hub
and a rim to make this look like a
wheel. What happens if DrawLineO is
used in place of DrawSegmentO?
Figure 4.5
Figure 4.7
Figure 4.6
The procedure FiIIPolygonO draws a polygon that is filled in the fore-
ground color. Changing DrawPolygonO to FiIIPolygonO in the preceding ex-
ample produces the result shown in Figure 4.8.
EraseArea(x, y, w, h) is similar to FiIIRectangte(), but itfills a rectangular
area using the background color instead of the foreground color. The arguments
x and y default to the upper-left pixel of the window, and wand h default to
values that extend the area to the opposite edges, so that EraseArea() with no
arguments erases the entire window.
DrawRectangle(), FillRectangle(), and EraseArea() all draw multiple
rectangles if provided with additional sets of arguments.
80 Drawing Chapter 4 Chapter 4 Drawing 81
Filled Triangles Complicated filled polygons can produce interesting designs, as shown
Later in this chapter, we'll show how
in Figure 4.10.
figures can be filled with patterns in-
stead of being solid. A Filled Star
It may not look like it, but this is a filled
regular star. There are 504 vertices
drawn with skips of 251.
Figure 4.8
A Dozen Eggs
procedure wheel(cx, cy, radius, n, hubradius, tirewidth, rimwidth)
local i, tireradius
spokes(cx, cy, radius, n) 000000 We'llleave itto you to draw the chicken.
"Arched" stars provide another example of the use of arcs. You can skip
return this example if you don't enjoy trigonometry. See Figure 4.13.
end # draw arched star at (x,y) with eccentricity ecc
procedure astar(cx, cy, radius, vertices, ecc)
A Wheel
local acr, x, y, r, kappa, theta, extent
This wheel was drawn by
if ecc < 0.1 then ecc := 0.1 # ensure valid eccentricity
wheel(200, 200, 180,25,25,8, 10) kappa := &pi / vertices # half of subtended angle
Notice that the hub covers the visual acr := radius / (ecc * cos(kappa» # arc center radius
artifacts that are noticeable in Figure x := acr - radius * cos(kappa)
4.4. y := radius * sin(kappa)
r := sqrt(y 1\ 2 + X 1\ 2) + 0.5 # arc rounded up
extent := 2 * atan(y, x) # arc extent
theta := &pi / 2 # angle to arc center
every 1 to vertices do {
x := cx + acr * cos(theta) # center of arc
y := cy + acr * sin(theta)
Figure 4.11
DrawCircle(x, y, r, theta + &pi - extent / 2, extent)
theta +:= 2 * kappa
}
Partial arcs also are useful in some kinds of drawings. The familiar egg return
shape, which is pleasing but not representable by a simple mathematical curve,
provides an example. A series of arcs can be combined to produce a reasonable end
approximation to the shape of an egg, as shown in Figure 4.12.
every y:= 701150 do
every x := 50 to 350 by 60 do
DrawCircle(
x, y, 20, 0.0, &pi,
x + 20, y, 40, &pi, &pi 14,
x, Y - 20, 12, 5 * &pi 1 4, &pi 12,
x-20, y, 40, 7 * &pi 14, &pi/4
)
84 Drawing Chapter 4 Chapter 4 Drawing 85
Arched Star The defaults for the angular measurements are the same as for
This arched star was drawn by DrawCircleO. FillArcO draws filled arcs and takes the same arguments as
DrawArcO·
astar(200, 200, 180, 9, 1.0)
Additional sets of arguments can be given in all four procedures to
Try other values to see what effects you
can get. produce multiple arcs with one procedure call.
Smooth Curves
The procedure DrawCurveO draws a smooth curve through the x-y
coordinates specified in its argument list. If the first and last coordinates are the
same, the curve is closed. An example is shown in Figure 4.15.
Curved Star
Figure 4.13
1his curved star was produced by using
DrawCurveO with the same vertices
Arcs also can be drawn by that were used in Figure 4.3.
WAttrib(llinewidth=3") The line style "dashed" is similar to "striped", except that no pixels are
sets the line width for subsequent line drawing to three pixels. Wide lines are drawn in the gaps between those drawn in the foreground color. Thus, the
centered along the line that would be drawn if the line width were 1. background in the gaps is left unchanged.
Using a larger line width allows the tire shown in Figure 4.11 to be drawn The following code segment uses line styles to depict a highway intersec-
tion.
with one arc. The loop previously used to draw the tire one line at a time can be
replaced by WAttrib(llinewidth=3")
WAttrib(llinewidth=" II tirewidth) # main road
DrawCircle(cx, cy, tireradius + tirewidth / 2)
Fg("light gray")
Another use of line widths is illustrated by Figure 4.16. FiIIRectangle(O, 50, 500, 160) # pavement
Fg("black")
Sunset Scene DrawLine(O, 126, 500, 126) # double center line
Oh, to be in Bali. DrawLine(O, 134, 500, 134)
WAttrib(llinestyle=striped")
# pavement
Fg("black")
t WAttrib(llinestyle=dashed")
Figure 4.16 DrawLine(160, 210,160,300) # center line
The code to draw this figure is: The result is shown in Figure 4.17.
Reversible Drawing
WAttrib(llinewidth=3")
All drawing operations combine some source pixels (to be drawn) with every x := 50 to 350 by 100 do {
some destination pixels (presently in the window). The way source and destina- every y := 60 to 240 by 60 do {
tion pixels are combined is specified by the attribute drawop. The default value WAttrib("dx=" II x, "dy=" II y)
of drawop is "copy·, in which case source pixels replace destination pixels, as DrawCurve(20, -20, -5, 0, 20, 20, 35, 0,
illustrated by previous examples. 0,-20,-35,0,-20,20,5,0,-20,-20)
The value "reverse" allows reversible drawing. If the drawop attribute is }
set to "reverse", drawing changes the pixels that are in the foreground color to }
the background color, and vice-versa. The color drawn on destination pixels that
are neither the foreground nor the background color is undefined, but in any Pretzels
event, drawing a pixel a second time restores the pixel to its original color. Try modifying the code to produce this
Drawing the same figure twice in reverse mode erases it. For example, 00 00 00 00 figure without coordinate translation.
WAttrib("drawop=reverse") 00 00 00 00
every x := 1 to 100 do {
Fill Rectangle(x, 100, 10,20)
WDelay(1)
00 00 00 00
FiIIRectangle(x, 100, 10, 20)
}
00 00 00 00
Figure 4.18
FiIIRectangle(x, 100, 10, 20)
moves a small rectangle horizontally across the screen, leaving an image only at Note that setting dx or dy replaces the previous value; the effects do not
the end. accumulate. Two calls of WAttrib("dx=1 Oil) are not the same as WAttrib(ldx=20").
The normal mode of drawing can be restored by
WAttrib("drawop=copy") Clipping
The procedure Clip(x, y, w, h) restricts drawing to the specified rectan-
Coordinate Translation gular area. Drawing outside the clipping region is not an error; everything
proceeds normally except that nothing outside the region is altered. Clipping is
The attributes dx and dy translate the position at which output is placed applied on a pixel basis: Characters can be cut in half, arcs cut into segments, and
in a window in the x and y directions, respectively. For example, as a result of soon.
WAttrib("dx=10", "dy=20") The extent of clipping also can be queried or set using the individual
output to the window is offset by 10 pixels in the x direction and 20 pixels in the attributes clipx, clipy, c1ipw, and cliph. Clipping is disabled by calling CIiPO with
y direction, and DrawCircle(100, 100) now draws a circle with its center at no arguments. When clipping is disabled, the entire window is writable, but the
(110,120). Positive offsets like this move the origin to the interior of the window, graphics system still discards any output beyond the window's edge.
giving negative locations to some parts of the window. In the example, the Clipping is particularly useful when making a drawing whose extent
upper-left corner now is addressed as (-10,-20). cannot be controlled easily. For example, the following code segment produces
Changing the offsets makes it easy to draw the same figure at different rings confined to a frame, as shown in Figure 4.19.
places in a window. The following code segment produces the image shown in
Figure 4.18.
90 Drawing Chapter 4 Chapter 4 Drawing 91
DrawRectangle(20, 20, 360, 260) # draw frame Tips, Techniques, and Examples
Clip(21, 21, 359, 259) # clip to inside of frame
every 1 to 50 do { Fractal Stars
x:= ?400 # choose random coordinates
y:= ?300 The previous sections show how interesting figures can be produced
WAttrib(lfg=black", Ilinewidth=5") using only the repetition of simple rules. "Fractal stars" show what can be done
DrawCircle(x, y, 30) # draw ring in black with only slightly more complicated operations. A fractal star consists of
WAttrib(lfg=white", Ilinewidth=3") successively smaller replicas of a figure, producing a result with "self-similar-
DrawCircle(x, y, 30) # color with white band ity" in which small parts have the same structure as the overall figure, but at a
} reduced scale. We'll limit the replication and reduction by specifying a limit on
the number of "phases", so that we can get a complete drawing. As with
A Field of Rings Sierpinski's triangle, the resolution of the window is the practical limiting factor.
Fortunately, even a small number of phases can produce interesting results.
Can you imagine how to produce this
image without clipping? A fractal star is produced by drawing a sequence of connected lines, each
at a constant angle from the next. If the basic design has n vertices and there are
p phases, the number of lines drawn is n x (n _l)p-l. The computation of the
lengths of the lines is central to the idea. If the ratio of "radii" for successively
smaller figures is " the length of the ith line is ,p-!-l where f is the number of
times n divides i evenly, stopping at p-l. That sounds complicated, but the
computation is relatively simple, as shown in the following procedure:
# Draw a fractal star with the specified number of vertices, phases,
Figure 4.19
# radius ratio, and angular increment. The parameter extent determines
# the "diameter" of the star; x and yare used to position the
# figure in the window.
Library Resources
procedure fstar(x, y, extent, vertices, phases, ratio, incr)
The gpxop module, which is incorporated by link graphics, includes a local theta, xprev, yprev, resid, factors, length, i
procedure Translate(dx, dy, w, h) for setting the dx and dy attributes and theta := 0 # starting angle
optionally setting a clipping region.
every i := 0 to vertices * (vertices -
1) 1\ (phases - 1) do {
The barchart and strpchrt modules provide families of procedures for resid := i # residual after division
creating bar charts and strip charts respectively. factors := 0 # number of factors
The fstars, jolygs, and orbits modules contain procedures for drawing # divide and count
fractal stars, "jolygons", and orbits. until (resid % (vertices - 1) -= 0) I (factors >= (phases - 1)) do {
resid /:= (vertices - 1)
The drawcard module supplies drawcard(x, y, c), which draws an image factors +:= 1
of a playing card. }
length := extent * ratio 1\ (phases - factors - 1)
x +:= length * cos(theta) # end position
y +:= length * sin(theta)
if i > 0 then # skip first point
DrawLine(xprev, yprev, x, y)
92 Drawing Chapter 4 Chapter 4 Drawing 93
xprev:= x # update previous position procedure that plays at being a "modem artist" by recursively subdividing the
yprev:= y window into rectangles, either drawing or filling at random. An example of the
theta +:= incr result is shown in Figure 4.21. rectO calls itself recursively to make smaller
} rectangles. The decision to split is made in divide(), which enforces a minimum
size but also includes an element of randomness that is controlled by Bias.
return
$define MinSide 10 # minimum size of a rectangle side
end
$define Gap 3 # gap between rectangles
Selecting parameters that produce visually interesting results is some- $define Bias 20 # bias setting; affects size choices
thing of an art, as is positioning the figure on the window. The results can be
# rect(x, y, W, h) -- draw rectangle, possibly subdivided, at (x,y)
fascinating, as shown in Figure 4.20.
procedure rect(x, y, w, h)
:
IMM ,. Ie,
IIaIla
IMM
: :
MM ,..
IIaIla
, 1M ..
local d
if d := divide(w < h) then { # if cut horizontally:
::
I: rect(x, y, w, d) # draw top portion
I. . . . ,.
IIa Il!lI
ICI I. . . .
I: else if d := divide(w) then {
rect(x, y, d, h)
# if cut vertically:
# draw left portion
IMM ,. ICI MM
rect(x + d, y, w - d, h) # draw right portion
: 11III 11III
: }
=:
: lBl! :
::: else {
if ?2 = 1 then
# else draw single rectangle
.... :
FiIlRectangle(x, y, w - Gap, h - Gap) # solid
III'IIIl'II
JIll lei
II'IIIl'II
I. Ie, .... else
DrawRectangle(x, y, w - Gap -1, h - Gap -1) # open
}
Fractal Stars Figure 4.20
return
It may seem surprising that these two figures that are so dissimilar were
drawn by the same procedure with only different parameters. The
end
fractal star at the left was drawn by # divide(n) -- find division point along length n
fstar(20, 165, 330, 5, 5, 0.35, 4 * &pi / 5) procedure divide(n)
while the one at the right was drawn by if (n > 2 * MinSide) & (?n > Bias) then
fstar(20, 245, 330, 4, 8, 0.47, &pi /2) return MinSide + ?(n - 2 * MinSide)
else
See Delahaye (1986) or Lauwerier (1991) for additional information
about fractal stars. fail
end
Random Rectangles
processor speed may be the limiting factor. If the time needed for computation DrawCircle(x, y. Radius) # erase sun
and drawing is insignificant, a loop using WDelay(50) will display about twenty x+:= OX # set next location
frames per second. y +:= DY
The following program uses this technique to display an animated }
version of the sunset scene of Figure 4.16. The sun starts high in the sky, then WDoneO
sinks slowly below the horizon. Figure 4.22 shows some of the positions of the
sun before it sets. end
until WQuitO do { # loop until interrupted Other animation methods are described in Chapters 7 and 9.
Fg("white") Avoiding Round-Off Problems
every b := !blist do # erase all old circles
DrawCirde(b.x, b.y, Radius) Coordinate values in drawing procedures are integers. Many computa-
every b := !blist do { tions that involve coordinate values, on the other hand, use real (floating-point)
b.x +:= b.dx arithmetic. For example, in
# update position
b.y +:= b.dy x+r * cos(theta)
98 Drawing Chapter 4 Chapter 4 Drawing 99
x and r may be integers, but cos(theta) produces a real number. Multiplying an x := cx + radius * cos(theta) # new position
integer by a real number produces a real number; Icon takes care of the y := cy + radius * sin(theta)
conversion automatically. Similarly, the addition of an integer and a real DrawLine(xprev, yprev, x, y)
number produces a real number. Consequently, the value of the preceding xprev:= x # update old position
expression is a real number. yprev:= y
As computation of successive coordinates continues, it is typical for all }
values to be real numbers. It's often best to compute coordinates as real numbers Duplicating expressions to handle the exception is unattractive, espe-
because this allows sub-pixel accuracy. If the arguments in a drawing operation cially if they are complicated, as in drawing fractal stars. One way to avoid the
like duplication of expressions is to perform all the calculations in the loop but skip
DrawLine(x1 , y1 , x2, y2) drawing on the first pass through the loop:
are real numbers, they are automatically converted to the integers that DrawLineO every i := 0 to vertices do {
expects. Conversion truncates the real numbers, discarding any fractional parts. theta +:= incr
x := cx + radius * cos(theta) # new position
Floating-point calculations are inexact. It is possible to start with a y := cy + radius * sin(theta)
coordinate value of 200.0, make a series of calculations that are designed to if i > 0 then # draw only after first pass
return to the same point, and end up with a resulting value of 199.9999. When DrawLine(xprev, yprev, x, y)
that's truncated to an integer, it becomes 199 and addresses a different pixel. The xprev := x # update old position
result may be a "kink" in a line that should be straight or two lines that fail to yprev:= y
meet as expected. }
An easy way to avoid such problems is to start from the"center" of a pixel Notice that a local identifier has been added to serve as a loop counter and that
instead of the "edge", in this case by using a value of 200.5. When the series of the loop now starts with 0, bringing the computation of the initial coordinates
calculations ends up with 200.4999, the truncation to an integer produces the into the loop.
same pixel coordinate as at the start.
There's a simpler way of detecting the first pass through this loop. Local
This technique doesn't really reduce round-off errors, but it reduces the variables have the null value initially when a procedure is called. Consequently,
probability that the errors will produce visible results. xprev and yprev are null until they are assigned other values at the end of the first
pass through the loop. Testing one of them for the null value earlier in the loop
Starting Drawings therefore can be used to detect the first pass.
As illustrated in the code for drawing regular stars and fractal stars, the As described in Chapter 2, Icon provides an easy way to determine if a
first computation in a series often is used to get a starting point for a drawing. variable is null or not. The expression \x succeeds if x is not null but fails if it is.
Lines are then drawn from the previously computed point to a newly computed Consequently the test can be written as
one. The firstpointis an exception, since no line is drawn to it, even though it may if \xprev then
be computed in the same way as the rest of the points. DrawLine(xprev, yprev, x, y)
One way to handle this is to compute the first point before the loop in Note that the loop counter no longer is needed.
which the rest are computed, as in:
In this example, it is possible to make the test even more concise. Since
xprev := cx + radius * cos(theta) # initial position a procedure is not called if one of its argument expressions fails, the loop can be
yprev := cy + radius * sin(theta) written as
every 1 to vertices do { every 0 to vertices do {
theta +:= incr theta +:= incr
100 Drawing Chapter 4 Chapter 4 Drawing 101
Figure Orientation
Consider the following code segment for plotting a sine curve: every x := 0 to points do
DrawPoint(xoff + x, base + yscale * -sin«2 * &pi * x) / xscale»
$define points 300
$define xoff 50 A somewhat different problem with orientation occurs when a figure
$define base 200 needs to be oriented so that it doesn't appear to defy gravity. For example, the
$define yseale 60 octagon in Figure 4.2 balances on a vertex instead of resting on a side.
$define xseale 100 For an n-sided regular figure, a horizontal bottom side can be obtained
every X := 0 to points do by using a starting angle for the first vertex of rt/2 + rt/ n. This works whether n
DrawPoint(xoff + x, base + yseale * sin((2 * &pi * x) / xseale» is odd or even.
The result is shown in Figure 4.24. It looks good at a glance, but it's upside down. Long Argument Lists
The default value for MSTKSIZE is 10,000, where the unit is a word. Since designed to produce drawings with an element of randomness, however,
the implementation of Icon is complex, it's generally not worth trying to figure different random sequences may be needed for each program execution. This
out a precise value for MSTKSIZE that is suitable. It's worth knowing, however, can be accomplished by setting &random to different values depending on, for
that every procedure argument occupies two words. On platforms with ad- example, what time of day the program is run. The procedure randomizeO does
equate memory, such as most modern workstations, setting a large value, as in this, taking into account several variable factors. Calling this procedure at the
setenv MSTKSIZE 500000 beginning of program execution virtually assures that each program execution
will produce a different random sequence. The procedure randomizeO is incor-
normally lets you work without having to worry about stack overflow. porated from the library by a
Default Values link random
declaration.
Some procedures use omitted arguments to provide defaults, so that
values that occur frequently do not have to be specified explicitly. For example,
in log(r1, r2), if the second argument is omitted, the base defaults to &e.
Consequently, log(r) produces the natural logarithm of r.
Consider rstarsO, which draws regular polygons if it is called with a
value of 1 for skips. For example,
rstars(200, 200, 180, 8, 1)
draws an octagon.
It's easy to provide a default of 1 for skips:
procedure rstars(cx, cy, radius, vertices, skips)
local theta, incr, xprev, yprev, x, y
/skips := 1
Randomizing Drawings
Turtle Graphics
Concepts
105
106 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 107
Turtle graphics originally appeared in the Logo programming language The procedure TResetO clears the stack, erases the window, and returns
(Abelson and diSessa, 1980), but turtle graphics has been added to many other the turtle to the center of the window, facing upward.
programming languages. The features and details of turtle graphics vary from
That's about all there is to turtle graphics, although we've omitted a few
implementation to implementation. Some implementations are simple while inessential procedures and some functionality in order to simplify the presenta-
others are elaborate, supporting multiple turtles and color. Despite their differ- tion here.
ences, all implementations share the same conceptual framework.
In Icon, there is a single turtle that starts out in the center of the subject Drawing with Turtle Graphics
window and faces toward the top. If there is no subject window, a 500-by-500
pixel window is opened. As indicated above, turtle graphics are best suited to drawings that can
be expressed in terms of simple, straight-line movements and changes of
The turtle moves in a straight line and changes its heading in response direction.
to commands. When it moves, it mayor may not draw a line, depending on the
command. Drawing is done in the current foreground color. An example is a "random walk" in which the turtle moves in a series of
steps at directions that are chosen at random. Here's a simple example:
Distances are measured in pixels. Angles are measured in degrees, and
the positive direction is clockwise. 00 is in the positive x direction, so the initial repeat {
heading of the turtle is -90 0 (facing straight upward). TDraw(1)
TRight(?61 - 31)
Procedures }
The turtle moves forward and draws for one unit. It then turns right an amount
The turtle commands are expressed in terms of procedure calls. The
in the range -30 0 and +30 0 and repeats. This goes on until the program is
following procedures are provided:
interrupted. An example of the result is shown in Figure 5.1.
Two procedures draw lines. TDraw(n) moves the turtle forward n units
in the direction it is facing, drawing a line from where it was to where it winds A Random Walk
up. TDrawto(x, y) turns the turtle to face toward the location (x,y) and moves the
turtle there while drawing a line. Increasing the amount in which the
direction can change between succes-
TSkip(n) is like TDraw(n) except that the turtle does not draw a line. sive steps results in a more erratic path.
TGoto(x, y) moves the turtle to (x,y) without drawing a line or changing its The turtle may, of course, wander out
heading. of the window. If this happens, it may
or may not reenter the window later.
TLeft(d) and TRight(d) tum the turtle d degrees to the left and right,
respectively. The procedure TFace(x, y) turns the turtle to face the location (x,y),
provided that the turtle is not already at (x,y). These procedures do not move the
turtle.
There are three procedures for finding the turtle's location and heading.
TXO and TYO return its x and y coordinates, respectively. THeadingO returns the
direction in which it is facing.
The state of the turtle - its location and heading - can be saved on a Figure 5.1
stack and later restored from the stack. The procedures TSaveO and TRestoreO
do this.
Many interesting figures can be drawn by repeating simple turtle com-
mands. The following code segment draws spiral figures in which the angular
108 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 109
change and amount of movement have random components. Four examples are The usefulness of being able to save and restore the state of the turtle is
shown in Figure 5.2. illustrated by the following procedure, which draws a random "bush":
angle := 30 + ?149 procedure bush(n, len)
incr := sqrt(4 * ?O) + 0.3
TSaveO
side:= 0
TRight(?71 - 36)
while side < 270 do {
TDraw(?len)
TDraw(side +:= incr)
TRight(angle) if n > 0 then
} every 1 to ?4 do
bush(n - 1, len)
TRestoreO
return
end
This procedure might be used as follows:
TSkip(-120) # position root
bush(n := 4 + ?4, 300/ n)
A Bush
Try using this procedure to produce
other bushes and see how much they
differ from this one. Also try changing
some of the constants in the procedure
to see if you can get more interesting
results.
A procedure like TGoto(x, y) simply changes T_x and T_y: The procedures TSaveO and TRestoreO simply push and pop the state
variables, respectively:
procedure TGoto(x, y)
procedure TSaveO
T_x:= x
T_y:= y push(T_stack, T_deg, T _y, T_x)
return return
end
end
The procedure TSkip(n) also changes T_x and T_y, but since the skip is procedure TRestoreO
in the direction the turtle is facing, the new location must be computed: T_x := pop(T_stack)
procedure TSkip(n) T_y := pop(T_stack)
local rad T_deg := pop(T_stack)
So far we haven't explained how the state variables are initialized. This global T_x, T_y # current location
is done by the procedure TlnitO: global T_deg # current heading
global T_stack # turtle state stack
procedure TlnitO
# TlnitO -- initialize turtle system, opening window if needed
initial {
if /&window then procedure TlnitO
WOpen("width=500", "height=500") I
initial {
stop("*** cannot open window")
if I&window then
T_stack := []
WOpen("width=500", Iheight=500") I
T_x := WAttrib("width") / 2 + 0.5
stop(,'*** cannot open window")
T_y := WAttrib("height") / 2 + 0.5
T_stack := []
T_deg := -90.0
T_x := WAttrib(lwidth") 12 + 0.5
}
T_y := WAttrib(lheight") I 2 + 0.5
return T_deg := -90.0
}
end
return
The initialization code is enclosed in an initial clause to ensure that it is only
executed once. If &window is null, the window has not yet been opened and end
TlnitO must do 50. Note also the use of "half-pixel" values to reduce problems
# TResetO -- clear screen and stack, go to center, head -90 degrees
from floating-point round-off; see Avoiding Round-Off Problems in the Tips,
Techniques, and Examples section of Chapter 4. procedure TResetO
The user need not call TlnitO before using turtle graphics; in fact, the initial TlnitO
procedure is not even documented as part of the turtle graphics system. Instead, EraseAreaO
every other turtle graphics procedure has a call of TlnitO in an initial clause (not T_stack := []
shown in the procedure given above). For example, the complete procedure for T_x := WAttrib(lwidth") 12 + 0.5
TGoto(x, y) is: T_y := WAttrib(lheight") I 2 + 0.5
procedure TGoto(x, y) T_deg := -90.0
initial TlnitO return
T_x:= x end
T_y:= y # TDraw(n) -- move forward n units while drawing a line
return
procedure TDraw(n)
end local rad, x, y
The complete set of turtle graphics procedures is given below for initial TlnitO
reference. As mentioned earlier, Icon's turtle graphics system includes proce- rad := dtor(T_deg)
dures and functionality not described in this chapter. See the Icon program x := T_x + n * cos(rad)
library for all the details. y := T_y + n * sin(rad)
DrawLine(T_x, T_y, x, y)
T_x:= x
114 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 115
# THeadingO -- return current heading using turtle graphics. The arguments are the same as those given in Chapter 4,
procedure THeadingO
but incr is in degrees instead of radians.
initial TlnitO
procedure fstar(x, y, extent, vertices, phases, ratio, incr)
local resid, factors, length, i
return T_deg
end every i := 0 to vertices * (vertices - 1) " (phases - 1) do {
resid := i # residual after division
# TSaveO -- save turtle state factors := 0 # number of factors
procedure TSaveO # divide and count
until (resid % (vertices - 1) -= 0) I (factors >= (phases - 1» do {
initial TlnitO resid /:= (vertices - 1)
push(T_stack, T_deg, T _y, T_x) factors +:= 1
}
return length := extent * ratio" (phases - factors - 1)
end TLeft(incr)
if i = 0 then TGoto(x, y) else TDraw(length)
# TRestoreO -- restore turtle state
}
procedure TRestoreO
return
initial TlnitO
end
T_x := pop(T_stack)
T_y := pop(T_stack) Lindenmayer Systems
T_deg := pop(T_stack)
Lindenmayer systems provide an interesting application in which turtle
return
graphics playa central role. Lindenmayer systems, or L-systems for short, are
end grammatical devices that originally were designed for characterizing the devel-
opment of plants. See Prusinkiewicz and Hanan (1989) and Prusinkiewicz and
Library Resources Lindenmayer (1990). There are several types of L-systems; we'll look at the
simplest - context-free, deterministic L-systems.
The turtle module in the library includes additional capabilities beyond A context-free, deterministic L-system consists of a string of characters,
those presented in this chapter. The library version supports multiple windows called the axiom, and replacement rules, whereby individual characters are
and adds procedures for drawing circles, rectangles, and polygons. replaced by strings of characters. The axiom is rewritten by performing the
replacements for all characters in it to produce another string. This process is
Tips, Techniques, and Examples repeated on the new string, and so on, for some specified number of "genera-
tions". (The axiom is the zeroth-generation string.) Depending on the axiom and
the replacement rules, the sequence of strings may characterize the stages in the
Fractal Stars growth of a simple plant - or a variety of other objects, including some fractals.
If a figure is composed of a sequence of lines drawn between successive Here's a simple L-system:
points, using turtle graphics may be simpler than using DrawLineO repeatedly. F axiom
Fractal stars, which are described in the Tips, Techniques, and Examples F -- F[+F]F[-F]F replacement rule
section of Chapter 4, provide an example. Here's how such figures can be drawn
118 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 119
In rewriting, any character for which there is no replacement rule is left The specified length of line segments determines the scale of the figure,
unchanged. For the L-system given above, the successive strings are: while the specified angular change is a property of the L-system and plays a
F major role in how the resulting figure looks. For the Plant L-system, an angle of
F[+F]F[-F]F
22.5° produces the result shown in Figure 5.4 at generation 5.
F[+F]F[-F]F[+F[+F]F[-F]F]F[+F]F[-F]F[-F[+F]F[-F]F]F[+F]F[-F]F
A Plant
The strings become very long with successive rewritings. The next one for the L- Compare this drawing to the bush
system above would take several lines to show. shown in Figure 5.3, which was pro-
duced by simple rules with an element
Replacements are made for every instance of every character on each of randomness. Does the plant here
rewriting. Thus, the L-system seem more realistic to you than the
x axiom bush?
X F-[[X]+X]+F[+FX]-X replacement rules
FF
produces
X
F-[[X]+X]+F[+FX]-X
FF-[[F-[[X]+X]+F[+FX]-X]+F-[[X]+X]+F[+FX]-X]+FF[+FFF-[[X]+X]+
F[+FX]-X]-F-[[X]+X]+F[+FX]-X Figure 5.4
where the last string is continued on a second line because of its length. We'll use
this L-system in examples that follow and call it the Plant L-system. The interpretation of the L-system characters as Icon turtle-graphics
What do these strings mean? In one sense, they don't mean anything; they procedure calls is obvious:
can be considered just as strings produced by a formal rewriting system. But in F TDraw(n)
another sense, such strings can be interpreted as successive stages in the f TSkip(n)
development of an (artificial) plant or other object. The interpretation that turns + TRight(d)
the strings of otherwise meaningless characters into drawings of objects uses
TLeft(d)
turtle graphics. In this interpretation, some characters correspond to turtle
graphics commands. These characters are: TSaveO
TRestoreO
F move forward a specified amount, drawing a line
f as F, but without drawing a line
In fact, only these six procedures are needed to interpret context-free, determin-
istic L-systems.
+ tum right by a specified amount
tum left by a specified amount Implementing a program to draw figures for an L-system is relatively
save the current state easy. Such a program should
restore the most recently saved state 1. Specify the L-system.
Thus, F and f draw, while + and - change the direction. The role of the 2. Rewrite the axiom for the desired number of generations.
characters [and] is to save the current state in order to draw a subfigure and then
3. Interpret the resulting string using turtle graphics.
restore the previous state to continue drawing as before.
Here's how it might be done for the Plant L-System:
120 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 121
if laxiom then stop(,'*** no axiom specified") That's all there is to it - the former rewriting code is not needed at all,
if langle then stop("*** no angle specified") and no rewritten string is ever formed. The procedure is called in the interpre-
tation code for each character in the axiom:
The rewriting and interpretation code is the same as before.
every c := lindgen(!axiom, rewrite, gener) do {
An L-system program might provide other features, such as a way to
case c of {
specify the initial point at which drawing begins. For example, the Plant L-
"F": TDraw(length)
system "grows" up. This was taken into account in the code that produced
"f": TSkip(length)
Figure 5.4.
"+": TRight(angle)
Although the approach to interpreting L-systems shown above is cor- "-": TLeft(angle)
rect, there are practical problems with it. For most interesting L-systems, the "[": TSaveO
strings that result from successive rewritings become very long. For the Plant L- "]": TRestoreO
system, the 10th-generation string is over 6 million characters long! Not only }
may such strings exceed the amount of memory available, they take time to }
produce and nothing is drawn until the final string is available. This delay may
be frustrating, and it may give the impression that the program is "hung". We've presented the program for interpreting L-systems in bits and
pieces; here's the whole program for reference:
If you think about the rewriting process a bit, you'll realize that the first
character of the axiom can be rewritten for the specified number of generations link turtle
before going on to the second character. Of course, in the process, the first
procedure mainO
character may produce many characters, but these simply can be "put in front"
local rewrite, line, keyword, value, c, currenCstring, new_string
of the second character of the axiom, and so on. In fact, it's not necessary to
local axiom, angle, length, gener
perform any concatenation; it's just a matter of generating the characters to be
interpreted in the right order. rewrite := tableO
The word "generate" is the key. Here's a procedure to generate the # Read in the grammar
characters as needed: while line := readO do {
procedure lindgen(c, rewrite, gener) line? {
local s if c := tab(find("->"» then {
move(2)
if gener = 0 then return c
rewrite[c] := tab(O)
else if s := \rewrite[c] then suspend lindgen(!s, rewrite, gener -1)
}
else return c
else if keyword := tab(find(":"» then {
end move(1)
value := tab(O)
The procedure lindgenO may appear mysterious at first. It's an instance
case keyword of {
of a very powerful programming technique - recursive generation. It's worth
"axiom": axiom:= value
taking the trouble to understand the procedure, perhaps turning on Icon's
"angle": angle:= real(value) I stop("*** invalid line: ", line)
procedure tracing facility to see in detail what's happening.
"length": length:= integer(value) I stop(,'*** invalid line: ", line)
The current character, rewriting table, and remaining number of genera- "gener": gener:= integer(value) I stop(,'*** invalid line: ", line)
tions are arguments. If there are no more generations, the character is returned. default: stop("*** erroneous keyword: ", line)
If there is a replacement for the character in rewrite,lindgenO is called recursively } .
for every character in that replacement (!s), but with one less generation. On the }
other hand, if there is no replacement, the character itself is returned. else stop("*** invalid line: ", line)
124 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 125
} A Penrose Tiling
} The L-system used to produce this fig-
# Check values ure is:
angle:36
/Iength := 5 length:35
/gener:= 3 axiom:[X)++[X)++[X)++[X)++[X)
w->YF++ZF----XF[-YF----WF)++
if /axiom then stop("*** no axiom specified") x->+YF--ZF[---WF--XF]+
if /angle then stop(,'*** no angle specified") Y->-WF++XF[+++YF++ZF)-
Z->--YF++++WF[+ZF++++XF)--XF
every c := Iindgen(!axiom, rewrite, gener) do { # interpret string F->
case c of {
"F": TDraw(length)
"f": TSkip(length)
"+": TRight(angle)
"_". TLeft(angle)
"[": TSaveO Figure 5.5
"]": TRestoreO
} The Icon program library's linden program extends the version pre-
} sented here with command-line options for controlling scaling and other aspects
WDoneO # wait for user to dismiss window of the display.
end
procedure lindgen(c, rewrite, gener)
local s
if gener =0 then return c
else if s := \rewrite[c] then suspend Iindgen(!s, rewrite, gener - 1)
else return c
end
We mentioned earlier that L-systems can be used to produce various
kinds of drawings. Figure 5.5 shows a Penrose tiling (Gardner, 1989).
Chapter 6
Text
When you think of graphics, you're likely to think of drawing and images, not
text. Text is, however, an important aspect of many graphics applications. It is
fundamental to word processing and desktop publishing, and some text ap-
pears in almost all graphics applications.
127
128 Text Chapter 6 Chapter 6 Text 129
WWritesO is used so that the text entered by the user follows "command? II on Fonts
the same line.
Icon lets you select from among the fonts provided by the graphics
system. A font is a set of characters with a particular appearance in a specified
Positioning Text
size.
Icon maintains a position at which text is written. The text position can Fonts are immensely complicated. There are thousands of them, includ-
be specified in terms of rows and columns and is one-based: That is, the character ing fonts for various languages, fonts with mathematical symbols, and fonts
closest to the origin of a window is in row 1 and column 1. This is the default with special marks used by typographers. Aesthetics playa very important part
position for a new window. Rows are counted from top to bottom in a window, in font usage. You don't have to be a font expert, however, to employ fonts in
and columns are counted from left to right. The horizontal text position is useful and attractive ways.
advanced as characters are written, and a newline advances the vertical position
The termfamily is used to distinguish fonts that have a common appear-
and resets the horizontal position to column 1. A return character resets the
ance. In this book, most of the text is in a font from the Palatino family, while
horizontal position without moving vertically. Backspace and delete characters
program material is in a font in the Helvetica family. Characters in Palatino have
have no effect when writing text. A tab character moves the position to the right
serifs - little extensions to the strokes that make up the characters. Serifs
to the next tab stop. Tab stops are at columns 9,17, and so on.
decorate characters and contribute to their legibility. Helvetica fonts, on the
The current text position is reflected in the row and col attributes. The other hand, have no serifs; they are "sans-serif" fonts. The differences in the
same position, measured in pixels, is in the x and y attributes. These attributes appearances of the two fonts allow program material to be easily distinguished
may be set by calling WAttribO or either of the procedures GotoRCO or GotoXYO. from the body of the text.
The procedure GotoRC(r, c) sets the text position to row r and column c. Within a family, different fonts may have different styles, such as bold or
For example, GotoRC(1, 1) sets the location to the upper-left corner of the italic. The term roman is used for an upright, plain style, such as the font used in
window, and text written subsequently starts there. this paragraph. Some styles are mutually exclusive, like roman and italic.
The procedure GotoXY(x, y) can be used to set the text position to a Others, like bold and italic, can occur in combination. Some families have
specific x-y pixel location. Pixel positioning can be useful in placing text more condensed and expanded fonts, which refer to the relative width and closeness
precisely than row-column positioning allows. Note that the arguments in of characters.
GotoRCO and GotoXYO specify horizontal and vertical positions in different Text that is written in a window is, of course, composed of pixels and
orders. limited by screen resolution, which is much less than what is possible on paper.
When a program is waiting for input, a text cursor indicates the position Fonts used in this way are called screen fonts. Screen fonts typically appear
at which the next character will be written. This cursor normally is invisible, but crude when compared to printed material.
it can be made visible by setting the cursor attribute cursor to lion", either when The size of a screen font is measured in pixels and refers to the vertical
a window is opened or by WAttribO: extent of the font. (In typography, font sizes usually are given in points, where
WAttrib("cursor=on") a point is approximately 1/72 of an inch.) The actual height of a font that appears
in a window depends on the screen resolution. In some cases, a font of a
and the cursor can be made invisible by particular family and style may be available in any size requested. Often,
WAttrib("cursor=off") however, only specific sizes are available.
The appearance of the text cursor varies from one graphics system to Fonts can be divided into two general classes: monospaced ones like
another. See Appendix N for more specific information. Courier, in which every character has the same width, and proportionally
spaced fonts like Palatino, in which characters have different widths according
to their visual appearances (an i being narrower than, for example, an 0).
Monospaced fonts are holdovers from typewriters, line printers, and
130 Text Chapter 6 Chapter 6 Text 131
computer terminals, for which the printing technology made fixed spacing Some platforms have different ways of specifying fonts that can be used
necessary. Monospaced fonts have one advantage: the characters line up in in addition to the standard one given here. See Appendix N.
columns, making layout simple.
A font can be specified when a window is opened, as in
Proportionally spaced fonts are more visually attractive and easier to
read than monospaced fonts, and are used for most printed material. Propor- WOpen("font=Helvetica,12")
tionally spaced fonts also typically are more compact than monospaced fonts. If no font is specified, "mono· is used.
When specifying a font in Icon, the font is specified by a comma- The current font can be determined by using FontO, as in
separated string that gives the family name, style characteristics, and size. The
write("The font is ", Font())
family name must come first; other specifications can be in any order. An
example is "Helvetica,bold,12". Some family names contain blanks, as in "New or set by supplying a font name, as in
Century Schoolbook,14". Font specifications are case-insensitive; "new century Font("sans")
schoolbook, 14" specifies the same font as the former example.
Font(s) fails if the specified font is not available.
The only part of a font specification that is required is the family name.
In the absence of style specifications, a normal style for the family is provided by The following procedure shows how fonts can be used to produce
default. If the size is not specified, the result depends on the graphics system. See whimsical output, much like a ransom note made out of cut-out letters. It uses
AppendixN. a different font to display each character in its argument s. An example of the
output is shown in Figure 6.2.
The fonts that are available depend on the graphics system used and in
many cases on the specific platform. A workstation is likely to have hundreds of procedure ransom(s)
fonts, while a personal computer may have only a few. local c
static famlist, attlist
To aid the construction of applications that are portable among different
platforms, four standard family names are provided: initial {
attlist := [ "", "", "bold", "italic"]
mono monospaced, sans-serif famlist:= [
typewriter monospaced, serif "AvantGarde", "Bookman", "Charter", "Courier", "Gill Sans",
sans proportionally spaced, sans-serif "Helvetica", "Lucida Bright", "Lucida Sans",
serif proportionally spaced, serif "New Century Schoolbook", "Palatino", "Rockwell", "Times"]
The actual fonts used for these depend on the platform and may differ }
somewhat in appearance from platform to platform. In the event that there is no every c := !s do {
font available with the specified characteristics, the result depends on the Font(?famlist 11",24," II ?attlist)
graphics system. See Appendix N. WWrites(c)
}
Examples of Screen Fonts
abcdefghijklmnopqrstuvwxyz The first four lines are in are mono,
return
abcdefghijklrnnopqrstuvwxyz typewriter, sans, and serif for a typical end
abcdefghijklmnopqrstuvwxyz platform. The fifth line is in a font from
Notice that normal (roman) appearance is chosen for one-half of the characters
the Symbol family. The last line shows
abcdefghijklmnopqrstuvwxyz some special characters from the Zapf on the average.
o£<I>'Yll tcpKA. VQlt8pO'tUID Dingbat family.
Figure 6.1
132 Text Chapter 6 Chapter 6 Text 133
Mixed Fonts The height of a font is no guarantee of how tall individual characters in
'Twasbrillig, and the slithy toveS Try modifying the code that it are. For example, a Tin Palatino is considerably taller than a T of the same size
produced this output to in Zapf Chancery ('1"').
Did gyre and gimble in the wabe: vary the type size as well as
Leading (which rhymes with heading) is the distance between the base
font and style.
lines of text written on successive lines. (The term leading comes from the use of
All mimsy were the borogoves,
thin strips of lead to separate lines of type.) The leading associated with a font
And the mOrne raths Qutgrabe. normally is the same as the font height (the line spacing having been considered
in the font design).
Figure 6.2
The various characteristics of a font are available in attributes that are set
when the font is selected:
fheight height of the font
Font Characteristics fwidth width of characters in the font
ascent extent of the font above the base line
For many purposes, it's possible to use fonts without worrying about
descent extent of the font below the base line
details. However, additional characteristics of fonts may be useful. Figure 6.3
leading distance between base lines
shows the font-dependent attributes that are associated with characters.
All values are in pixels. The first four attributes are properties of the font and
Font Characteristics cannot be changed. Leading is set to the font height when a font is selected, but
it can be changed.
Notice that the character is
ascent enclosed in a bounding rect- In the case of a proportionally spaced font, fwidth is the width of the
base line angle that includes white widest character, which normally is M or W. Columns for proportionally spaced
height space to separate the pixels fonts are based on fwidth, although, of course, characters in proportionally
of the character from the spaced fonts usually do not line up in columns.
pixels of other characters.
The leading can be changed to adjust the space between lines. For
descent
example,
WAttrib(lleading=" II (2 * WAttrib(lfheight")))
Figure 6.3 produces "double spacing".
The base line is the line on which a character"sits" - the y coordinate of Text Width
the current text position. The ascender portion is above the base line, while the
descender portion is below. In most fonts, only a few characters, such as g, p, and For a monospaced font, the width of a string when written is just the
y, have descenders. The amount of space and where it is varies from font to font. character width multiplied by the number of characters in the string. For a
Many fonts have all the horizontal space between characters at the right, so that proportionally spaced font, however, the width of a string is more complicated.
a character written in column 1 touches the left edge of the window, which is The procedure TextWidth(s) returns the width (in pixels) of the string s
visually unattractive. in the current font. For example, to write a string centered between x1 and x2 on
base line y, all that's needed is
GotoXY(x1 + (x2 - x1 - TextWidth(s)) 12, y)
WWrites(s)
134 Text Chapter 6 Chapter 6 Text 135
Of course, a check should be provided to assure the positioning specifications Another reason for drawing a string rather than writing it is to take
can be met. advantage of drawing attributes, and in particular to be able to erase text. If the
drawop attribute is "reverse", a string drawn a second time at the same position
Drawing Strings erases the first one. For example
WAttrib("drawop=reverse")
In addition to writing text to a window, you also can draw strings using
DrawString(x, y, s)
°
until *Pending > do {
DrawString(10, 10, "Wake up!")
which draws the string s starting at the specified location without changing the WDelay(500)
text cursor position. Multiple strings can be drawn by supplying additional sets DrawString(10, 10, "Wake up!")
of x, y, s arguments. Characters such as "\n" are not interpreted as positioning WDelay(200)
actions but instead select the corresponding characters, if any, of the current }
font. flashes "Wake up!" in the upper-left corner of the window until the user re-
DrawStringO draws only the foreground pixels of the characters, not the sponds.
background color that normally fills the "space" around text when it is written
using WWriteO and WWritesO. Otherwise, strings that are drawn look the same Library Resources
as strings that are written.
Since DrawStringO includes positioning arguments, it is useful for plac- The fontpick program lets you type in a font specification and see all the
ing text at specific locations. For example, to draw a string centered both characters of the resulting font.
horizontally and vertically at x and y, the following can be used:
x1 := x - TextWidth(s) /2 Tips, Techniques, and Examples
y1 := y + (WAttrib("ascent") - WAttrib("descent")) /2 + 1
DrawString(x1, y1, s) Text Justification
Another example of the use of DrawStringO is illustrated by the map of
Most word processors perform text justification - the addition of extra
the state of Arizona shown in Figure 6.4.
space to square up the left and right sides of typeset paragraphs. Figure 6.5
shows unjustified and justified versions of the same text. This section presents
A Map the simple program that produces the justified version.
In constructing an image like this, the
labels need to be placed with some care
to produce an attractive and readable
result.
PtJiX
uma
Figure 6.4
136 Text Chapter 6 Chapter 6 Text 137
139
140 Color Chapter 7 Chapter 7 Color 141
pale
,,-=
light
--=1
medium
dark
deep
I medium I
---
_ 1_--==:.2..J
where choices enclosed in brackets are optional and hue can be one of
black orange
gray or grey yellow - 1"...-----.
white green
bluish cyan
pink cyan
violet blue
Color Names Figure 7.1
brown purple
red magenta A tool like colrbook can help you quickly get the results you want and
avoid the pitfall of using only garish, primary colors.
A single hyphen or space separates each word from its neighbor.
Conventional English spelling is used. When adding ish to a hue ending in e, the If a color name does not conform to the naming system above, the name
e is dropped. For example, purple becomes purplish. The ish form of red is is passed to the underlying graphics system, which may recognize other names.
reddish. Some examples are See Appendix N for information about color names for various graphics sys-
"pale-blue" tems.
"light greenish-blue" Color names can specify only a few of the millions of possible colors.
"deep purplish blue" Despite this limitation, color names are easy to use. If simple colors are all you
"vivid orange" need, names do nicely.
When two hues are supplied and the first hue has no ish suffix, the
resulting hue is halfway between the two named hues. When a hue with an ish Numerical Specifications
suffix precedes a hue, the resulting hue is three-fourths of the way from the ish
hue to the main hue. The default lightness is medium and the default saturation Numerical specifications are given in terms of the brightness of the red,
is vivid. green, and blue (RGB) light that is used to produce color on most monitors. Red,
green, and blue in this context are primary aydditive colors. The intensities of the
Mixing radically different hues such as yellow and purple usually does components determine the color. At zero intensity for all components, the color
not produce the expected result. It's also worth noting that the human percep- produced is black - at least in theory; most monitor screens don't appear to be
tion of color varies widely, as do the actual colors produced by these names on completely black because of light reflected off the screen. At maximum intensity
different monitors. The program colrbook allows you to see what different color for all components, the color produced is white - again, in theory; the screens
names produce. See Figure 7.1 or Plate 7.1. of most monitors appear to be light graywhen fully illuminated. And, in general,
equal intensities of all components produce a shade of gray. (Black, white, and
gray aren't really colors in the technical sense, but it's useful to treat them as if
they are.)
142 Color Chapter 7 Chapter 7 Color 143
Unequal intensities of the primaries produce other colors. For example, Trying a Color
red and blue in the absence of green produce magenta, while red and green With trycolor, you can type a color
produce yellow, and blue and green produce cyan. All other colors are produced specification (from standard input, not
by combinations of the primaries in various intensities. You may be surprised to in the window) in any of the forms
learn thatthere are colors that can't be composed in this manner, but this isn't an described earlier. If you specify a color
important problem in practice. name that is not available or make a
light blue-green syntax error in a numerical specifica-
The advantage of the RGB color-specification system is that it corre-
21845,65535,65535 tion, you'll be told about it. The win-
sponds directly to the hardware that produces the color. The RGB system has #55FFFF dow also shows the hexadecimal and
some disadvantages, however. One disadvantage is that most persons learn decimal values corresponding to a
colors with a subtractive model in which a combination of pigments produces named color.
a darker color, not a lighter one. Persons who are used to thinking in terms of
subtractive colors usually are surprised to learn that the additive primaries red
and green produce yellow. Another problem with the RGB system is that it isn't
particularly intuitive. Unless you have experience with the RGB system, it may
not be obvious to you how to get an orange color by combining red, green, and Figure 7.2
blue light. And how would you get teal blue?
The intensities of red, green, and blue can be specified in several ways. The program colrpick presents an interactive method for selecting colors
Strings of the form "#rgb", "#rrggbb", "#rrrgggbbb", and "#rrrrggggbbbb" specify either according to RGB values or hue, saturation, and value. (Value is the
intensities in which r, g, and bare upper- or lowercase hexadecimal digits. The traditional name for lightness in the HSV color model.) Hue is measured in
more digits used, the more precisely a color can be specified. The specification degrees around a circle; red is at 0°, green is at 120°, and blue is at 240°. Saturation
"#ddd" might suffice for a light gray, but to get a particular pink, you might need and value are measured on a scale of 0 to 100. See Figure 7.3 or Plate 7.2.
something like "#FFCOCB".
Interactive Color Selection
Intensities also can be specified by three comma-separated decimal select color:
values in the range from 0 to 65535. For example, "32000,0,0" specifies a dark red As you drag a slider, the color in the square
(less than half the maximum intensity for red, and no other primary).
As these ranges suggest, Icon uses 16 bits of information for color
intensities. Neither the human visual system nor color monitors approach this
I I. changes accordingly. The sliders and values
are linked so that, for example, changing the
hue changes the RGB values.
Fg(Hred") greater than zero to better match a particular monitor or to produce special
effects. Changing gamma does not alter anything that has already been drawn,
sets the foreground color to red. but it affects all subsequent operations, including the interpretation of the
The foreground color is used for drawing and text. The background color current foreground and background color.
is used by EraseAreaO and for filling behind text that is written (but not text
drawn by DrawString()). PortabiIity Considerations
The attribute reverse can be set to "on" to swap the foreground and
background colors. Thus, after setting the colors as above, Icon's color names and RGB specifications are portable among different
H
graphics systems, although the appearance of a color may vary from platform to
WAttrib("reverse=on )
platform because of hardware differences. Platform-specific color names gener-
the foreground color is black and the background color is white. The colors can ally are not portable, however. The use of nonportable names may cause a
be restored by program to fail or produce unexpected results.
WAttrib(" reverse=off") The procedure ColorValueO, described earlier, is useful for converting
a system-specific color name to a form that can be used on any platform.
Color Correction
Color Maps
In Icon, intensity values range uniformly from darkest to lightest. At the
midpoint, "32768,32768,32768" or "#808080" specifies a medium gray. Com- If you use more than a few different colors for identifying objects and
puter monitors don't really work this way, though. A 50% hardware intensity attracting attention to an important situation, you'll run into one of the most
produces a dark color; around 75% is needed to produce "medium" gray. Many troublesome aspects of dealing with colors.
graphics systems take care of this automatically, adjusting the programmer's Most modem color monitors are capable of displaying a very large
values to provide the expected appearance. Others, notably the X Window number of different colors. On the other hand, the number of different colors that
System, control the hardware using uncorrected values. This leaves it to Icon to can be displayed at anyone time may be very limited. This limit is determined
fix things up. by the number of planes in the hardware used to drive the monitor. One plane is
Precise color correction involves complicated calculations and measure- provided for each bit associated with a pixel on the screen. Machines with one
ments of the particular monitor involved; these measurements change as a plane (one bit per pixel) support only two colors - a bi-Ievel, black-and-white
monitor ages, and even as it warms up. Nevertheless, a remarkably good display. On the other hand, well-equipped machines support thousands (15 or
approximation can be produced using a simple process called gamma correction. 16 planes) or millions (24 planes or more) of simultaneous colors.
This is what Icon uses. All too common is the intermediate case: many color and grayscale
In gamma correction, the apparent brightness Bof a color is related to its systems have 8 planes, allowing 28 =256 different colors or shades of gray to be
hardware intensity I by the formula B = ['Y where B and I range from 0.0 to 1.0 displayed at one time. The attribute depth gives the number of planes supported
and y (the Greek letter gamma) is a parameter characterizing the monitor. For by the graphics hardware. For example, if WAttrib("depth") returns I, the display
most monitors, y is between 2 and 3. Gamma correction is applied indepen- is bi-Ievel. If it returns 8, then 256 different colors or shades of gray are supported.
dently to each of the red, green, and blue color components. Mid-range colors Because of the limited ability of the human visual system to distinguish
and grays are most affected; gamma correction has no effect on black, white, or colors, a number such as 256 is not as limiting as it might seem. Realistic pictures
fully saturated primary colors. can be composed from 256 different colors if there is a large selection of colors
The gamma attribute controls Icon's color correction. A value of 1.0 is the to choose from. The real problem comes from the fact that the different available
default on systems that need no conversion. On others, a larger default value colors usually are shared by all the applications that use the screen. This includes
effects a translation from Icon's linear color space to hardware-based values colors that the graphics system may use for decoration, as well as colors
needed by the graphics system. The gamma attribute can be set to any value belonging to other applications that are in the background when the program is
running.
146 Color Chapter 7 Chapter 7 Color 147
On almost a1l4-plane and 8-plane systems, the colors that appear on the with complex objects; on the other hand, the colrpick program uses a mutable
screen are registered in a color map that typically is shared by all applications. color just to change its central square as the sliders are moved. Mutable colors
Different applications that use the same color share an entry in the map. For these can be used to emphasize or de-emphasize something, to make an object blink
systems, the limitations on the number of different colors can become a problem or flash, or even to produce animation (as shown later).
for applications that display images with many colors, especially if the colors Suppose you have a program for drawing figures in a window. Drawing
that are used change. The procedure FreeColor(51, 52,. ..) frees the specified
is done on top of a light-blue grid to help with vertical and horizontal alignment.
colors, allowing the graphics system to reuse the corresponding entries in the Now suppose you want to view the finished drawing without the grid lines. This
color map. Whether this actually is done depends on the graphics system. If a requires either erasing the grid lines, which is tricky if some of them have been
color is in use when it's freed, the result is unpredictable. Another way to free drawn over, or redrawing the figure in a cleared window.
colors is to close the window or completely erase it by calling Era5eAreaO.
There is an easier way. If the grid is drawn in a mutable color (initially set
In the case of multiple windows, described in Chapter 9, the discussion
to light blue), it can be rendered invisible by setting its color to match the
above applies to all windows. For example, on systems with 8 planes, at most 256 background color. It can even be brought back by changing it to light blue again.
colors or shades of gray are available among all windows. Here's an example of such a usage:
procedure mainO
Mutable Colors
When a color map entry is modified, any corresponding pixels on the
curr := "light blue" # current grid color
screen change instantly to the new color. Sometimes this can be used to
alt := "white" # alternate grid color
deliberate advantage. Many graphics systems allow modification of color map
grid := NewColor(curr) I stop(,'*** cannot allocate mutable color")
entries, and Icon provides this facility in the form of mutable colors.
drawgrid(grid)
The procedure NewColor(5) reserves an entry in the color map and
returns a negative integer n representing this new mutable color. The string 5, if drawgraphO
supplied, specifies the color initially set in the color map entry. NewColorO fails # loop and handle events
if mutable colors are not supported or if the color map is full. repeat {
An integer returned by NewColorO can be used as a color specification. case EventO of {
In particular, Fg(n) makes a mutable color the foreground color for subsequent "q": exitO
drawing. &Ipress: Color(grid, curr :=: alt) # change grid color
}
The procedure Color(n, 5) sets the color map entry for the mutable color
}
n to the color specified by 5. Any pixels that have been drawn in this mutable
color immediately change to color 5. Additional pairs of arguments can be end
supplied to change multiple entries. If only a single argument n is passed, the procedure drawgrid(color) # draw background grid
current color map setting is returned and nothing is changed. local fg, i
The color map entry of a mutable color can be freed only by calling # save present foreground color
fg:= F90
FreeColor(n). As with other uses of FreeColorO, the results are undefined if the # set grid color
Fg(color)
color is still in use.
every i := 22 to 347 by 25 do {
Using Mutable Colors DrawLine(i, 0, i, 370)
DrawLine(O. i. 370, i)
The important aspect of mutable colors is the ability to change the }
appearance of something that already has been drawn. This is especially useful
Chapter 7 Color 149
148 Color Chapter 7
if WAttrib(ldepth") = 1 then ...
Fg(fg) # restore foreground color
might be used. This approach groups gray-scale monitors with color monitors
return - both have depths greater than 1 - since approximating colors with grays
end usually looks better than using halftones.
Every time the user presses the left mouse button, the grid lines are toggled.
Figure 7.4 illustrates this. Printing Color Images
One problem that plagues the use of color is the need to produce printed
copies of such images for use in technical reports, dissertations, journal papers
- and books like this one.
1/ "-If \ Although new technologies have lowered the cost and increased the ease
\ ./ of color printing, it's still impractical in most situations. What usually happens
...... / is that color images are printed in black and white in the hope of capturing at
I least some sense of the distinctions that color provides. Halftoning is used to
, I convert colors to different shades of gray.
If \ II
\ The trouble with this is that radically different colors may appear similar
or even identical when printed. See Figure 7.5. Ways of dealing with this
problem are beyond the scope of this book, but if you expect to print color images
in black and white, you may want to consider color selection in advance and see
what different choices look like when they are printed.
Applications that are designed for color screens can be ugly or even orange
unusable on monochrome monitors unless some attention is paid to portability. purple
On a bi-Ievel monitor, F90 can choose only black or white, and it returns light blue
whichever of these is closer to the requested color. On a gray-scale monitor, it
chooses the closest gray. light green
Choosing black or white is about the best that can be done for drawing light red
lines or writing text, but it doesn't work very well for colors that cover large dark yellow
areas. The procedure Shade(s) addresses this problem. On a color monitor, Figure 7.5
ShadeO acts just like F90. On a bi-Ievel monitor, ShadeO sets the window to use
a halftone pattern that approximates the darkness of the requested color.
(Halftones are patterns of black and white dots that give the illusion of gray.)
The best results usually are obtained by designing an application to
consider at least two types of displays: bi-Ievel and color. A test such as
150 Color Chapter 7 Chapter 7 Color 151
The gpxop module, which is incorporated by link graphics, contains A little bit of shading can make objects appear to be raised above or
several additional procedures not discussed elsewhere. These include: sunken below the plane. An example can be seen in Figure 7.7.
Blend(k1, k2, ...) generate a sequence of colors
A Domino and its Mold
Contrast(k) choose white or black to contrast with k
Rotating the book 1800 turns one into
Procedures for converting RGB colors to and from HSV and HLS color C' C' oJ ;J
the other.
spaces also are provided. c oJ
n ('
oJ oJ
("> ('
oJ oJ
II· i-···· - • I-
111=····=1l1li-
Random Rectangles in
Color
frame are colored accordingly.
A light, unsaturated background color, such as pale gray, works best.
This provides sufficient contrast for button labels and other such things while
. 1..1
:'11 111- .
I.
III- ••••••
1 - .•••
1 - - •.-=.
=1• •
Try experimenting with dif-
ferent selections of hues and
darknesses, or biasing the
darknesses and hues byhav-
allowing highlights to be drawn in white, a sti11lighter color. It is also possible
to use light-gray frame highlights with a white background, or a dithered frame
on a bi-Ievel display, but neither is as convincing.
·-.11.••.... -
- . 1 . ing some appear more than
once in a list.
The library file bevel.icn contains several procedures for drawing bev-
eled objects; it was used to produce Figure 7.7.
• .II••!;-••II:; Figure 7.6 Mutable colors can be used to produce animation. This program displays
152 Color Chapter 7 Chapter 7 Color 153
randomly placed helicopters with spinning rotors. Each rotor is drawn in twelve EraseArea(O, 12, 50, 13) # clear approx background area
different positions using twelve different mutable colors; at any time only one DrawSegment(2, 30, 20, 30, 15, 30, 15, 25) # draw body
of those is visible, and the rest are set to match the background color. The DrawLine(2, 25, 20, 25, 60, 12, 20, 12, 20, 25)
appearance of motion is produced by changing the color map settings to make DrawCircle(14, 18, 14,5 * &pi / 6, &pi / 3) # draw curved windshield
the "visible" color advance from one position to the next.
off := ?O * &pi # random initial blade offset
link random
every cv := 1 to colors do { # draw blades
$define colors 12 # number of colors Fg(mcol[cv])
$define copters 10 # number of helicopters FiIIArc(-10, 0, 50,14, off + cv * slice, slice)
$define nap 20 # spin delay in msec FiIIArc(-10, 0, 50,14, off + cv * slice + &pi, slice)
$define cwidth 50 # helicopter width }
$define cheight 30 # helicopter height Fg("black")
global mcol # list of mutable colors DrawArc(-10, 0, 50, 14) # ellipse around blade tips
randomizeO
every 1 to copters do # place helicopters randomly
launch(?(WAttrib(lwidth") - cWidth), ?(WAttrib("height") - cheight))
# Loop until user signals a halt.
cv := mcol[1]
until WQuitO do {
Color(cv,"white") # turn old blades to white
put(mcol, cv := get(mcol)) # rotate colors
Color(cv, "black") # turn new blades to black
WDelay(nap) # don't spin too fast
}
end
# launch(x, y) - draw helicopter at (x,y)
$define slice (&pi / colors) # blade width
procedure launch(x, y) Animated Helicopters Figure 7.8
local CV, off Try creating as many helicopters as you dare, and verify that the speed
isn't affected.
WAttrib("dx=" II X, "dy=" II y) # adjust coordinate system
Chapter 8
Images
Drawing Images
Drawlmage(x, y, spec) draws an arbitrarily complex figure in a rectan-
gular area by giving a value to each pixel in the area. x and y specify the upper-
left comer of the area. spec is a string of the form "width,palette,data" where
width gives the width of the area to be drawn, palette chooses the set of colors to
be used, and data specifies the pixel values.
Each character of data corresponds to one pixel in the output image.
Pixels are written a row at a time, left to right, top to bottom. The amount of data
determines the height of the area drawn. The area is always rectangular; the
length of the data must be an integral multiple of the width.
The data characters are interpreted in paint-by-number fashion accord-
ing to the selected palette. With the c2 palette, the data characters r, g, b, c, m, y,
k, w, and x draw the colors red, green, blue, cyan, magenta, yellow, black, white,
and gray, respectively. With the g16 palette, the hexadecimal characters 0
through F select sixteen shades of gray. Other palettes provide larger sets of
colors or grays. The available palettes are described in detail in Appendix H, and
the color palettes are illustrated in Plate 8.1. Plates 8.2 through 8.4 contrast the
discrete colors of the c1 and c6 palettes with the full range of colors.
Spaces and commas can be used as punctuation to aid readability. The
characters - and \377 specify transparent pixels that do not overwrite the pixels
on the window when the image is drawn. Punctuation and transparency
characters lose their special meanings in palettes in which they represent colors.
155
156 Images Chapter 8 Chapter 8 Images 157
The following code segment uses the g16 palette to draw the small PaletteChars(palette) returns a string containing the characters that are
spheres shown in Figure 8.1: valid in the given palette. The procedure PaletteGrays(palette) returns only the
sphere := "16,916, FFFFB98788AEFFFF" II characters corresponding to shades of gray, ordered from black to white.
"FFD865554446AFFF FD856886544339FF E8579BA9643323AF" II None of the palette inquiry procedures requires a window, either im-
"A569DECA7433215E 7569CDB86433211A 5579AA9643222108"1I plicit or explicit, but for uniformity they all accept an optional window argu-
"4456776533221007 4444443332210007 4333333222100008"11 ment. Only PaletteKeyO utilizes a window: Platform-dependent color specifica-
"533322221100000A 82222211100oo03D D41111100000019F"1I tions may not be recognized without one.
"FA200000000018EF FFA4000000028EFF FFFD9532248BFFFF"
Bi-Level Images
every x := 20 to 460 by 20 do {
y:= 290
For an image composed of only the foreground and background colors,
every 1 to ?17 do
only one bit is needed to specify the setting of each pixel. A more compact form
Drawlmage(x, y -:= 16, sphere)
of specification is allowed as an alternative in this situation.
}
A bi-Ievel image specification has the form width,#data. The data field is
A Drawn Image a series ofhexadecimal digits specifying the row values from top to bottom. Each
row is specified by width/4 digits, with fractional values rounded up.
Thenur.nberofballsineadh
column in this figure was The digits of each row are interpreted as a base-16 number. Each bit of
selected at random. This this number corresponds to one pixel; a value of 0 selects the background color
kind of graphic also could and a value of 1 selects the foreground color. The least significant bit of this
be used to present a bar number corresponds to the left-most pixel, so the bits of each row are backwards
graph in an eye-catching from the usual representation.
manner.
For example, Drawlmage(x,y,15,#1113151911") draws a 5-by-5letterN.
The hexadecimal string is interpreted in this way:
Programs that construct images need information about color palettes in If the data field is preceded by the character - instead of #, the image is
usable form. Four procedures provide this in various ways: written "transparently": Bit values of 0 preserve existing pixels instead of
writing the background color.
PaletteKey(palette, color) returns a character from the given palette
representing an entry in the palette that is close to the given color.
Patterns
PaletteColor(palette, s) returns the color represented by the single
character s in the given palette, failing if the character is not a member of the In previous chapters, we described several procedures for drawing solid
palette. The color is returned in the same form as produced by ColorValueO. lines and areas. It's also possible to use these procedures to lay down a pattern.
158 Images Chapter 8 Chapter 8 Images 159
Two attributes control pattern drawing: a pattern and a fill style. For example, with the pattern specified above,
A pattern is defined in terms of a small rectangle, or tile. Conceptually, WAttrib(lfillstyle=textured")
the tile is aligned in the upper-left comer of the window and then duplicated as FiIIRectangle(200, 200, 80, 160)
necessary until the window is filled. When the pattern is active, each pixel drawn
produces the result shown in Figure 8.3.
on the window is controlled by the corresponding pixel of the pattern. Because
the pattern is always aligned with respect to the edge of the window, and not the
coordinates of the drawing operation, areas drawn at different times merge A Patterned Rectangle
seamlessly. Patterns can be used with any drawing
There are 16 built-in patterns with string names, as shown in Figure 8.2. procedure. You mighttry your hand at
..
crafting a fish.
••
The built-in patterns
are designed to provide
cornrnonJy used back-
lightgray verylight white verti cal grounds and textures.
.:.:.:.:-:-:-:-:-:-:
••
diagonal horizontal grid trellis
Figure 8.3
Patterns are not limited to the built-in ones. Figure 8.4 shows an example
of another pattern.
A Patterned Necktie
Figure 8.2
The tile used in this necktie is 14,#8CA9":
row 1 2 4 8 hex
A pattern is specified by calling Pattern() with a pattern specification, as
in 1 000. 8
2 DO• • C
Pattern("scales") 3 D.O. A
4 .00. 9
The attribute pattern also can be used to specify the pattern, as in
WAttrib(lpattern=scales")
The fiIIstyle attribute must be set appropriately to use a pattern. With the
default setting of "fillstyle=solid", any pattern is ignored. Setting "fillstyle=masked"
causes drawing to be restricted to only those pixels that correspond to bits in the
pattern. Setting "fillstyle=textured" causes all the usual pixels to be drawn, using
the foreground color where the pattern is set and the background color where Figure 8.4
it is not.
160 Images Chapter 8 Chapter 8 Images 161
Tiles are given by bi-Ievel image specifications, as described in the Image Files
previous section. The specification used in this necktie is 14,#8CA9".
Any rectangular portion of a window can be saved in an image file.
Here is a program fragment that produces the image shown in Figure 8.4.
Conversely, image files can be read into a window.
Notice that the fill style is reset after drawing the interior in order to draw a solid
outline. Icon supports GIF, the CompuServe Graphics Interchange Format
(Murray and vanRyper, 1994). Other image file formats are supported on some
points := [90, 70, 110, 70, 120, 40, 80, 40,
platforms. See Appendix N for more information.
90,70,60,330,100,370,140,330,110,70]
Pattern(14,#8CA9")
GlF files are limited to 256 different colors. There are two GIF formats:
GlF87a and GIF89a. GIF89a supports transparency, in which designated pixels
WAttrib(lfillstyle=textured")
in the image are not displayed, leaving those in the window unchanged. Icon can
FillPolygon ! points
read both GlF formats, but it can write only GIF87a.
WAttrib(lfillstyle=solid")
DrawPolygon ! points An image can be loaded into a window when it's opened by using the
image attribute with a file name as value, as in
The sizes of tiles that can be used for patterns are dependent on the
particular graphics system used. There is no inherent limit. Tile sizes of 4 by 4 WOpen("image=igor.gif")
and 8 by 8 are the most portable. Figure 8.5 shows more examples, each which opens a window using the image file igor.gif. The size of the window is set
constructed using one of these two tile sizes. automatically to the size of the image. The result might be as shown in Figure 8.6.
A Start-Up Image
Igor, at your service
......
........
1'I't1'Pt1'Ptll't
"''''
Image files are available from a wide variety of
sources. In addition to ones available from
............ '"
"''''
"'''' electronic bulletin boards and networks, im-
........
......
"''''
"'''''''
"''''''' age files can be created from printed images
......
Itt""""""
"'''''''
"''''''' using a scanner. A large number of "clip art"
ll'tll'tll'tll't
ll'tll'tlttl'lt "'''''''
WWWI;;'I
Random Colors
Figure 8.7
An mentioned in Chapter 3, an element of randomness often enhances
the visual appeal of graphic designs.
An image can be read into a window after it has been opened using
Readlmage(s, x, y, p), which reads the image file named s into the window. The One way to accomplish this is to create a list of colors and use the random
upper-left corner of the image is placed at x and y, which default to O. Any selection operation, as in
portion of the image that does not fit into the window is discarded. If p is present, colors := ["red", "blue", "green", "orange", "purple", "black"]
the image is displayed using only the colors of the palette p, thus giving the
program some control over color allocation. Fg(?colors)
Readl mageO fails if the image file cannot be opened, if it is not in a valid If many different colors are needed, it is tedious to list them all and it may
format, or if p is not a valid palette name. It normally returns the null value, but be hard to find an appropriate range. In this case, color palettes can be useful.
if one or more of the needed colors cannot be allocated, it returns a count of those
colors (after substituting black or white for them). The library module color contains a procedure RandomColor(p), which
selects a random color from palette p, omitting the extra shades of gray that are
The procedure Writelmage(s, x, y, w, h) writes the specified rectangular used to fill out large color palettes. See Appendix H.
area of the window to the file named s, starting at x and y and extending by w
and h. x and y default to O. If w or h is omitted, the extent is to the edge of the An alternative method, which has less overhead per color needed but
window in that direction. Thus, Writelmage(s) writes the entire contents of the requires some work initially, is to create a color list from a designated palette, as
window to the file named s. The image normally is written in GIF format, but in
certain forms of file names may select different formats on some systems; see colors := []
Appendix N for details. WritelmageO fails if the given bounds exceed the
window, if the width or height is zero, or if the file cannot be written. every put(colors, PaletteColor(p, IPaletteChars(p)))
If the graphics system allows it, the image used for the iconified version All grays can be omitted by using PaletteGraysO:
of a window also can be set by using the attribute iconimage, as in colors := []
WAttrib(liconimage=sleep.gif") grays := PaletteGrays(p)
Gamma correction is applied when images are read in and written out. every c:= !PalleteChars(p) do
Consequently, an image that is read in and then written out without modifica- if not (grays? upto(c)) then put(colors, PaletteColor(p,c))
tion is the same, regardless of the value of the gamma attribute.
164 Images Chapter 8
* Quicky Burger
Fast Food
* Quicky Burger
Fast Food
procedure WOpenO opens a new window every time it is called. It returns a
value of type window that can be used to identify the new window. For example,
*** Escamillo's
Mexican
*** Escamillo's
Mexican
alert := WOpenO
opens a window and assigns it to alert.
If the first argument of a graphic procedure is a window, it operates on
that window instead of the subject window, which is not changed. For example,
Ordinary and Transparent GIFs Figure 8.8
DrawRectangle(alert, 100, 100, 10, 20)
On the left, the rectangular boundary of each small GIF image is readily
apparent. On the right, using transparent GIFs, the edges disappear. draws a small rectangle in the window alert. The subject window can be
referenced explicitly, as in
Transparent GIFs also can be used for other visual effects, such as DrawRectangle(&window, 100, 100, 10,20)
superimposing images.
which draws a small rectangle in the subject window. It is, of course, easier just
to leave the window argument out when referring to the subject window.
In addition to returning a new window value, WOpenO assigns this
value to &window if &window does not already have a window value. Conse-
quently, you can ignore the value returned by WOpenO if all you're interested
165
166 Windows Chapter 9 Chapter 9 Windows 167
in is the subject window. If you want to change the subject window, you can WOpen(lrows=40", "columns=80", "font=mono") I
assign the null value to &window, as in stop("*** cannot open window")
&window := &null The size of the window can be changed after it is opened.
so that a subsequent call of WOpenO will assign a new subject window; or you The initial position of the upper-left corner of a window can be specified
can simply assign the result of WOpenO to &window. in terms of pixel coordinates. The value of the pos attribute is an integer pair of
Exceptwhen more than one window is needed, we'll continue to omit the x-y coordinates measured relative to the upper-left corner of the screen. For
window argument to procedures and just refer to "the window" with the example,
understanding that we're referring to the subject window. WAttrib("pos=100,200")
causes the window to be placed with its upper-left corner 100 pixels from the left
Opening and Closing Windows edge of the screen and 200 pixels from the top of the screen. The attributes posx
and posy can be used to specify the coordinates individually.
WOpenO fails if a window cannot be opened. This may happen, for
example, if too many windows already are open. WOpenO also fails if a specified Once a window is open, its size and position can be changed. For
attribute cannot be set. Such a failure can result from something as simple as a example,
keyboarding mistake. Since undetected failure of WOpenO can have cata- WAttrib(lwidth=600")
strophic consequences, it is important to provide a check, as in
changes the width of the window to 600 pixels.
log := WOpenO I stop(,'*** cannot open log window")
The display attribute is a system-dependent string that identifies the Stacked Windows
particular monitor and keyboard associated with a window. This string is useful
when a computer has multiple displays or when a network connects the displays When several windows are on the screen at the same time, they may
of several computers. The display attribute can be set to select a display when overlap so that one window obscures another. In this sense, the obscured
opening a window, but it cannot be changed thereafter. window is behind the other window. In some cases, a window may be com-
pletely obscured, so that there is no evidence of its existence on the screen.
WCloseO closes the window. Closing the subject window sets &window
to the null value. When a window is closed, it disappears from the screen and its The user can rearrange the windows or change the stacking order using
contents are discarded. A window that is closed cannot be re-opened. When the facilities provided by the graphics system. If a window is completely
program execution terminates, all windows are closed automatically. obscured, it may be necessary to move or resize other windows to get to the
obscured window.
Window Size and Position The program also can change the order of windows using the procedures
RaiseO and LowerO. Raise(win) raises win to the front, so that all other windows
The size of a window can be specified by width and height in terms of are behind it. Conversely, Lower(win) puts win behind all other windows.
pixels. For example,
Under most window managers, Raise(win) makes win the "focus" for
WOpen(ls ize=300,500") I stop("*** cannot open window") input: the window that accepts user input.
opens a window 300 pixels wide and 500 pixels high. The width and height can
be specified separately, as in Graphics Contexts
WOpen(lwidth=300", Iheight=150") I stop("*** cannot open window")
So far we've presented a somewhat superficial view of what a window
The size of a window also can be specified in terms of the number of rows is: a rectangular array of pixels, together with various attributes. A window
and columns for text in the window's font, as in actually consists of a coupling between two other objects: a canvas, which is what
168 Windows Chapter 9 Chapter 9 Windows 169
you see on the screen, and a graphics context, which determines how drawing is CANVAS A Coupling
done on the canvas. Notice that the size is an attribute of the canvas, while the
size=200,100
The attributes associated with the graphics context are: ... foreground color and font are attributes of the graphics context.
There are, of course, many other attributes not shown here.
colors: fg, bg, reverse, drawop, gamma
text: font, fheight, fwidth, ascent, descent, leading
drawing: fillstyle, linestyle, linewidth, pattern
WINDOW
clipping: clipx, clipy, clipw, eli ph
win1
translation: dx, dy
All other attributes are associated with the canvas:
window: label, image, canvas, pos/ posx/ posy
size: resize, size, height, width, rows, columns GRAPHICS CONTEXT
CANVAS CANVAS
CANVAS
A Cloning
size=200,100 size=350,200 The most common use of cloning
size=200,100
... ... couples two or more graphics con-
, texts with a single canvas.
win1 win4
, , ,
GRAPHICS CONTEXT
fg=red
font=mono
...
GRAPHICS CONTEXT
fg=blue
font=sans
...
.. GRAPHICS CONTEXT
fg=blue
font=serif
...
GRAPHICS CONTEXT
fg=red
font=mono
- --
GRAPHICS CONTEXT
fg=blue
font=mono
Figure 9.3
A Cloning Figure 9.2
Although CloneO creates a new graphics context without opening a
Since graphics contexts contain attributes like colors and fonts that window, the only way to create a new canvas is to open a new window.
determine the appearance of drawing and text, different effects can be
produced on the same canvas by using different couplings with the
Coupling and Uncoupling
canvas.
The procedure Couple(win1, win2) is similar to CloneO, except that a
For the couplings shown in Figure 9.2,
new graphics context is not created, but instead the graphics context of win2 is
WWrite(win3, "A selection of choices follows") shared with the new window produced by CoupleO. For example,
every WWrite(win1," ", !choices)
win5 := WOpen(ls ize=320,200", "font=serif")
writes a sans-serif heading in blue followed by a list of items in red and a mono- win6 := Coupte(win5, win4)
spaced font, all on the canvas created when win1 was opened.
produces the situation shown in Figure 9.4.
If the second window argument in Clone() is omitted, the single window
argument supplies both the canvas and the attributes for the new graphics
context, except for any attributes specified in Clone(). If bothwindow arguments
are omitted, the subject window is used.
For example, if
win4 := Clone(win1, "fg=blue")
then the situation is as shown in Figure 9.3.
172 Windows Chapter 9 Chapter 9 Windows 173
second canvas created, the fourth graphics context created, and the label display.
size=200,100 size=320,200
Canvas States
In its normal state, a canvas appears on the screen. There are three other
possible states for a canvas: hidden, iconic, and maximal.
WINDOW WINDOW WINDOW WINDOW
When a canvas is hidden, it does not appear on the screen and does not
win1 win4 win6 win5 accept events, but it otherwise behaves in all respects like a normal, visible
canvas. You can draw to a hidden canvas, write text to it, and so forth, but
nothing appears on the screen. When a hidden canvas is returned to its normal
state, however, any changes made to its canvas become apparent.
GRAPHICS CONTEXT GRAPHICS CONTEXT GRAPHICS CONTEXT
When a canvas is changed to the iconic state ("iconified"), it becomes an
fg=red fg=blue fg=black
icon - a small image that typically has only an identifying label. An iconified
font=mono font=mono font=serif
canvas can be changed just like a hidden one.
The label associated with an icon can be changed using the attribute
Shared Graphics Contexts Figure 9.4 iconlabel, as in
Either Fg(win4, "green") or Fg(win6, "green") now changes the fore- WAttrib("iconlabel=wake Up!")
ground color in the shared graphics context to green, affecting subse-
quent output to both canvases.
The position of the icon can be changed using the attribute iconpos,
which is analogous to pos for a canvas in its normal state. The image displayed
The procedure UncoupleO removes the coupling for the window. If by the icon is set using the iconimage attribute.
there is no coupling of the canvas to another window, the window is closed as A maximal canvas fills the entire screen or as much of it as the graphics
in WCloseO. If there are other couplings to the canvas, however, the window is system allows. On some window systems, the title bar and other decorations are
not closed. removed when a canvas is maximal, allowing the canvas to fill the entire screen.
Changing a canvas to the maximal state usually changes its size and produces
Using Graphics Contexts a resizing event. If a maximal canvas subsequently is changed back to its normal
state, the canvas is restored to the size it had in its normal state, and there is a
The effects of using graphics contexts in the ways described here can be resizing event.
obtained by changing attributes in a single graphics context. Graphics contexts
offer several advantages over changing attributes: (1) a particular set of at- The state of a canvas is set by the attribute canvas, for which the values
tributes can be established and encapsulated in a graphics context, (2) once are normal, hidden, iconic, and maximal. For example,
graphics contexts are established, less code is required to change the effects of WAttrib(lcanvas=hidden")
operations on windows, and (3) there is less likelihood of programming errors
(such as failing to restore the value of an attribute after it has been changed). causes the canvas to become hidden.
The displaywidth and displayheight attributes give the dimensions of the
Information About Windows screen on which the canvas appears (as distinguished from the current size of the
canvas itself). WAttrib(lcanvas=maximal") resizes the canvas to the size given by
The procedure image(win) produces a string showing serial numbers for displaywidth and displayheight.
win's canvas and graphics context, along with the window label. The form of
174 Windows Chapter 9 Chapter 9 Windows 175
Copying Areas of the rectangular area, advancing across each row before going to the next.
Ordinary colors are represented by comma-separated decimal values for the
The procedure CopyArea(win1, win2, x1, y1, w, h, x2, y2) copies the RGB components, while mutable colors are given by the negative integers
rectangular area of the canvas for win1 defined by x1, y1, w, and h to the canvas produced by NewColorO.
for win2 at the offset x2, y2. If the rectangular area exceeds the boundaries of the PixelO obtains the entire contents of the specified rectangle when it is
canvas for win1, the background color of win1 is used for that portion. Any called. Modifying the contents of the rectangle while PixelO is generating values
portion of the copy that falls outside of the canvas for win2 is discarded. The does not affect the the values generated by PixelO.
source and destination windows may be the same, and the areas may overlap.
The coordinates x1, y1, x2, and y2 default to the upper-left corners of Customization
their windows, and wand h default to include the remainder of the canvas for
win1. Consequently, CopyArea(yvin1, win2) copies the entire canvas of win1 to Some graphics systems allow users to customize their programs by
the upper-left corner of win2. . providing sets of default values. The procedure WDefaultO provides access to
If no window arguments are given, the source and destination for these customized values. WDefault(program, option) returns the custom value
copying are the subject window. For example, registered for the option named option for the program named program. It fails
if the option is not registered for the program. If the graphics system does not
CopyArea(10, 20, 100,200,300,310) provide a customization mechanism, WDefaultO always fails.
copies a 100-by-200 rectangle from (10,20) to (300,310) on the subject window. Custom defaults can be used to override program defaults. For example,
If only one window argument is given, it is both the source and the Fg(WDefault(lteacher", "fg") I "blue")
destination. For example,
sets the foreground color to the customized foreground color associated with the
CopyArea(win, 10, 20, 100, 200, 300, 310) program teacher, if there is one, or to blue if there isn't.
copies a 100-by-200 rectangle from win to (300,310) on win.
Copying areas is another way to produce the same figure at several Tips, Techniques, and Examples
places in a window. For example, the following code segment produces the
image shown in Figure 4.18 without drawing the figure several times. Sunset Meltdown
WAttrib("linewidth=3")
It's often possible to produce interesting images by using graphics
DrawCurve(70, 40, 45, 60, 70, 80, 85, 60, procedures in ways that are not obvious.
50,40,15,60,30,80,55,60,30,40)
The following code segment "melts down" the sunset image of Figure
every x := 0 to 300 by 100 do # overwrite original drawing 4.16 by copying randomly selected portions of the window toward the bottom.
every y := 0 to 240 by 60 do # to make the loop simpler See Figure 9.5.
CopyArea(10, 35, 80, 50, x + 10, Y + 35)
ww := integer(WAttrib("width")) # get window size
It's worth noting that copying a portion of a window often is considerably faster wh := integer(WAttrib(lheight"))
than redrawing a figure.
every 1 to 500 do {
w :=?ww # width of shifted area
Reading the Canvas x := ?(ww + 2 * w) - w # horizontal location
y:= ?wh # height
In some applications, you may need to know the colors of pixels on the h:= ?y # starting altitude
canvas. The procedure Pixel(x, y, w, h) generates the pixel colors from the CopyArea(x, y - h, w, h, x, Y - h + 1)
specified rectangular area. Colors are generated starting in the upper-left corner }
176 Windows Chapter 9 Chapter 9 Windows 177
Meltdown return
What happens if the loop is end
continued indefinitely?
Animation by Copying Images
, -.
J-
One way to produce animation is to successively copy images that differ
so slightly that the change is not noticeable. This is the way that animated icons
--- are done.
As an example, consider the pinwheel image shown in Figure 9.6:
A Pinwheel
Figure 9.5
This image has 180° rotational symmetry; it if is rotated by 180°,
Scrolling it appears the same.
The key to getting the appearance of animation is to produce the images Figure 9.8 illustrates the use ofIcon subwindows. In that figure, there are
fast enough. This can be done with images on hidden canvases and CopyAreaO, four subwindows inside the main window. The smallest subwindow is entirely
which is fast because the images are in memory. In fact, it usually is necessary contained within the largest one. Each window is filled with a different pattern.
to introduce delays to avoid changing images so fast that the sense of animation
is lost. How much delay is needed depends on the speed of the platform and the
impression that is desired. Here's a procedure that takes a window, a location in Patterned Subwindows
it for the upper-left comer of the animation, a value for delaying between
The arcs locate the origin of
successive images, and a list of image files from which the animation is to be
each subwindow, and the
produced. name of its pattern is given.
procedure animate(win, x, y, delay, file_list)
local canvas_list, i
canvas_list := list(*file_list)
every i := 1 to *fiIe_list do {
canvas_list[i] := WOpen("canvas=hidden", "image=" II file_list[i]) I
fail Figure 9.8
}
repeat { Each subwindow was drawn using this procedure:
every CopyArea(!canvas_list, win, , , , , x, y) do {
WDelay(delay) procedure patternfill(win, pat)
if WQuit(win) then break break Pattern(win, pat) I fail
} WAttrib(win, "fillstyle=textured")
} FiIIRectangle(win)
every WClose(!canvas_list) WAttrib(win, "fillstyle=solid")
every DrawCircle(win, 0, 0, 10 to 50 by 10)
return WWrite(win, pat)
end return
The procedure animateO fails if an image cannot be opened; this allows end
the application that uses animateO to retain control. The animation continues
until the user enters a q. Other ways of controlling the duration of the animation, Each procedure call passes an explicit window argument instead of using the
such as suspending between copies, might be more appropriate in some situa- subject window &window. The FillRectangleO and DrawCircleO calls depend on
tions. clipping to keep their output within the subwindow.
The main procedure creates subwindows and passes them to patternfillO:
Subwindows
patternfill(&window, "verylight")
Some graphics systems provide subwindows that behave like other w1 := SubWindow(&window, 80, 50,150,80)
windows but reside within the bounds of a parent window. Icon does not have patternfill(w1, "grains")
true subwindows in this sense, but close approximations can be created for w2 := SubWindow(&window, 50,180,150,80)
output purposes. An Icon subwindow has its own coordinate system, clipping patternfill(w2, "waves")
bound, text cursor position, and other graphics context attributes such as font w3 := SubWindow(&window, 300, 30, 150, 200)
and color. It shares canvas attributes with its parent window. patternfill(w3, "gray")
180 Windows Chapter 9 Chapter 9 Windows 181
w4 := SubWindow(w3, 50, 60, 80, 80) of the window that is not occupied by the image probably will be in the specified
patternfill(w4, "trellis") background color. If you do not take this into account, copying that window to
Subwindow w4 was formed from subwindow w3, not directly from &window. another one may produce erroneous results. A window that is smaller than
specified presents more serious problems.
The SubWindowO procedure is part of the graphics library. Here is a
simplified version: Some window managers limit dimensions if the window is opened with
its contents visible, as in "canvas=normal". You therefore may be able to
procedure SubWindow(win, x, y, w, h) circumvent the limits by opening the window with "canvas=hidden". If, how-
win := Clone(win, ever, you then make the window visible, its size may change.
"dx=" II (WAttrib(win, "dx") + x),
"dy=" II (WAttrib(win, "dy") + y) Animation Revisited
)
Clip(win, 0, 0, w, h) A moving object need not be as simple as the bouncing balls of Chapter
GotoRC(win, 1, 1) 4. A complex object can be constructed on a hidden canvas, then repeatedly
drawn on the screen using CopyAreaO. DrawlmageO also can be used to draw
return win an image, and DrawStringO can be used to produce a moving string.
end Animation becomes more difficult with a complex background. Restor-
The incoming window is cloned, and its dx and dy attributes are set; this ing the background as the object moves away is no longer a simple EraseAreaO
translates the origin. To properly handle subwindows within subwindows, it is call. One possibility in this case is to copy the arena of motion to a hidden canvas
necessary to base the new attribute values on the previous ones. With the new before placing the object for the first time. When it's time to move the object, the
coordinate system in effect, clipping bounds are set to delimit the subwindow. area of the background that it obscured can be restored by copying from the
The text cursor is moved to the new origin, and the cloned window is returned. hidden canvas.
A caution: Subwindows created in this manner can be very useful, but The techniques given here produce effective results, but they fall far
they are not the same as separate windows. Canvas attributes such as width and short of the standards required for a Hollywood special-effects production. We
height, and default argument values for procedures such as WritelmageO, still haven't discussed shadows, changes of shape, and so on. Lasseter (1987)
encompass the full window. Actions in the parent window can overwrite the discusses the application of professional animation techniques to computer
subwindow region. Furthermore, subwindow input events are not segregated graphics.
from those of the parent window; they share a common queue.
Canvas Size
Interaction
Events
User actions such as mouse clicks produce events. When an event occurs,
three values are placed in an event queue for the canvas: a value identifying the
event and two integers containing related information. Each event is associated
with a particular window, and each window has its own event queue. Events
remain in event queues, which are Icon lists, until they are processed.
There are three kinds of events: key presses, mouse actions, and resizing
events. Key presses fall into two categories: "standard" keys that are used for
representing text and "special" keys that are used for manipulating the display
or other non-text purposes. Standard key presses are encoded as strings. For
example, pressing the key a puts the string "a" on the event queue. Special key
presses are encoded as integers for which there are defined constants. For
example, Key_Left is the code for the left arrow key. See Appendix Jfor a list of
the defined constants associated with special keys.
Pressing a mouse button generates an event, as does releasing the button.
A mouse"click", then, produces a pair of events. If the mouse is moved while a
button is pressed, one or more drag events are produced. The final drag event
represents the end of the movement; intermediate events also may be produced,
depending on the particular graphics system and the speed of the motion.
Mouse actions are encoded as integers. Keywords with corresponding integer
values are provided:
183
184 Interaction Chapter 10 Chapter 10 Interaction 185
&Ipress left mouse button press Recall that a case statement compares values without converting types,
&Idrag left mouse button drag as does the === operator. Because some events yield an integer and others yield
a string, it's usually unwise to compare event codes using = or ==.
&Irelease left mouse button release
&mpress middle mouse button press When EventO removes an event from an event queue, the other two
&mdrag middle mouse button drag values associated with the event also are removed and the information con-
&mrelease middle mouse button release tained in them is used to set the value of Icon keywords. Two keywords relate
to the x-y location of the mouse cursor at the time the event occurred:
&rpress right mouse button press
&rdrag right mouse button drag &x x coordinate
&rrelease right mouse button release &y y coordinate
A mouse press event is always followed, eventually, by the correspond- For an &resize event, &x and &y produce the width and height of the resized
ing release, although an arbitrary number of other events may intervene. If the window.
mouse is dragged outside the window, events still are generated until the button
is released. For example, this short program allows the user to draw in the window.
Pressing the left mouse button establishes the position at which drawing begins.
When a window is resized as the result of a user action, an &resize event Dragging the mouse with the left button pressed tracks the mouse and draws at
occurs. This allows the program to rearrange its display if necessary. The resize corresponding places in the window. Pressing the right button and dragging the
attribute determines whether the user can resize the window. It is "off" initially mouse with the right button pressed erases pixels in the vicinity. Finally, a q key
but can be set to "on" to enable resizing. If it is "off", a user attempt to resize the press terminates the program. An example of the use of this program is shown
window has no effect and no resize event occurs. The capabilities of the graphics in Figure 10.l.
system determine whether resizing actually can be prevented.
procedure main{)
No event occurs when the program resizes the window or when a local x, y
window is moved by the program or the user.
WOpen(lsize=400,300") I stop(,'*** cannot open window")
Drawing in a Window Modifier status keywords produce the null value if the corresponding
A program like this pro- modifier key was pressed at the time of the event, but they fail otherwise. For
vides an easy way to de- example,
velop your skill at moving
case EventO of {
the mouse precisely. Try
"q": if &meta then exitO else {
writing your signature.
# request confirmation
}
}
exits the program if the meta key was pressed when q was entered, butit requests
Figure 10.1
user confirmation if the meta key was not pressed.
Two other keywords give the row and column (based on the size of the When an event is processed, the keyword &interval also is set. The value
current font) in which the event occurred: of this keyword is the interval, in milliseconds, between the time that the event
occurred and the time of the previous event.
&row text row
The following code segment provides a display of events:
&col text column
repeat {
These keywords allow the use of mouse clicks to determine, for example, the
e:= EventO
location at which text is entered in a window.
WWrites("\n ")
Integer values also can be assigned directly to these keywords, as in WWrites(if &control then "C" else "_")
WWrites(if &shift then "5 " else "_")
&x:= 10
WWrites(if &meta then "m" else "_")
When values are assigned to pixel-coordinate keywords, the values of the WWrites(" ", left(image(e), 6), left("(" II &x 11"," II &y 11")",11),
II " ,
corresponding text-coordinate keywords are changed automatically, and vice right(&interval, 6), " msec.")
versa. Such assignments can be useful in translating between pixel and text }
coordinates. The translation is based on the attributes of the window used by the
The three characters at the beginning of each line indicate the status of the
most recent call of EventO.
modifier keys at the time the event occurred. The actual value of the event is
The values of &x, &y, &row, and &col reflect any coordinate translation shown in the next column, followed by its coordinate position and the time
specified by the value of the dx and dy attributes at the time EventO is called. elapsed since the previous event. An example of the result is shown in Figure
Three keywords are set corresponding to the status of "modifier" keys at 10.2.
the time of the event:
&control control key
&meta meta key
&shift shift key
The labelings of these keys depend on the keyboard used.
In the case of standard characters, the status of the shift and control keys
also is encoded in the event value. For example, if the a key is pressed with the
shift key pressed, the event value is "A".
188 Interaction Chapter 10 Chapter 10 Interaction 189
Displaying Events an event queue creates an artificial event, which is the next one to be processed.
-5- "H" (70,24) o msec.
--- "e" (70,24) 270 msec. The first line shows the result of press- For example,
--- "1 u (70,24) 90 msec. ing the H key. Notice that the shift is push(PendingO, x3, x2, x1)
--- "1 " (70,24) 120 msec.
--- "0" (70,24) 180 msec. encoded in the event value. The fol-
--- (70,24) 150 msec. lowing events correspond to the rest of pushes an event corresponding to x1, x2, and x3 onto the event queue. Similarly,
" "
-5- "w" (70,24) 480 msec. Hello World. The last six lines show put(PendingO, x1, x2, x3)
--- "0" (70,24) 330 msec. mouse events. We'll leave it to you to
--- " r" (70,24) 150 msec. figure out what kind. appends an event corresponding to x1, x2, and x3 to the end of the event queue.
--- "1 " (70,24) 150 msec.
Since a real event can occur at any time, it is important when appending to the
--- I'd" (70,24) 120 msec.
--- " " (33,15) 1471 msec. event queue to add all three values using a single call of putO.
--- -1 (15,5) 1331 msec.
--- -4 (15,5) 86 msec. Direct manipulation of event queues requires considerable care. Exactly
--- -2 (225,202) 2726 msec. three values must be removed from a queue to remove an event. When inserting
--- -5 (225,202) 100 msec. events, not only must three values be provided for each event, but also the
--- -3 (388,368) 1784 msec.
second and third values must correctly encode event information. See Appendix
--- -6 (388,368) 114 msec.
Figure 10.2 K. The procedure EnqueueO handles the details of placing artificial events on the
event queue. See Appendix E.
WReadO and WReads(i) also process events and remove them from the
event queue. Standard key presses are echoed to the window and accumulate to Polling and Blocking
produce the values for these procedure calls. All other events are discarded
when these procedures are waiting for input. WReadO does not return a value There are two basically different ways of dealing with events: polling
until the enter ("carriage return") key is pressed. WReads(i) does not return and blocking.
until there are i characters. Polling consists of periodically checking for an event between times
The echoing of key presses by WReadO and WReadsO to the window when other computation is being performed. A typical polling loop looks like
can be turned off by this:
WAttrib("echo=off") repeat {
while *PendingO > 0 do {
and turned back on by # process events
WAttrib(lecho=on") }
# do other work
The procedure PendingO produces the event queue. If there are no
}
pending events, the list is empty. Thus,
How promptly user actions are processed depends on how much time is
*PendingO > 0
spent doing other work before PendingO is called. For a good user interface, care
succeeds if events are pending but fails otherwise. Note that the value of should be taken so that the user does not experience significant delays.
*PendingO is three times the number of pending events, since there are three
On the other hand, ActiveO and EventO wait until an event occurs, and
values for each event.
similarly, WReadO and WReadsO wait until text is entered. This is called
Since the event queue is an Icon list, it can be manipulated directly. For blocking. Thus, in
example,
while input := WReadO do {
while get(PendingO) # process input
}
removes all events from the event queue. Similarly, pushing three values onto
190 Interaction Chapter 10 Chapter 10 Interaction 191
nothing is done until the user provides a line of input for WReadO. Since all
events except standard key presses are discarded until WReadO returns, other }
user actions, such as presses of mouse buttons, are ignored. In other words, the
user is required to complete text input before anything further is done. Procedures that are invoked as the result of user actions are called
callback procedures, or simply callbacks. This term refers to the fact that the event
Event Loops loop calls back into the program after being itself called by the program.
The use of callbacks is particularly advantageous when the event loop is
Programs that must handle user interaction at any time usually are not written by hand but is constructed by an interface builder that handles the
organized around an event loop like the ones shown earlier in this chapter. In construction of visual interfaces with tools such as buttons, sliders, and menus.
such situations, the event loop is the heart of the program. An event may result The names of callbacks are specified for the interface builder so that it can
in some computation, after which control is returned to the loop to process the construct an event loop that calls the appropriate procedures. Such an interface
next event. Typically only a few types of events are of interest, and all others are builder for Icon is described in Chapter 12.
discarded without any action being taken.
An event loop usually consists of a case expression in which the type of Active Windows
the event results in the selection of the computation to be performed. Applica-
tions that provide functionality in response to user actions often have event If a program has several windows open, it may be necessary to find one
loops with many case selectors, as in for which there is an event pending. The procedure ActiveO returns a window
that has an event pending. If no window has an event pending, ActiveO blocks
repeat case EventO of {
and waits for an event to occur. To find an active window, ActiveO checks each
&Ipress: {
window, starting with a different window on each call to assure that every
# handle left button press
window in which there is an event pending is serviced. ActiveO fails if no
}
windows are open.
&mpress: {
# handle middle button press An example is
}
repeat {
&rpress: {
case ActiveO of {
# handle right button press
calc_win: calc_event(Event(calc_win»
}
texCwin: texcevent(Event(text_win»
&Idrag: {
status_win: status_event(Event(status_win»
# handle left button drag
}
}
}
} Because Active 0 always waits for an event, it is not suitable for a program
that needs to check multiple windows without blocking. Such a program must
In a program designed around user actions, the event loop may become keep track of active windows and call PendingO for each one in turn.
large and unwieldy. In such cases, procedures can be used to handle events,
moving code out of the event loop, as in Synchronization
repeat {
case EventO of { Some graphics systems accumulate output in a buffer before displaying
&Ipress: selectO it on the screen. The output from a call to DrawLineO, for example, may not be
&Idrag: moveO displayed until some time after DrawLineO returns. This is not usually a problem
&Irelease: placeO in practice, because output is flushed to the display when the program is waiting
192 Interaction Chapter 10 Chapter 10 Interaction 193
for text input. The procedure WFlush() can be used to force pending output to Dialogs
be written to the window at other times, such as to display progress during
periods of heavy computation. The procedure WDelay() also flushes window The kinds of interaction we've described so far involve the application
output. responding to the user. Applications often need to notify users of situations that
In client-server graphics systems, such as the X Window System, it is require attention or action. Applications also may need information from users,
possible for the display to develop a backlog of unprocessed output and for the such as the name of an output file. Sometimes the information needed may
program to get far ahead of the display. Again, this is seldom a problem in involve setting values and making choices. Temporary windows, called dialogs,
practice. When explicit synchronization is desired, the procedure WSync() are used to handle these matters.
flushes all output and then waits for an acknowledgment from the server that all The following section describes some simple and frequently used dia-
pending requests have been processed. logs. See Chapter 14 for more extensive coverage of dialogs.
Sometimes it's useful to attract the attention of a user by producing a There often are situations in which a user needs to be alerted to a
sound. The procedure Alert() does this. The sound is produced on the computer condition before a program continues.
with which the window is associated. The procedure Notice(line1, line2, ... ) produces a dialog with the strings
The nature of the sound produced depends on the graphics system. Not line1, line2 ,.... For example,
all graphics systems support sound. Notice("The file you specified does not exist.")
produces the dialog shown in Figure 10.3.
Mouse Pointer
The mouse pointer moves on the screen as the mouse itself is moved. The .•..'" A Notice Dialog
visual representation of the mouse pointer can be changed using the attribute This dialog has only one line of information. If
The file \IOU $lMlC1fied does not exist.
pointer. The pointers that are available depend on the graphics system. Some more arguments are given to NoticeO, each is
graphics systems have a pointer that looks like a small wristwatch. On such a left-adjusted on a separate line.
system,
WAttrib(lpointer=watch") Figure 10.3
might be used to alert the user to situations in which the program is involved in When the user clicks on the Okay button, the dialog is dismissed, it
a lengthy computation and is unable to respond to user events. Appendix N lists disappears, and program execution continues. Typing a return character also
the pointer shapes available on different graphics systems. dismisses the dialog.
Pointer location attributes give the current position of the mouse, even
when no button is down and no events are being generated. The location in File Name Dialogs
window coordinates is given by the pointerx and pointery attributes. The
location in terms of character position is given by the pointerrow and pointercol Opening files and saving data are such common operations that dialog
attributes. procedures are provided for querying for file names.
The pointer location attributes can be set to new values to change the The procedure OpenDialog(caption, filename, length) produces a dialog
pointer's position. Doing this is, however, generally a bad idea from an interface that allows the user to specify the name of a file to be opened. The argument
design standpoint. caption, which defaults to "Open:" if not given, appears at the top of the dialog.
A text-entry field appears below, in which the user can enter the name of the file
194 Interaction Chapter 10 Chapter 10 Interaction 195
to open. The argument filename provides the string used to initialize the text- repeat {
entry field. It often is omitted, defaulting to the empty string, in the common case case OpenDialogO of {
where there is no meaningful default file name. The argument length specifies "Okay": {
the size of the text field and has a default value of 50. For example, if input := open(dialog_value) then {
OpenDialogO currenCfile := dialog_value # save name in global variable
data_list := []
produces the dialog shown in Figure 10.4. while put(data_list, read(input)) # get the data
An Open Dialog c1ose(input)
return data_list
A file name is entered in the long }
Open: trough. Pressing return is equiva- else Notice("Cannot open file.")
I lent to selecting the default button, }
indicated by a sunken outline.
Okay Cancel "Cancel": fail
}
Figure 10.4 }
If the user selects Okay (or types a return character), the specified file is
The user can type in the text-entry field. An "I-beam" text cursor shows
opened and the data in it is read into a list. If the file cannot be opened, however,
the current location in the field where typed text is inserted. This cursor can be
the user is notified via a dialog and the open dialog is presented again. The
positioned in the text by clicking with the mouse pointer at the desired location.
Dragging over the characters in the text field selects them for editing and procedure fails without trying to open a file if the user selects Cancel.
highlights them (reversing the foreground and background colors). Characters The procedure SaveDialog(caption, filename, length) is used to provide
that are typed then replace the selected ones. A backspace character deletes the a dialog for saving data to a file. The argument caption, if omitted, defaults to
character immediately to the left of the text cursor, if there is one. All this sounds "Save:". Providing a file name can eliminate the need for the user to reenter a
complicated, but as in many interactive operations, it becomes natural in name that is already known to the program. An example is
practice, and it is easier to do than it is to describe.
SaveDialog(, currenCfile)
Figure 10.5 shows the dialog after a file name has been entered in the text- which might produce the dialog shown in Figure 10.6.
entry field.
A Save Dialog
An Open Dialog
If the supplied file name is accept-
Until the user selects a button, the SlIve: able, it's only necessary to type re-
Open:
text entered is tentative and can be iftijihWJm turn, which is equivalent to click-
Poi nts.d"l edited as needed. ing on the Yes button.
Yes No C8nCe1
Cancel
Figure 10.6
Figure 10.5
One use of SaveDialogO is to check whether the user wants to save
The procedure OpenDialogO. like all dialog procedures, returns the modified data before quitting an application. Typical code to do this is
string name of the button selected and assigns the string in the text-entry field
repeat {
to the global variable dialog_value. A typical use of OpenDialogO is
case SaveDialog("Save before quitting?", current_file) of {
"Yes": {
196 Interaction Chapter 10 Chapter 10 Interaction 197
if output := open(dialog_value, "w") then { In the case of actions that may have serious consequences, a dialog box
every write(output, !data_list) should be presented for confirmation.
exitO
} Event Values for Control Characters
else Notice("Cannot open file for writing.")
} As described earlier, upper- and lowercase letters produce event values
"No": exitO that are the letters as you'd see them in other context, as in "a" and "A". In the case
"Cancel": fail of uppercase letters, the keyword &shift also is set, although it is not necessary
} for distinguishing uppercase letters.
}
When the control key is depressed, the event value for a letter is the
If the user elects to save the current data, it is written to the specified file corresponding control character. For example, to detect control-C events, the
and program execution is terminated. If the file cannot be opened for writing, following can be used:
however, the user is notified and the process is repeated with a new dialog box. if EventO === "\"e" then... # interrupt processing
If the user selects No, program execution is terminated without saving any data.
If the user selects Cancel, perhaps because of second thoughts about quitting the Tracking Mouse Movement
application, the procedure fails and program execution continues. The pro-
grams in Chapters 15 and 16 show how interaction for quitting an application Unless a button is depressed, no events are generated by mouse move-
can be handled in the context of an entire application. ment. It is still possible, though, to follow the mouse position by polling the
pointerx and pointery attributes.
Library Resources The program that follows illustrates this by drawing a circle and then
tracking the mouse position. When the mouse pointer is inside the circle, the
The gpxop module, which is incorporated by link graphics, includes the pointer changes to a cross.
SweepO procedure; it allows the user to specify a rectangular area by moving the
mouse. The drag module provides Drag(x,y,w,h) for interactively moving a
rectangular object within a window.
Plates 10.1 and 10.2 illustrate two interactive programs from the library.
The img program is a simple editor that builds small images for use with
DrawlmageO. The concen program is a solitaire card game.
In the code below, the name of the standard pointer shape is recorded a fixed number of states that correspond to the significant situations. When an
and the circle is drawn in the center of the window. The main loop repeats until event occurs, the current state may change to another state and an action may be
a q is pressed, and the pointer shape is set every time. taken.
p := WAttrib(lpointer") # standard pointer For the method of selecting a rectangle that is described above, three
x := WAttrib("width") /2 # center of circle states suffice. Initially, the event loop waits for a mouse button press that starts
y := WAttrib(lheight") /2 the selection. For simplicity, we will assume that the left mouse button is used.
r := WAttrib(lheight") /5 # radius In the waiting state, the only event of interest therefore is &Ipress. When an
&Ipress event occurs, the location of the mouse pointer is recorded to establish
DrawCircle(x, y, r) # draw the circle a corner and the program goes to another state in which the opposite corner is
# repeat until "q" entered selected by dragging the mouse, producing &Idrag events. The program remains
until *PendingO > 0 & EventO === "q" do { in this state until the left mouse button is released and an &Irelease event occurs
to establish the final position of the opposite comer. The program then goes into
# get pointer position a state in which an action is to be performed on the selected area. For sake of
px := WAttrib(lpointerx") example, we'll assume only two operations are possible: clearing the area to the
py := WAttrib("pointery") background color or filling it with the foreground color. The keyboard events "e"
# is pointer within the circle? and "f" determine which is done. Figure 10.9 shows the corresponding finite state
if (px - x) A 2 + (py - y) A 2 < r A 2 then machine.
WAttrib(lpointer=cross") # yes
else A Finite State Machine
WAttrib(lpointer=" II p) # no &Idrag
Such a diagram clarifies the
# share the processor update significant situations that
WDelay(10) may occur in an event loop
} and what events are to be
processed. This determines
Selection Rectangles the structure of the event
loop. The actions taken when
A selection rectangle, which is used to specify a rectangular area of a there is a transition from one
window on which an operation is to be performed, provides an example of state to another are provided
interaction between the user and the program. in the code itself.
Different methods can be used to specify a rectangular area of a window. "f" or "e"
Typically, the user starts a rectangle by pressing a mouse button to pick one
comer of the rectangle and then drags the mouse with the button depressed to fill or clear
Figure 10.9
the opposite comer. While the mouse is being dragged, the program draws a
rectangle so that the user can see where it is. The selected area is determined
when the user releases the mouse button. The actions that are taken in response to events require elaboration. For
That sounds simple, but programming an event loop to create a selection example, positions have to be recorded and a rectangle drawn to show the
rectangle requires careful attention to events and proper maintenance of the selection. The finite state machine just serves as a conceptual tool to assist in
image in the window. writing the event loop.
It's often useful to use a finite state machine (Kain, 1972) to model the The event loop might begin as follows:
interaction and to design the logic of the event loop. A finite state machine has
200 Interaction Chapter 10 Chapter 10 Interaction 201
WAttrib("drawop=reverse")
"select": { # select
WAttrib("linestyle=dashed")
case event of {
state := "wait" &Idrag: { # selecting ...
DrawRectangle(xO, yO, x1 - xO, y1 - yO) # erase rectangle
repeat {
x1 := &x # new position
event := EventO
y1 := &y
case state of {
DrawRectangle(xO, yO, x1 - xO, y1 - yO) # draw rectangle
"wait": { # wait for selection
}
case event of {
&Irelease: { # got it!
&Ipress: {
DrawRectangle(xO, yO, x1 - xO, y1 - yO) # erase rectangle
x1 := xO:= &x # initial coordinates
x1 := &x # new position
y1 := yO:= &y
y1 := &y
DrawRectangle(xO, yO, 0, 0) # start the rectangle
if (xO = x1) I (yO = y1) then # no area
state := "select"
state := "wait"
}
else {
}
w:= x1-xO # set up for action
h := y1 - yO
The reverse drawing mode is used so that the selection rectangle can be DrawRectangle(xO, yO, w, h) # draw rectangle
drawn, erased, and redrawn as the user drags the mouse. A dashed line style is state := "act"
used so that the rectangle can be seen on top of different colors. The initial }
waiting state is indicated by the value "wait" for state. In the event loop, the }
section of code to be executed depends on this variable. The variables xO and yO }
hold the location of the comer from which the selection starts, while x1 and y1 }
hold the location of the opposite comer. "act": { # act on area
In the waiting state, an &Ipress event causes the position variables to be case event of {
initialized. An initial rectangle is drawn, even though it has no area, so that the "f": {
current rectangle canbe erased and redrawn with each subsequent &Idrag event. DrawRectangle(xO, yO, w, h) # erase rectangle
Finally, the state is changed to "select", so that the next event will be processed WAttrib("drawop=copy")
by another section of code. The entire event loop looks like this: FillRectangle(xO, yO, w, h)
WAttrib("drawop=reverse")
repeat { state := "wait"
event := EventO }
case state of { lIe": {
"wait": { # wait for selection DrawRectangle(xO, yO, w, h) # erase rectangle
case event of { EraseArea(xO, yO, w, h)
&Ipress: { state := "wait"
x1 := xO := &x # initial coordinates }
y1 := yO:= &y }
DrawRectangle(xO, yO, 0, 0) # start the rectangle }
state := ·select" }
} }
}
}
202 Interaction Chapter 10 Chapter 10 Interaction 203
In the selecting state, if the event is &Idrag, the current rectangle is erased
by redrawing it, the new position of the opposite corner is recorded, a rectangle
is drawn for this new position, and the state remains the same.
If the event is &Irelease, the current rectangle is erased and the final
corner position is recorded. At this point, a situation that cannot be directly
represented in a finite state machine must be handled: The rectangle may have
no area. This can occur if the mouse button is pressed and released without
dragging, or if the initial and final coordinate positions in one direction are the
same. Although the first situation could be handled in the finite state machine
by adding a state between waiting and selecting to discard the selection if there
was no drag event, the other possibility cannot be handled this way. If there is
no area, the state reverts to waiting.
On the other hand, if the rectangle has an area, things are set up for an
action on it. The width and height are recorded so that they won't have to be
computed later in two places, the final rectangle is drawn, and the state changes
to wait for a user action on the selected area.
The character "f" indicates that the area is to be filled in the foreground
color. First, the selection rectangle is erased. Before calling FillRectangleO, the Erasing an Area Figure 10.10
drawing mode is changed to "copy"; otherwise filling will be done in the reverse
mode, possibly with an interesting but unintended effect. After restoring the The image at the left shows the sitUation before selecting an area. The
reverse mode of drawing, the state is changed to waiting for another selection middle image shows a selection rectangle drawn around material to be
rectangle. Clearing is similar but simpler, since the drawing mode need not be deleted. The result of pressing C is shown at the right.
changed and restored.
One problem with the event loop shown above is that there is no way out
Note that in the action mode, the selection rectangle is changed in the of it. One possibility is to allow the user to press q in any state to exit the loop:
code for each event that is handled. It cannot be changed before these events are
repeat {
handled, since that would erase the rectangle for events that may occur in this
event := EventO
state but do not result in an action on the selected area. Similarly, the state cannot
if event === "q" then break
be changed until one of the expected events occurs.
case state of {
Figure 10.10 shows an example of using a selection rectangle. "wait": {
}
}
Suppose that the user drags an object in the window using the mouse. If
redrawing the object takes too long, the screen updates will lag behind the
Chapter 11
mouse movement and the user will become disoriented. If the list returned by
PendingO grows past a certain size, the application can skip drag events, as
indicated in the following loop:
repeat { User Interfaces
# perform background computation
e:= EventO
if e === (&Idrag I &mdrag I &rdrag) &
*PendingO > Limit then next # skip drag event
# process the event
In preceding chapters, we described how a user can convey information to a
program using mouse and keyboard events. Except for the simplest applica-
Dialog Colors
tions, it is more helpful to organize interactionbetween the program and the user
If there is no subject window, dialogs have a black foreground and a light by using interface tools such as buttons, menus, and sliders. Such interface tools
gray background, as illustrated by the examples in this chapter. provide a visual interface between the user and the program.
If there is a subject window, however, dialogs inherit the foreground and Interface tools provide a wide range of functionality in ways that are
background colors of the subject window. This may produce unattractive or convenient, familiar, and easily understood. For example, clicking on a button
illegible dialogs (for example, a black foreground and a white background do on the application window can be used to tell the application to perform some
not produce an attractive dialog). action, pulling down a menu can be used to select among operations, and
dragging on a slider can be used to change a numerical value.
This problem may be avoided by saving the foreground and background
colors of the subject window and setting appropriate ones before calling a dialog The rest of this chapter describes an application with a visual interface
procedure. The saved colors are restored after the dialog is dismissed: and then goes on to describe the available interface tools. Subsequent chapters
explain how to build a visual interface and how it fits into a complete program.
fg := WAttrib("fg")
bg := WAttrib(lbg")
Fg("black") An Example Application
Bg("light gray")
Figure 11.1 illustrates an application that displays a multicolored kalei-
# call dialog procedure doscopic image. The image changes as old circles are erased and new ones are
drawn. Plate 11.1 shows what the application looks like in color.
Fg(fg)
Bg(bg)
205
206 User Interfaces Chapter 11 Chapter 11 User Interfaces 207
s.-cI
slow slow
densfty density
111"
.1"1_ radius
IItn
111"
+ discs +d1scs
rings rings
pushed a second time to tum it off. Both kinds of buttons are highlighted when
they are on.
Buttons are available in a variety of styles, as illustrated in Figure 11.4.
Button Styles
En8ble
o There are four basic button styles. Out-
lines are optional. The button at the
:..J Enablej .J En8ble right is called an X-box button. Unlike
other button styles, it has no text asso-
oJ En8ble En8ble
,oJ
ciated with it.
Figure 11.4
.tn
Figure 11.5 shows the highlighted forms of the various buttons.
+ discs Highlighted Buttons
rings
The nature of highlighting depends on
the style of the button. As you see here,
En8ble
there is an X-box button without an
Buttons are among the most simple and commonly used interface tools.
Pressing a mouse button when the mouse cursor is on a button amounts to
1/pushing" the button. We'll use the term pushing" with the understanding that
1/
it amounts to pressing a mouse button with the mouse cursor positioned on the
button.
Buttons support two kinds of functionality. An ordinary button only has
a momentary effect: It remains on only as long as it's held down, then reverts to
its original state. A toggle button remains on when it is pushed, and it must be
210 User Interfaces Chapter 11 Chapter 11 User Interfaces 211
Radio Buttons A menu item can itself be a menu. Such items are identified by an angle
The example here was chosen to em- bracket at the right. If you select one of these items, its menu appears to the right,
phasize the origin of the term radioU as shown in Figure 11.8.
640 KFI Los Nlgeles button". Radio buttons can, of course,
680 KNBIl san Franci 5CO have any labels such as the names of
Chicago A Submenu
720 WCN colors available in a particular applica-
770 WMIC New York
tion. If you drag off a submenu and select
830 WCCO Mi nneapo1i s
850 KOA Denver another item from the main menu, the
870 WL New Orleans submenu disappears.
1040 WHO Des Iloines
1120 KIlOX St. Louis
1200 WOAI san Antonio
Figure 11.6
Menus
A menu is a button that conceals a list of items. When you push the menu Figure 11.8
button, the list of items is "pulled down" and the item under the mouse cursor
is highlighted. As you drag over the items on the list, the item under the mouse You can then drag onto this submenu and select an item there, as shown
cursor is highlighted, as shown in Figure 11.7. in Figure 11.9.
Text-Entry Fields
Scrollbars
You can select a text-entry field by clicking on it, at which point an "1-
.J
beam" text cursor appears and you can enter or edit text. The I-beam cursor Sliders usually are used for setting val-
shows the current place in the field where typed text is inserted. This cursor can ues, while scrollbars typically are used
be positioned in the text by clicking with the mouse pointer at the desired to select a portion of a larger image for
location. Dragging over characters in the text field selects them for editing and display in a smaller area.
highlights them (reversing the foreground and background colors). Characters
that are typed then replace the selected ones. A backspace character deletes all
the selected characters. If no character is selected, a backspace character deletes
the character immediately to the left of the text cursor, if there is one. All this
sounds complicated, but as in many interactive operations, it becomes natural .J
in practice, and it is easier to do than it is to describe. Figure 11.12
Sliders
Dragging the thumb of a scrollbar or clicking in the trough has the same
effect as with a slider. In addition, clicking on an arrow at the end of a scrollbar
A slider specifies a numerical range visually. Numeric values, which can
moves the button incrementally in the direction indicated.
be integers or real numbers, are associated with the end points of a slider. A
"thumb" marks the current position in the range. The value is changed by
Text Lists
dragging the thumb or by clicking anywhere in the trough to move it to that
point. A text list displays multiple lines of text. If there are more lines than will
Sliders may be vertical or horizontal, as shown in Figure 11.11. fit in the space provided, a scrollbar allows scrolling through the lines.
There are three kinds of text lists: ones that allow a user to scroll but not
select a line, ones that allow the user to select one line, and ones that allow the
user to select multiple lines. These are shown in Figures 11.13 and 11.14.
214 User Interfaces Chapter 11 Chapter 11 User Interfaces 215
Labels
1•• Is there a users' BnqI for Icon?
1electnJl'llc
There is 110 official Icon
uil1ng list,
users' BnqI. The Icon Project uint81ns 1m A label consists of text. Figure 11.16 shows a slider with four labels.
1con-gl"OUllks. ari zona. edu •
..n sent to this address Is forwarded to subscribers. To subscribe (or " Labels
unsubscribe), send a IIIlSS8lIIl to ...
1con-group- request8cs. ari zona. edu Labels can be used to identify tools,
indicate values, and so forth.
SCALE
A Scrollable Text List Figure 11.13
A text list that allows the user to scroll is a good way to provide a large 0.0 I ..u 1.0
amount of infonnation in limited space. drag on the tt..b to dIange the SC81e
Selectable Text
unde11 ....11.1cn- -:1 The groove beside the left-hand list
unde12 118nde12.1en
801re , 801 reo len indicates that an item can be selected. Figure 11.16
offt11er
orbit
palnterc
The double groove beside the right-
pal check hand list indicates that multiple selec-
palette I tions are allowed. No groove appears
Lines
IWMt
pextract if selection is disallowed, as shown in
PlllItotas
Figure 11.13.
Lines can be used to visually delineate areas of an interface. See Figure
I plckt11e
plat 11.17.
PIlI!
Regions
,
-- -, they nonetheless can be very helpful in
I
SCALE
\
, making an interface visually under-
i
A region is a rectangular area that serves to accept events within its standable.
boundary. Figure 11.15 shows three regions. i
!
0.0 ..1..,.----- 1.0
\
,
!
r-I- - - - . . , -
...
D
grooved, raised, and invisible (which
we can't show). Figure 11.17
o Callbacks
When the user presses a button, selects an item from a menu, or activates
some other interface tool, a procedure associated with the vidget is called. Such
Figure 11.15 procedures are called callbacks because user actions on the interface result in
calls back to procedures in the application.
216 User Interfaces Chapter 11 Chapter 11 User Interfaces 217
Callback procedures have the following form: Notice that the list element is the complete text for the item selected.
procedure cb(vidget, value) The callback value for a text-entry field is the text in the field at the time
The first argument identifies the vidget that produced the callback, and the the user presses return with the I-beam cursor in the field. There is no callback
second argument gives a value. The vidget argument is not always needed, but until the user presses return.
it can be used to distinguish among differentvidgets that share the same callback The callback value for a slider or scrollbar is the numerical value in the
procedure. The value often is important, since in many cases it indicates the given range, as determined by the position of the thumb. A slider or scrollbar can
nature of the user action. be configured in two ways: to provide callbacks as the user moves the thumb, or
For a toggle button, the value is null when the toggle is turned off and 1 "filtered" to provide a callback only when the user releases the thumb. Filtering
(nonnu1l) when it is turned on. This makes testing of the state of a toggle easy, is appropriate when only the final value is important, as in
as in procedure density_cb(vidget, value)
procedure pause_cb(vidget, value) density := value # set global variable
if \value then ... # stop display return
else ... # continue display
end
return
Unfiltered callbacks may be needed when the application needs to respond
end while the user moves the thumb, as in scrolling an image.
The callback value for a radio button is the (string) label of the selected The callback value for a text-list vidget depends on the kind of the text
button, as in list. If the vidget allows selection of only a single item, the value is the selected
procedure shape_cb(vidget, value) line. If thevidget allows multiple selections, the value is a list of the selected lines.
There is no callback for a text list that does not allow selection.
case value of {
"discs": # set shape to filled circle The form of callback procedures for regions is somewhat different from
"rings": # set shape to outlined circle the callback procedures for other vidgets. The second argument is the event
} produced by the user, and there are two additional arguments that indicate
where on the application canvas (not the region) the event occurred:
return
procedure cb(vidget, e, x, y)
end
Note in particular that e is not a value associated with the region vidget, as it is
Since menus can have submenus, their callback values are lists whose for other kinds of vidgets; it is the actual event, such as a mouse press or a
first elements are the text for the item selected from the main menu, whose character from the keyboard.
second elements are the text for the item selected from the first submenu, and so
on. If there are no submenus, the callback value for a menu is a one-element list, Labels and lines do not produce callbacks; they provide decoration only.
as illustrated by
procedure file_cb(vidget, value)
The Interaction Model
case value[1] of { In order to design an application with a visual interface, it is necessary
"snapshot @S": # take snapshot to understand how the user and the application communicate. Figure 11.18
"quit @Q": # shut down the application shows this schematically.
}
return
end
218 User Interfaces Chapter 11 Chapter 11 User Interfaces 219
r--
/
/
When choosing between a menu and a set of radio buttons, consider the
/ following:
"\ /
\ /
ouse
[;j • A menu button takes less space on the interface than a set of radio
buttons.
• Using a menu requires the user to pull it down and navigate with the
User Interaction with an Application Figure 11.18 mouse, while selecting a radio button is just a matter of clicking on it.
Most user events in an application with a visual interface come from the • The menu choices are not visible until the menu is pulled down, while
mouse. For example, clicking on an interface button with the mouse radio buttons always are visible.
causes the callback procedure associated with the button to be called. • A radio button maintains an internal state and the last selected radio
button remains visible, while the last selected menu item does not.
Tips, Techniques, and Examples The "real-estate problem" often determines the choice - a large inter-
face cluttered with buttons may be confusing and annoying to users.
Scaling Sliders and Scrollbars .
It is conventional to use menus to select among actions and to use radio
buttons for selecting states. Users generally prefer interfaces that follow conven-
The bounds of a slider or scrollbar are fixed when the program is built and
tion.
cannot be altered during execution. At first glance, this would appear to be a
serious limitation - what if a scrollbar is to be used to control the panning of a
viewport across an image of unknown size?
There is a simple solution, though: The scrollbar is configured to range
from 0.0 to 1.0, and its output is then scaled to the desired range. The scaling is
easily done in the callback procedure. For the image-panning situation, it could
be done this way:
procedure sbaccb(vidget, value)
value := value * image_width
Nonlinear scaling also can be useful. For a slider controlling the size of an
object (its area), the square root of the slider value can be used to scale the object's
width and height. Logarithmic scaling often is best for speed controls.
Chapter 12
In many situations, the screen is shared by several applications, so an We put a menu bar at the top, also because that's conventional. The
application canvas generally should not be larger than is necessary. On the other functionality we had in mind included the ability to save snapshots of the
hand, the application canvas should be large enough to be visually attractive and display. Such operations usually are put in a menu named File. An entry for
allow the user easy access to interface tools. An application that displays an quitting the application also typically is put in a menu named File, although it
image or provides a user work area generally is more attractive and useful with has little to do with files. The point is that experienced users expect it there. In
a relatively large canvas. Achieving a good compromise may be difficult. this application, there are no other menus; many applications would have
Figure 12.1 shows a sketch of the interface we designed for the kaleido- others.
scope program. Allowing the user to stop the display temporarily and to reset it are part
of the application design. We could have put these operations in a menu, but
buttons are easier to use than menus and there is ample space on the canvas to
provide buttons. Furthermore, since pausing the display involves a change of
state, using a button rather than a menu item makes the state visible on the
interface.
Since the speed of the display, the density of circles, and the maximum
[ \i and minimum radii of circles all are numerical quantities, we chose sliders to let
the user adjust these values. An alternative would have been to provide text-
r entry fields in which the user could enter numbers. For an application like the
kaleidoscope, there is little advantage to allowing the user to specify precise
values - entering precise values is more difficult than moving sliders and the
[ Ii user would need to know what the numerical values mean. Sliders deprive the
user of precision, but they allow a more intuitive approach to using the
application. Because of the area available and the need to label the sliders, we
oriented the sliders horizontally.
All that remains is a way for the user to select between discs and rings.
Because there are only two choices and there is space available, we decided to
use radio buttons, which makes the choice visible on the interface. If there had
been more choices for shapes or less available space on the canvas, a menu might
Initial Interface Layout Figure 12.1 have been a more appropriate choice.
It's often worth doing a series of rough sketches with different layouts With this layout in hand, we're ready to build the interface.
before committing to interface construction. Sometimes more precise
drawings done to scale, perhaps using graph paper, can save work
later. A Visual Interface Builder
Our first consideration was the display region. We decided, somewhat The Icon program library contains procedures for creating vidgets,
arbitrarily, to make the region 400-by-400 pixels (the region needs to be square configuring them, and positioning them at specified places on an application
because of the drawing symmetry). This is large enough to provide an attractive canvas. Using procedures to do this, however, is a tedious and often intricate
display but small enough so that the entire canvas would fit within the 640-by- task. Icon provides a visual interface builder, VIB, that automates much of this
480 limit. We put the region at the right side of the canvas because it's conven- process. VIB allows you to create instances of vidgets, place them where you
tional to put user controls at the top and left of visual interfaces. Following want them, configure them, name their callbacks, and try them out, all interac-
common, well-known conventions, in the absence of compelling reasons not to, tively.
makes learning the application easier for users. In the following sections, we'll go through the process of building the
224 BUilding a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 225
visual interface for the kaleidoscope application. We won't attempt to describe Building the Kaleidoscope Interface
all the features of VIB in this chapter. See Appendix M for more information.
It's generally a good idea, before creating any vidgets, to set the desired
The VIS Application size of the application canvas. This can be done by dragging with the left mouse
button on the lower-right comer of the rectangle representing the application
The VIB window for building a new interface is shown in Figure 12.2. canvas. Alternatively, clicking the right mouse button on the lower-right comer
of the canvas area brings up a dialog in which information can be entered. See
FUe
.
Figure 12.3.
r7,':';, . .. .•
....-"
0'
... .. '
.... __... _...-._.-
ril.
kaleldo.k:n
procedure _ : I
window 1.1:
width: S94
height: 392 dl.101 Wiricloii
border around the display region. Figure 12.4 shows the result of editing the
canvas dialog and Figure 12.5 shows the new application canvas.
proc:ecllre _ :
window 1_1: fka1eido
width: lGOO
height: .. d1810g window
dragging it onto the canvas. For a line vidget, this produces a short horizontal
line, as shown in Figure 12.6.
. '"
• •
The end points of the line are highlighted to indicate that the vidget is The x1 coordinate should be set to 0 and the x2 coordinate to 599 to fitthe
"selected". Operations are performed on the currently selected vidget. A vidget width of the application canvas. (If a line is a little too long to fit on the application
is selected when it is created. A vidget that is not selected can be selected by canvas, that doesn't matter, since nothing beyond the edge appears when the
clicking on it with the left mouse button. Only one vidget can be selected at any application is run.) The values of y1 and y2 need to be the same, of course, to
given time. produce a horizontal line. It may be necessary to try out different values or to
drag the line until its appearance on the canvas is acceptable. We chose 25 for the
There are several ways we can adjust the length and position of the line. vertical offset, with the results shown in Figure 12.8.
We can press the left mouse button on the line and drag it to a new position. We
can drag one end point to stretch and pivot the line while the other end remains
anchored. Alternatively we can press the right mouse button to bring up a dialog
that allows us to specify the length and positions of the end points. See Figure
12.7.
230 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 231
x: S21 32 irw1s1ble
v: so height: ZO sunken
• grooved
raised
Okav cancel
The Menu Bar Line Figure 12.8 The Region Dialog Figure 12.9
What we have so far isn't very impressive, but it didn't take long. There are several attributes of a region that need configuring. As with
all vidgets, if we don't get everything right the first time, we can go back
The display region is the next order of business. Figure 12.9 shows a later and make changes.
region vidget and the dialog for configuring it.
We chose to use a dialog to configure the region, since we wanted to
specify a precise size. For approximate sizing and positioning, we can drag on
the corners of a region vidget when it's selected.
In this case the suggested ID is almost what we want, but since there's
only one region, we just deleted the number. There also is a suggested name for
a callback for the region. Since the region is only for the display and there's no
functionality associated with user events in the region, we don't need a callback.
The callback can be eliminated by deleting the text in the field,leaving it empty.
When there is no callback for a vidget, events that occur on it are ignored.
We know the width and height for the region, and we could make a guess
as to where the upper-left corner should be. If we're wrong, we can move the
region later.
The four radio buttons at the right of the region dialog provide alterna-
tives for the visual appearance of the region's border. We decided on "raised".
232 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 233
The edited dialog is shown in Figure 12.10, and the resulting region is shown in - - - - - - -- - - - ---
Figure 12.11. -
. .-
r
"
A
r ,
r . --
;
•
!
<.
1 1 I"
•
.. I
-,
- ..
. .. .."
x: r0-
y: 6S
0k8y I C8nce1
cancel
"
The menu label needs to be changed to File, since that's what appears on
the menu button on the interface. The callback also should be changed to identify
the functionality of the menu. We use the suffix _cb to distinguish callbacks
from other procedures in the application, but this is only a convention.
A newly created menu vidget provides three items. The kaleidoscope
application needs only two; one can be deleted by clicking on the del button
beside it (clicking on an add button between two items adds an item there). This
menu has no submenus, so we can ignore the create submenu buttons. See
Appendix M for instructions on creating submenus.
The edited dialog is shown in Figure 12.13 and the result, after position-
ing the menu vidget, is shown in Figure 12.14.
236 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 237
x: 48 + regular outl1..
y: fi4'l c:heck
toggle
widtfl: r35 circle
height: 20 lCbox d18lllll default
f f
Okay I cancel
pause reset
libel: .-se
,._....
ID'
r -- ;;;=====:;:::::;:;:::;;;;=1
.-se
Olcay cancel
The Edited Button Dialog Figure 12.16 Five Vidgets in Place Figure 12.17
The size of the button adapts automatically to the label it's given, It's usually necessary to adjust the positions of vidgets so that they are
although it can be made larger. aligned and are placed in a visually appealing way. It's worth doing
this; visually misaligned or off-balance layouts annoy users and sug-
We also need a reset button, but we won't go through all the details here; gest that the application is not well done.
the process is similar to that for creating the pause button, except that the reset
button is not a toggle. Figure 12.17 shows the canvas with the two buttons after The four sliders are next. Figure 12.18 shows a newly created slider and
positioning them where we thought they looked best. its dialog box after editing. We've changed the default vertical orientation to
horizontal and set the range from 500 to 0, anticipating that the left end of the
slider will correspond to "slow" and the right end to "fast". We set the filter
toggle because our program doesn't need to react to every motion as the user
drags the slider. With filter set, intermediate events are filtered out, and the
program takes action only when the mouse button is released to fIlet go" of the
slider.
240 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 241
-- - .... -- - .. .. -
..
file Edit select
.::=J _
-. -r A
fife.
reset
ID:
C811beck:
x: ;n
y: 102
length: 1
wtddl: 1S filter
vert1C8l
+ horizontal
Okay c.nce1• I
.
J
'-.,
r e ritit "select
pause reset
The Sliders in Place Figure 12.20 Configuring the Radio Buttons Figure 12.21
The interface is now taking shape; at this point the results should be As for menus, three radio buttons are provided by default. Adding and
satisfying. deleting radio buttons and changing their names is similar to the
process for menus. We've already done this in the figure.
The radio buttons are next. Figure 12.21 shows a newly created set of
radio buttons and the dialog after it has been edited.
Figure 12.22 shows the resulting radio buttons.
244 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 245
r - .----- -- -
,.,
i I II, ,'.
..
Fn e [df t 58 let
IP.. -r:- A
fne'
p-use reset
label:
m: label1
.../1
IC: 46
i
I
y: 79
0k8y C8IICe1
dfscs
rings
- -
The Radio Buttons in Place Figure 12.22 Creating a Label Figure 12.23
Only the labels remain to be done. This is the label that goes over the speed slider, so we need to change its
text accordingly.
We've saved the labels until last for a good reason: We couldn't be sure
the sliders were where we wanted them until the radio buttons were in place. Figure 12.24 shows the new label in place.
Twelve labels are needed and moving them around after creating them is a lot
of work.
Figure 12.23 shows a newly created label and its dialog before editing.
246 Building a Visua,l Interface Chapter 12 Chapter 12 Building a Visual Interface 247
.- ... ....
.. . .
-,,------------
_ ' .,,, .'. -; • _ '. _ 1.£19,I,!.EdC.'U! _,.. ,.; -,- :.. .
Fl1e
-r
File File
5lIBBd
--
slow fast
density
.In
.i n1_ radi us
ain
.In
aaxl_ radius
dtscs
-
dtscs
rings
One of the Labels in Place Figure 12.24 The Completed Interface Figure 12.25
At this point, we know the end is near. Finally, the interface is complete. All the planned vidgets have been
created, and they are at least approximately where we want them. That
We can save work for the remaining labels by copying. Note that several doesn't mean the interface will never change. As the application devel-
of the labels have the same text, so by choosing what to copy, we can reduce the ops, new functionality may require additions or changes to the inter-
amount of work even further. The final result is shown in Figure 12.25. face. With a good foundation, though, future changes will not be as
hard.
The interface as shown in Figure 12.25 looks like it will look when the
application is run. It's possible, however, to see the application "in action"
without leaving VIE. Typing @ P starts up a prototype of the application with
functional vidgets. See Figure 12.26.
248 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 249
reset
speed
slow -fast
.1n
density
- speed
.1n
l'8dtus
- slow
dens1ty
fast
-
-
.1n
--
l'8dtus
.1n ain1_ I'8ltt us
ain
aaxi_ I'8ltt us
ct1scs .1n
• rings
discs
rings
VIB has three menus, as shown in the previous figures. The File menu,
shown in Figure 12.27, provides for creating new interfaces, opening previously
saved ones, saving the current interface, and so on.
250 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 251
fil.
1_101
l 1OZ
11l----..,---il
m. -
1_103
I_lOS
pause 1_106
1_107
sPMd 1_108
speo IbLdens1 tv
slow
--
fast slow .J·1b1_II8lLnd1us
lblJ1tn..radtus
density
I
II1n line
pause
.1n1_ rad1us .t ni_ I'.8A1 on
.1n .tn reset
-
shape
sl cLdens1 tv
II1n
Uld_ rad1us
- - .in
- - t _ slcL118lLradtus
..J. slcL.tn..nd1us
slct.sP-1
-,,/ct1scs
r111lJS
The Edit Menu Figure 12.28 The Select Menu Figure 12.29
We've used the @C shortcut already. It's worth learning the other Ordinarily a vidget is selected by clicking on it. Sometimes, however, it
shortcuts to save time in building interfaces. Aligning vidgets is de- is difficult to select a line, since it's only two pixels wide. A vidget also
scribed in Appendix M. may have no visible appearance and can get "lost". The Select menu
solves these problems. It also illustrates why choosing good mnemon-
The Select menu lets us select a vidget by its ID, as shown in Figure 12.29. ics for vidget IDs is important.
VIS Output
When a file is saved by Vill, it appends Icon code describing the interface
to the named file. If the file is new, Vill provides a main procedure to give a
runnable program that behaves much like a prototype in VIB does. If the file
already contains Icon code, Vill does not modify that code but just appends an
updated VIB section when the file is saved.
The Vill section for the kaleidoscope application looks like this:
252 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 253
#===«vib:begin»=== modify using vib; do not remove this marker line More About Vidgets
procedure uLattsO
return ["size=600,455", "bg=pale gray"] Vidgets are implemented with records whose fields contain the at-
end tributes of the vidgets. Some of these are described below; see Appendix L for
more information.
procedure ui(win, cbk)
return vsetup(win, cbk, All vidgets on an interface are enclosed within a "root" vidget. The root
[":Sizer:: :0,O,600,455:kaleido",), vidget accepts user events (such as mouse presses) and identifies the vidget, if
["file:Menu:pull::12,3,36,21 :File",file_cb, any, on which the mouse cursor is positioned. If the mouse cursor is on a vidget
["snapshot @S","quit @Q"ll, when an event occurs, that vidget is activated. For example, if a mouse button
["labeI01 :Label:::13, 180,21,13:min",], is pressed with the cursor on a slider vidget, the slider is activated. If the event
["labeI02:Label:::152, 180,21 ,13:max" ,), is not appropriate for that vidget (such as a keypress event on a button vidget),
["labeI03:Label:::13,240,21, 13:min",], it is rejected by the vidget.
["labeI04:Label:::152,240,21, 13:max" ,l,
["labeI05:Label:::13,300,21,13:min",], Vidget States
["labeI06:Label:::152,300,21, 13:max" ,l,
["labeI07:Label:::7, 120,28,13:slow",), Toggle buttons, radio buttons, text lists, text-entry fields, sliders, and
["labeI08:Label:::151 ,120,28,13:fast",], scrollbars maintain internal states. For most of these, the vidget state is the same
["lbLdensity:Label:::67, 160,49,13:density",], as the last callback value it produced. But for textlists, the state is a list ofintegers.
["lbLmax_radius:Label:::43,280,98,13:maximum radius" ,l, The first integer indexes the top line currently displayed; this reflects the
["lbLmin_radius:Label:::44,220,98,13:minimum radius" ,], position of the scrollbar thumb. Additional integers, if any, index the currently
["lbLspeed: Label: ::74,100,35, 13:speed",], selected items.
["line:Line:::0,30,600,30:",], Since the callback values and the states usually are the same, it seldom
["pause:Button:regular:1 :33,55,45,20:pause",pause_cb], is necessary to ascertain the state of a vidget. If it is necessary, the procedure
[" reset Button:regular:: 111,55,45,20:reset" ,reseCcb),
["sld_density:Slider:h:1 :42,180,100,15:1,100,50",density_cb], VGetState(vidget)
["sld_max_radius:Slider:h:1 :42,300,100,15: 1,230,115",max_radius_cb), produces the state.
["sld_min_radius:Slider:h:1 :42,240,100,15: 1,230,115",min_radius_cb],
["sld_speed:Slider:h:1 :42,120,100,15:500,0,250",speed_cb), The procedure
[l region:Rectraised::188,42,400,400:",], VSetState(vidget, value)
)
end sets the state of the vidget to the given value. It also produces a callback, as if the
#===«vib:end»=== end of section maintained by vib user had set the value by manipulating the interface. For example, VSetStateO
can be used to set the state of a slider and move its thumb to the corresponding
The first and last lines are markers that VIB uses to find the interface position.
section in an existing file. The code produced by VIB should not be modified; if The lists of items associated with menus and text lists can be accessed by
something is changed, VIB may not be able to use the modified section. VGetitems(vidget) and VSetitems(vidget, L), which get and set the lists, respec-
Although the interface sections produced by VIB are not designed for tively.
easy reading, it's worth knowing that every vidget is represented by an Icon list. VGetltemsO returns a list of strings representing the items displayed by
The string up to a colon in the first item in a list is the ID for the vidget. If the the menu or text-list vidget. If a menu vidget contains a submenu, the submenu
vidget has a callback, it's the second item in the list. is represented by two entries in the returned list: a string label followed by a list
of items in the submenu.
254 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 255
VSetltemsO sets the list of strings representing the items displayed by the
menu or text-list vidget. For a menu vidget, any string entry may be followed by The procedure uiO in the VIB section opens the application, draws and
a list representing a submenu. initializes the vidgets, and returns a table that contains the vidget records.
A program with a VIB interface typically begins with
Vidget Fields
vidgets := uiO
Vidgets have several fields that contain attributes. Most of these fields are The keys in the table returned by uiO are the vidget IDs. Their corre-
used for internal purposes, but some provide useful information, such as the sponding values are the vidget records. One vidget in the table returned by uiO
location and size of a vidget on the interface canvas. Except for lines, vidgets is particularly important: a "root" vidget that encloses all other vidgets and
occupy a rectangular area and have these fields: processes events that occur on them. The root vidget has the ID root.
vidget.ax x coordinate of the upper-left corner of the vidget In an application with a VIB interface, events are not handled by EventO
vidget.ay y coordinate of the upper-left corner of the vidget but by higher-level procedures that understand vidgets and the meaning of
vidget.aw width of the vidget events that occur on them. There are two procedures that handle vidget events,
vidget.ah height of the vidget ProcessEventO and GetEventsO.
Regions also have fields that give the "usable" area that can be drawn on ProcessEvent(root, missed, all, resize} processes a single event. If the
without overwriting borders used for three-dimensional effects: event takes place on a vidget and is appropriate for the vidget (such as a mouse
press within the area of a button), a callback for that vidget occurs. The
vidget.ux x coordinate of the upper-left corner of the usable area arguments missed, all, and resize are optional procedures that are called for
vidget.uy y coordinate of the upper-left corner of the usable area events that occur when the mouse pointer is not on a vidget or are not
vidget.uw width of the usable area appropriate for that vidget (such as a keyboard event with the mouse cursor on
vidget.uh height of the usable area a button), for all events, and for resize events, respectively. For example,
The Organization of a Program with a VIB Interface ProcessEvent(root, , shortcuts}
might be used to call shortcutsO for all events in order to handle keyboard
An application with a VIB interface usually has several relatively distinct
shortcuts that are entered whether the mouse pointer is on a vidget or not.
components, as illustrated in Figure 12.30.
The procedure ProcessEventO is used when an application wants to
Program Organization handle events one by one. For example, the kaleidoscope application needs to
header The section labeled "application function-
run the display between user events. Such programs typically have an event
ality" contains the code necessary to imple- loop of the form
main procedure ment the features of the program that do repeat {
not reside in callbacks. All but the VIB while *PendingO > 0 do
initialization interface section is written by the author of ProcessEvent(vidgets[lroot"])
the application.
# work performed between processing events
application functionality }
In the repeat loop, if there are any pending events, they are processed
callbacks before going on. This assures prompt response to the user. If no events are
pending, other work is done. The amount of computation done before checking
VIB interface again for user events should be small, so as to assure a responsive interface.
Figure 12.30
The procedure GetEvents(root, missed, all, resize}, whose arguments
have the same meaning as those for ProcessEventO, handles all events and does
256 Building a Visual Interface Chapter 12 Chapter 12 .Building a Visual Interface 257
The window to use also can be specified as the argument to a /lui" is a shift in focus between the control window and the drawing window.
procedure, as in Furthermore, when the drawing window is the focus, all events in it might be
processed, ignoring events in the control window until a specific event in the
controLwin := WOpen ! controLatts() drawing window changes the focus to the control window, and vice versa. The
controLvidgets := control(controLwin) code might look like this:
draw_win := WOpen ! draw_atts()
draw_vidgets := draw(draw_win) root := controLroot # initial interface
while ProcessEvent(root, ...)
Controlling MUltiple Interfaces
procedure go_drawO # callback in control
As mentioned earlier, each interface has its own set of vidgets; in each, root := draw_root
the ID of the root vidget that encloses and manages all others in the interface is
"root". These roots can be obtained as needed or assigned to variables, as in return
control_root := control_vidgets["root"] end
draw_root := draw_vidgets[" root"] procedure go_controlO # callback in draw
The most difficult part remains: managing events in more than one root := controLroot
window. How this is done depends on the functionality of the application.
return
The simplest case is a purely event-driven application in which actions
are taken only in response to user events and events in all interface windows end
have equal priority and need to be handled as they occur. where gO_drawO is in control.icn and go_controlO is in draw.icn.
In this case, it is not sufficient to process the windows in order, waiting, One problem with this is that if events occur in the control window while
for example, for an event in the first window before going on to the second. If this the draw window is the focus of attention, these events are not processed until
is done, events may accumulate in other windows and not be processed. the focus is changed - and then they all are processed.
The procedure Active() can be used to deal with this problem. Active() One way to handle this problem is to discard events that occur in
returns a window in which an event is pending - blocking and waiting for an windows other than the focus window. This can be done by emptying the event
event if none is pending. Every time Active() is called, it starts with a different queue of the window that is to become the focus before changing the focus. The
window in round-robin fashion, to assure that all windows can be serviced. callbacks given earlier can be modified to do this:
The event loop for an event-driven application of the kind described procedure go_drawO # callback in control
above might look like this:
while get(Pending(draw_win))
repeat {
root := case Active() of { root := draw_root
controLwin: controLroot return
draw_win: draw_root
end
}
ProcessEvent(root, ...) procedure go_controlO # callback in draw
}
while get(Pending(controLwin))
where the ellipses indicate other possible arguments for ProcessEvent(). root := controLroot
In some applications, different interface windows may have different
return
priorities. For example, a drawing application might be designed so that there
end
260 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 261
Handling multiple interfaces poses other problems for an application • Don't innovate. Interface design is difficult at best and innovation
like the kaleidoscope that is not entirely event driven. In this kind of an should be left to professionals, most of whom know it's generally not
application, processing goes on even if there are no user events, but user events a good idea. Users tend to like interfaces that look familiar and allow
must be processed when they occur. them to use their experience with other interfaces.
For a single interface, the event-processing loop typically looks some- • Be neat. An interface that is sloppily done suggests an application with
thing like this: a similar problem. Align vidgets where appropriate and lay them out
in a logical and attractive way.
repeat {
• Avoid clutter. It's all too easy to use too many vidgets, resulting in
while *PendingO > 0 do
what is called the "747-Cockpit Syndrome". See the tip on Choosing
ProcessEvent(root, ...)
Vidgets at the end of Chapter 11.
# do something before checking for next event
} • Use color sparingly and appropriately. See the tip on Interface Colors
that follows.
It's important that what's done before checking for the next event be
brief; otherwise the user may become annoyed at the unresponsiveness of the • Ask others what they think of your interface and listen to what they
interface, perhaps repeat actions that"didn't take", or even assume the applica- say.
tion is "hung". • Be willing to modify your interface to improve it - or even to scrap
it and start over.
If we introduce multiple interfaces, this event loop needs to be recast. For
two interfaces, the loop might look like this: Interface Colors
repeat {
while *Pending(win1 I win2) > 0 do When designing an interface, it is tempting to use color for decoration-
ProcessEvent( to enliven the interface. While this may produce "interesting" results, it can all
case ActiveO of { to easily lead to gaudy, confusing, and illegible results. What you think is
win1: root1 attractive may not appear that way to others.
win2: root2 Guidelines for color usage on interfaces have been established through
}, years of experimentation and testing. Here are some of them:
) • Backgrounds should be neutral and unsaturated. Light gray is a good
# do something before checking for next event choice.
}
• Text generally should be black; in any event, dark text on a light
Note that ActiveO is called only if there is an event pending in one of the background always is more legible than the opposite.
windows; it therefore does not block. • Bright colors should be used with restraint, in small areas, and only to
attract attention.
Tips, Techniques, and Examples • Certain colors have generally accepted connotations:
red: danger, stop
The Aesthetics of Interlace Layout yellow: caution, warning
Here are some guidelines for laying out visual interfaces: green: okay, go
• Unless the interface is for your use only, design it with a typical user • Blue is perceived as the least intense of colors, yellow the most.
in mind. Avoid showing off. • Light blue is the least visible color; it is suitable only for uses such as
grid lines.
262 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 263
• Adjacent areas of different bright colors may cause optical illusions Invisible Regions
and should be avoided. VIB shows invisible re-
gions with dashed out-
Interface Window Attributes lines so they can be lo-
cated when building an
It is possible to add to or override the window attributes provided by sillPlex ,,,.---.,, interface. Such regions
VIB. duplex
'. 1
are, however, invisible in
triplex the application interface
As mentioned earlier, uLattsO returns a list of the attributes for the
•---------------------i
t ! window.
interface window. Attributes can be appended to this list before using it to
provide arguments for WOpenO. For example, adding posx and posy attributes
can be used to determine the location on the screen at which an interface is
opened. Figure 12.32
Appending attributes that are already on the list overrides them. For
example, if you want an ivory background, this can be used: The application can access these regions by ID. Suppose the ID for the
square regions is logo and the ID for the lower region is status. Then, the
put(atts, "bg=pale yellow-white") application might do this during initialization:
WOpen! atts
logo := vidgets[ OI logo"]
Placing Graphics and Text on an Interface status := vidgets[OIs tatus Ol ]
Readlmage( logo.gif", logo.ax, logo.ay)
l
An application can draw or write text anywhere on its interface window. DrawString(status.ax, status.ay + status.ah, status_text)
Doing this on portions occupied by vidgets generally is unwise, since it may not The height of the status region is added, since the y coordinate is at the top of the
only obscure the vidget, but also because VIB redraws some kinds of vidgets region and the text is drawn on a baseline.
after the user manipulates them, in tum overwriting what the application may
have placed on them. The result might appear as shown in Figure 12.33.
Places on a window that are not occupied by vidgets can be used, A Decorated Interface
organize
however, for decoration or to provide information. One way to assure that such file Action It may take some adjustment to get text written on an
material is in the appropriate place is to create regions with invisible borders but interface where it looks best.
no functionality. The locations and extents of these regions then are accessible to sillPlex a
duplex B
the program and can be used when placing graphics and text. triplex
It's important to reset the normal drawing mode; otherwise other actions on the Unaligned Text-Entry Fields
interface may produce inappropriate results.
Although it may seem like a trivial matter, a well laid-out
width:
Sharing Callback Procedures interface is important in making a good impression.
depth:
Another approach is to check the vidget value. This doesn't work for
buttons, but it does work for menus, provided that all the menu labels are
unique: Figure 12.35
Completing an Application
267
268 Completing an Application Chapter 13 Chapter 13 Completing an Application 269
Applications that support user control through a visual interface usually procedure main(args) # initialize the interface
need more global variables than other kinds of applications because several initO # initialize the application
procedures may need to access the same state information. The global variables
for the kaleidoscope application are divided into three sections: kaleidoscopeO # run the kaleidoscope
global vidgets # table of vidgets All initialization is done by initO. The initialization could have been placed in the
global root # root vidget main procedure, but a substantial amount of code is needed, and putting it in a
global pause # pause vidget separate procedure makes the program structure clearer.
global size # size of display area (width & height) The procedure kaleidoscopeO draws the display and contains the event
global half # half size of display area loop that handles user actions. The structure of the event loop is:
global pane # graphics context for viewing
repeat {
global colors # color list
# set up new display
# Parameters that can be set from the interface repeat {
global delay # delay between drawing circles # process pending events
global density # number of circles in steady state # break out of inner loop if new display needs to be set up
global draw_proc # drawing procedure # draw and erase circles
global max_radius # maximum radius of circle }
global min_radius # minimum radius of circle }
global scale_radius # radius scale factor
270 Completing an Application Chapter 13 Chapter 13 Completing an Application 271
This event loop is described in more detail in the section on drawing the return
kaleidoscope. end
Initialization The procedure uiO opens the application window and draws and initializes the
vidgets. It returns a table containing the vidgets, which is assigned to a global
Initializing the application involves creating the interface, setting up the variable.
display area, establishing initial values, and setting the states of the vidgets
The root vidget, which also is assigned to a global variable, is needed for
correspondingly:
the event loop. Next, the size of the display region is determined by accessing the
procedure initO appropriate fields of the region vidget. Notice that the "usable" portions of the
region are used; they determine the part of the region that can be drawn on
randomizeO
without overwriting its border. A check is made that the width and height are
vidgets := uiO the same, since a square area is required by the geometry of the display.
root := vidgets[" root"] A clone then is made for the display area. The origin is set to the center
size := vidgets[" region"].uw of the area using the dx and dy attributes because the drawing is symmetric
if vidgets[" region"].uh -= size then { around the center and placing the origin there simplifies the drawing code. The
Notice("improper interface layout.") display area is clipped to prevent drawing outside the region. Drawing is done
exitO with "drawop=reverse", so that circles can be erased by drawing them a second
} time.
delay:= 0 The initial values for the display are set next. The delay is set to zero, so
density := DensityMax / 2.0 that the display runs at the maximum speed until the user changes it. The chosen
max_radius := SliderMax # scaled later values for the density of the display (the number of simultaneous circles
min_radius := SliderMin allowed) and the maximum and minimum radii are somewhat arbitrary. They
scale_radius := (size / 4) / SliderMax were chosen by experiment to provide an attractive display.
draw_proc := FillCircle The global variable draw_proc, whose value is the procedure used for
colors := [] drawing, is set to FiIICircle, so that the display starts with discs.
every put(colors, PaletteColor(lc1", !PaletteChars(lc1"))) The pause vidget is assigned to a global variable so that its state can be
pause := vidgets["pause"] checked.
VSetState(vidgets[ldensity"], (density / DensityMax) * SliderMax) The palette c1, which has 90 colors, was chosen for colors by experiment.
VSetState(vidgets["delay"], delay) Next, the states of the sliders and radio buttons are set to correspond to
VSetState(vidgets[lmin_radius"], min_radius) the global variables just set; what the user sees initially corresponds to the actual
VSetState(vidgets["max_radius"], max_radius) state of the application.
VSetState(vidgets[lshape"], "discs")
The procedure kaleidoscopeO makes random choices for colors and
# Get graphics context for drawing. radii. The use of randomizeO assures that the display is somewhat different on
half := size / 2 every run.
that controls the speed of the display is: minimum radius track the one for the maximum radius. The callback procedure
for the minimum radius is similar to the one above.
procedure delay_cb(vidget, value)
Changing the shape that is used for drawing is done by assigning the
delay := value * 200 appropriate procedure value to the global variable draw-proc depending on the
return radio button the user chooses:
end procedure shape_cb(vidget, value)
The global variable delay is used in kaleidoscopeO to pause temporarily be- draw_proc := case value of {
tween drawing actions. We'll show that later. "discs": FiIICircie
"rings": DrawCircle
Controlling the density of the display is nearly as simple as setting the
}
delay, but when the density is changed, kaleidoscopeO must be informed that
the display needs to be erased and drawing restarted. This is done by setting the reset := 1
global variable reset to a nonnull value: return
procedure density_cb(vidget, value) end
density := (value / SliderMax) * DensityMax The callback procedure for the file menu illustrates that the callback
density <:= 1
value for a menu is a list, even if there are no submenus:
reset := 1 procedure file_cb(vidget, value)
end case value[1] of {
We'll show how reset is used when we get to kaleidoscopeO. ·snapshot @S": snapshot(pane, -half, -half, size, size)
"quit @O" : eXit()
Conceptually, the callback procedures for setting the radii are as simple
}
as the one for setting the density. It's necessary to ensure, however, that the
maximum radius is not set to less than the minimum radius, and conversely. return
This is accomplished by forcing the other value to conform to the newly set one: end
procedure max_radius_cb(vidget. value) The strings in the case expression must exactly match the items in the menu,
max_radius := value since they are the source of the values in the list.
if max_radius < min_radius then { # if max < min lower min The procedure snapshot() is contained in interact, which is linked in the
min_radius := max_radius program header. This procedure requests a file name for the saved image, alerts
VSetState(vidgets[ls ld_min_radius"], min_radius) the user if a file with that name already exists, and provides the user the choice
} of overwriting an existing file or of choosing another name.
reset := 1 In the case of this application, a user request to quit is honored without
comment. In an application that creates or modifies data, the user should be
return
given the option of saving the data or deciding not to quit.
end
The procedure for handling keyboard shortcuts is invoked when an
Note that VSetStateO is used to set the minimum radius if necessary. This event is not handled by any vidget, such as a mouse click on a label or a keypress
produces a callback for the minimum radius slider, which sets the state and the with the mouse over a slider. In the kaleidoscope program, there are only two
position of the slider thumb. In this situation the user sees the slider for the keyboard shortcuts, as indicated in the items in the file menu:
274 Completing an Application Chapter 13 Chapter 13 Completing an Application 275
procedure shortcuts(e) The center and radius of each circle are selected at random within limits
that produce an attractive appearance. A color is chosen at random and circles
if &meta then are drawn in each octant until the specified density (number of simultaneous
case map(e) of { # fold case circles) is reached. At that point, the oldest set of circles is erased and a new set
"q": exitO is drawn. This continues until the user intervenes.
"s": snapshot(pane, -half, -half, size, size)
} In order to keep track of the circles so that old ones can be erased, a list
is used as a queue. The parameters of a new circle specification are put on the end
return of the queue and the parameters for the oldest circle specification are taken off
end the beginning.
As described earlier, the meta modifier key, indicated in the file menu by The procedure kaleidoscopeO is:
@, is used for keyboard shortcuts to provide some protection against unin- procedure kaleidoscopeO
tended effects caused by casual typing while the application is running.
# Each time through this loop, the display is cleared and a
Uppercase characters are mapped to lowercase ones so that q and have a # new drawing is started.
the same effect. The actions performed are identical to the ones in the file menu
callback. repeat {
EraseArea(pane, -half, -half, size, size) # clear display
Drawing the Kaleidoscope draw_list := [] # new drawing list
reset := &null
The kaleidoscope is produced by drawing circles in eight symmetric # In this loop a new circle is drawn and an old one erased, once the
positions (the crystallographic symmetry p4m, known in quilting as the sun- # specified density has been reached. This maintains a steady state.
flower symmetry). The symmetry is produced by two "mirrors", as shown by
the heavy lines in Figure 13.2 and their reflections in each other as indicated by repeat {
the narrow lines. while (*PendingO > 0) I WGetState(pause) do {
ProcessEvent(root, , shortcuts)
if \reset then break break next
The Sunflower Symmetry
}
An asymmetrical shape is used here to putcircleO
show the nature of the symmetry. Since WDelay(delay)
circles are symmetric under rotation,
only the relative positions in the eight # Don't start clearing circles until the specified density has
octants are important for drawing. # reached.
if *draw_list > density then c1rcircleO
}
}
end
The outer repeat loop sets up a new display. The list draw_list provides
the queue for keeping track of circles that have been drawn. It is empty for a new
display, since no circles have been drawn yet. The global variable reset is set to
Figure 13.2 null to indicate a new display has been set up.
276 Completing an Application Chapter 13 Chapter 13 Completing an Application
Circles are drawn and erased in the inner repeat loop. Before drawing a # Draw in symmetric positions.
new set of circles, if there are any pending events or if the state of the pause draw-proc(pane, off1, off2, radius)
vidget is nonnull (indicating the display is paused), ProcessEventO is called. It draw_proc(pane, off1, -off2, radius)
processes an event if one is pending or waits for an event if the display is paused. draw_proc(pane, -off1 , off2, radius)
Some events, such as changing the radii of the circles, require the display draw_proc(pane, -off1, -off2, radius)
to be reset. As shown in the previous section on callbacks, this is indicated by draw_proc(pane, off2, off1, radius)
assigning a nonnull value to reset. If this has happened, draw_proc(pane, off2, -off1, radius)
draw_proc(pane, -off2, off1, radius)
break break next
draw_proc(pane, -off2, -off1, radius)
is used to break out of the while loop, break out of the inner repeat loop, and go return
to the beginning of the outer repeat loop.
end
If, on the other hand, drawing is to continue, there is a delay as specified
by delay, a new set of circles is drawn using putcirciesO, and there is another The procedure clrcircleO also draws circles, but it gets the specification
delay. from the oldest one on draw_list and removes it:
At this point, a test is made to determine if the specified density has been procedure c1rcircleO
reached. If the density has been reached, c1rcircleO is called to erase the oldest set local circle
of circles. circle := get(draw_list)
The procedure putcircleO is: outcircle(circle.off1, circle.off2, circle. radius, circle.color)
procedure putcircleO return
local off1 , off2, radius, color
end
# get a random center point and radius
off1 := ?size % half The Complete Program
off2 := ?size % half
radius := «max_radius - min_radius) * ?O + min_radius) * scale_radius In the listing of the program that follows, procedures are given in
radius <:= 1 # don't let them vanish alphabetical order, except for the main procedure, which is given first.
color := ?colors link interact
put(draw_list, circle(off1, off2, radius, color» link random
link vsetup
outcircle(off1, off2, radius, color)
# Interface globals
return
global vidgets # table of vidgets
end global root # root vidget
The offsets for the centers of the circles are picked with an element of global pause # pause vidget
randomness, as are the radii and colors. These four values are put on the end of global size # size of view area (width & height)
draw_list, and outcirclesO is called to do the actual drawing: global half # half size of view area
global pane # graphics context for viewing
procedure outcircles(off1, off2, radius, color) global colors # color list
Fg(pane, color)
278 Completing an Application Chapter 13 Chapter 13 Completing an Application 279
} return
}
end
end
procedure density_cb(vidget, value)
procedure putcircleO density := (value / SliderMax) * DensityMax
local off1, off2, radius, color density <:= 1
# get a random center point and radius
reset := 1
off1 := ?size % half end
off2 := ?size % half
radius := ((max_radius - min_radius) * ?O + min_radius) * scale_radius procedure delay_cb(vidget, value)
radius <:= 1 # don't let them vanish
delay := value * 200
color := ?colors
return
put(draw_list, circle(off1, off2, radius, color))
end
outcircle(off1, off2, radius, color)
return procedure file_cb(vidget, value)
end case value[1] of {
"snapshot @S": snapshot(pane, -half, -half, size, size)
procedure c1rcircleO "quit @a" : exitO
local circle }
circle := get(draw_list) return
outcircle(circle.off1, circle.off2, circle.radius, circle.color) end
return
procedure max_radius_cb(vidget, value)
end
max_radius := value
procedure outcircle(off1, off2, radius, color) if max_radius < min_radius then { # if max < min lower min
min_radius := max_radius
Fg(pane, color)
VSetState(vidgets["min_radius"], min_radius)
# Draw in symmetric positions. }
draw_proc(pane, off1 , off2, radius) reset := 1
draw-proc(pane, off1, -off2, radius)
return
draw_proc(pane, -off1, off2, radius)
draw_proc(pane, -off1, -off2, radius) end
draw-proc(pane, off2, off1, radius)
draw_proc(pane, off2, -off1, radius) procedure min_radius_cb(vidget, value)
draw_proc(pane, -off2, off1, radius)
min_radius := value
draw_proc(pane, -off2, -off1, radius)
if min_radius> max_radius then { # if min> max raise max
282 Completing an Application Chapter 13 Chapter 13 Completing an Application 283
[lregion:Rectraised::188,42,400,400:",],
end )
end
procedure shortcuts(e) #===«vib:end»=== end of section maintained by vib
if &meta then
case map(e) of { # fold case Tips, Techniques, and Examples
"q": exitO
"s": snapshot(pane, -half, -half, size, size)
} Using a Separate Window for the Display
It's actually quite simple to convert the one-window design to a two- scopic display. There are, of course, shapes other than circles that have symme-
window one. Instead of cloning the display region from the subject (interface) tries that do not need rotation - certain polygons and stars, for example.
window and clipping the clone, all that needs to be done is to open another Although it's easy to specify various drawing procedures a user can pick,
window, replacing filled and outline circles can be drawn without changing how their arguments
pane := Clone("bg=black", "dx=" II (vidgets["region"].ux + half), are computed. Other shapes make this aspect of the application more complex.
"dy=" II (vidgets["region"].uy + half» An approach in which shapes are drawn with respect to a bounding box may
Clip(pane, -half, -half, size, size) prove more flexible.
by
Other Applications
pane := WOpen(lbg=black", "size=" II size II"," II size, "dx=" II half,
"dy=" II half) I { The color plates show two other programs that utilize graphics inter-
Notice("Can't open display window. ") faces. The bin packing program of Plate 13.1 shows how several packing
exit() algorithms operate. The tiling program of Plate 13.2 allows the user to select a
} rectangle from an image and have it tiled in a larger area to see how it would look
The size of the display now can be specified in the application instead of being as a repeating pattern.
obtained from the region vidget, replacing
size := vidgets["region"].uw
by
size := 400
The interface needs changing too: removing the region vidget and reducing the
width of the interface canvas. But that's all; just a few trivial changes.
Using two windows allows additional functionality, such as the ability
to change the size of the display while the application is running. Although the
size of the display in the one-window version can be reduced from its original
size at the expense of some complexity in the code, it cannot be made signifi-
cantly larger. In the two-window version, it's easy to change the size of the
display window and to provide the user with a facility for doing this.
We'll leave this as an exercise. You'll find it's not hard to do, but you'll
also see aspects of the one-window version that might have been done in a more
general manner, had this possibility been considered.
It's tempting to try to provide other shapes for drawing. There are two
problems: symmetry and drawing.
Since a circle has complete rotational symmetry, it's not necessary to
rotate it when reflecting around a diagonal mirror; see Figure 13.2. Shapes
without suitable rotational symmetry need to be rotated to produce a kaleido-
Chapter 14
Dialogs
Icon provides two kinds of dialogs: standard ones, which handle common
situations, and custom dialogs built by VIB, which can be tailored for specific
uses.
Standard Dialogs
In addition to the notification dialogs, open dialogs, and save dialogs
described in Chapter 10, Icon has several other standard dialogs for situations
that occur frequently.
Text Dialogs
Unlike the dialogs that were described previously, TextDialogO pro- A Simpler Text Dialog
I .
vides for labels that appear before text-entry fields to identify them. Each field It is good practice to offer a button to cancel the operation in
can have a default value and a width to accommodate a specified number of case the user has second thoughts.
characters, based on the width of the current font. It
y
Here's an example of the most general kind of usage:
•
TextDialog( h ..
["To open a window, fill in the values", "and click on the Okay button."],
["xpos", "ypos", "width", "height"],
[10, 10, 500, 300],
[4,4,4,4], Figure 14.2
["Okay", "No"],
1 In a dialog that has more than one text-entry field, the text cursor
) indicates the field in which text can be entered and edited. The text cursor
initially is in the first field. Typing a tab moves the text cursor to the next field.
The dialog produced by this call is shown in Figure 14.1. From the last, it moves to the first. A specific field also can be selected by clicking
on it. Pressing return or clicking on a button dismisses the dialog.
A General Text Dialog
Since lists are used in the arguments of Selection Dialogs
TD DP8II • wi ndow, fill 1n the YlI1 ues TextDialogO, there is no limit to the number of
end c11 ck on the 0kII'/ button.
text-entry fields except the height of the screen. The procedure SelectDialogO allows the user to pick one of several
IlPDS
ypos 10
choices. Its general form is
width SOD SelectDialog(captions, choices, dflt, buttons, index)
..1ght 3DD
The arguments captions, buttons, and index serve the same purpose that they do
in TextDialogO. The argument choices is a list of choices from which the user can
select. The argument dflt is the default choice, which is highlighted in the dialog.
Figure 14.1 The defaults for omitted arguments follow the same rules as the defaults for
TextDialogO. The user's choice is returned as a string in dialog_value.
If there is only one caption line, it can be given as a string instead of a list. The following procedure call illustrates the use of SelectDialogO.
If there is only one text-entry field, the specifications for it also can be given as
single values instead of lists. In the case where there are several fields and all SelectDialog(
have the same value, a single value can be given for that argument in place of a "Pick a suit",
list. If there are no labels or defaults for fields, these arguments can be omitted ["spades", "hearts", "diamonds", "clubs"],
altogether. The default field width, if its argument is omitted, is 10. "hearts" ,
["Okay", "Cancel"]
If the button argument is omitted, Okay and Cancel buttons are pro- )
vided. If no button index is given, the first button is the default button. An index
of 0 indicates that there is no default button. The dialog produced by this call is shown in Figure 14.3.
An example of the use of defaults is:
TextDialog("Open window:", ["x", "y", "w", "h"l)
which produces the dialog shown in Figure 14.2.
290 Dialogs Chapter 14 Chapter 14 Dialogs 291
Figure 14.4
292 Dialogs Chapter 14 Chapter 14 Dialogs 293
Custom Dialogs
If no standard dialog fits a particular need, a customized dialog can be
built using VIB. The method for building a dialog using VIB is very similar to the
one for building an application interface. The few differences are noted in the
following example.
Lines have both width and style attributes. The width can be entered in
a text-entry field, but the style should be chosen using radio buttons that indicate
the possible choices. There are standard dialogs for both cases, but that would
require the use of two dialogs, which is an annoyance for the user. A custom
dialog can be created in VIE to handle both kinds of vidgets.
In order to create a dialog using VIE, VIE must be informed that a dialog,
not an application interface, is being created. This is done by checking dialog
window and entering the name of a procedure to invoke the dialog in the VIB
canvas dialog, as shown in Figure 14.6:
attrlbs.lcn
prucedure . - : lettrtbutes A Dialog for Setting Attributes Figure 14.7
window 1.1:
wi dth: liiiii In a custom dialog all kinds of vidgets except menus, text lists, and
hei gilt: f37S Iii"die109 wi ndow , regions can be used.
A dialog must have at least one regular button; otherwise there would be
no way to dismiss it. VIB enforces this. A default button can be designated by
selecting dialog default in the button dialog, as shown in Figure 14.8.
The VIB Canvas Settings for a Custom Dialog Figure 14.6
The window label is irrelevant for a dialog; the label for the dialog is
inherited from the window of the application that invokes the dialog.
Next the vidgets for the custom dialog are created and placed as they are
in building an application. Figure 14.7 shows a dialog for setting attributes.
294 Dialogs Chapter 14 Chapter 14 Dialogs 295
The code produced by VIB for a custom dialog is similar to that produced #===«vib:begin»=== modify using vib; do not remove this marker line
for an application. It is shown later at the end of a complete listing of a procedure procedure attributes(win, deftbl)
for using the attribute dialog. static dstate
initial dstate := dsetup(win,
Using a Custom Dialog ["attributes:Sizer:: 1:0,0,256,160:" ,l,
["cancel:Button:regular::164,1 02,60,30:Cancel" ,l,
A custom dialog is invoked by calling the procedure named in VIB's ["linestyle:Choice::3: 155,20,78,63:""
canvas dialog. The argument of the procedure is a table whose keys are the IDs ["solid","striped", "dashed"]],
of the vidgets in the dialog and whose corresponding values are the states of ["linewidth:Text::3:1 0,20,115, 19:1ine width: \\=" ,l,
these vidgets. ["okay:Button:regular:-1 :31,1 02,60,30:0kay",l,
)
When a dialog is dismissed, it returns the text of the button used to return dpopup(win, deftbl, dstate)
dismiss it (as for standard dialogs). Before returning, it also changes the values end
in the table to correspond to the states of the vidgets when the dialog was #===«vib:end»=== end of section maintained by vib
dismissed. Here's the code for the line attribute dialog:
If the value for the line width is invalid, an attempt to set it fails. If this
link dsetup # dialog setup happens, the user is notified and the dialog is presented again. The radio button
procedure attribs(win) choices, on the other hand, are guaranteed to be legal by virtue of the button
static atts names used.
initial atts := tableO # table of vidget IDs An example of the use of the attribute dialog is shown in Figure 14.9.
twin := &window The Custom Dialog
# Assign values from current attributes. This dialog could be made more capable by allowing
11ne width: 0 the user to set the foreground color, pattern, and fill
atts[llinewidth"] := WAttrib(win, Ilinewidth")
attributes.
atts[llinestyle"] := WAttrib(win, Ilinestyle")
# Call up the dialog.
Figure 14.9
296 Dialogs Chapter 14 Chapter 14 Dialogs 297
Standard Dialogs Versus Custom Dialogs The lines can be written explicitly in the call to NoticeO, but it may be
more convenient to put the lines of text in a list and then use list invocation, as
Standard dialogs generally are easier to use in a program than custom in
dialogs, and they have the virtue of providing a standard appearance. Standard
dialogs also offer a facility that is easily overlooked. A standard dialog is help := [
constructed using the arguments given when the corresponding dialog proce- "If you want to move an object in the display·,
dure is invoked. These arguments can be lists that change depending on current "window, first select it with the pointer tooL",
data. For example, in an application that allows the user to create and delete "Then you can drag on one of the handles on",
items, standard dialogs can display the current list of items, which may change "the edges, nudge it one pixel at a time using",
the number of items presented in the dialog. "the arrow keys, or get a dialog for precise",
"positioning from the Adjust menu."
Constructing custom dialogs requires time and effort. Custom dialogs, ]
however, can be laid out for a particular situation, and slider, scroll bar, label,
and line vidgets can be used in their construction. Unlike standard dialogs, Notice! help
however, the structure of a custom dialog is fixed when it is created. The states
of the vidgets can be changed, but the vidgets themselves cannot. This technique is particularly useful when the contents of a notice dialog
are not fixed and known when the program is written. The list can be created
Since VIB can handle only one VIB section in a file, custom dialogs must during program execution and used as needed.
be kept in separate files if they are to be maintained using VIB. In this case, the
applications that use them must link their ucode files. The need for multiple files Creating Notice Dialogs with Nonstandard Buttons
causes organizational, packaging, and maintenance problems. A general guide-
line is to use custom dialogs only when standard dialogs won't do or when a A notice dialog is just a text dialog with no text-entry fields. You
custom dialog can provide a substantially better interface. therefore can use TextDialogO to create notice dialogs with nonstandard but-
tons. For example,
Library Resources TextDialog(["We're trapped!"], , , , ["No way", "Get help", "We quit"])
The attribs module provides: produces the dialog shown in Figure 14.10.
atlribs() interactively alter graphics attributes Nonstandard Notice Buttons
The interact module contains several general-purpose dialog proce- One of the problems with dialog procedures is that
dures, including these: we',. tl"llPlMld I they have many arguments. It/ s difficult enough to
keep track of them if all are used; when many
load_fiIe(s) file loading dialog
aren/t, it's a matter of counting commas.
save_as(s1, s2) file saving dialog
snapshot(x, y, W, h, n) window dump dialog Figure 14.10
If a custom dialog has more than one text-entry field, the order in which
Creating Notice Dialogs with Many Lines of Text text-entry fields in a custom dialog are selected by pressing the tab key is the
lexical order of the IDs for the text-entryfields. Since mnemonic IDs are not likely
A notice dialog can be used not only to alert the user to a problembut also to be in lexical order, the desired order can be imposed by prefixes, such as 1_fg,
to provide information. In this usage, a notice dialog may have many lines of 2_bg, 3_width, 4_pattern, and so on.
text.
298 Dialogs Chapter 14
The Application
Basic Functionality
299
300 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 301
The functionality described above can be cast in many ways. Figure 15.1
shows our choices and the visual layout we've designed.
In such a matrix, imx[i. j], which is equivalent to imx[ilU], references the jth value
in the ith row, so that, for example,
imx[3, 6] := 0
clears the bit in the third row and sixth column to the background.
Although this list-of-lists representation is a natural one, there's an
alternative representation that can be subscripted in the same way, but that is
easier to use for many operations - a list of strings.
The Interface as Seen in VIB Figure 15.3
The application is shown with the specification sheet for the region that In the list-of-strings representation, each row of the pattern is repre-
displays the pattern. sented by a string of Os and Is. Thus, the matrix above can be represented by
imx:= [
There are five objects in the interface: a menu, three regions, and a line "1 0000001".
of decoration. Note that one region handles all transformation events. It is ·01000010"•
treated as a grid of button-sized cells, not all of which are used. Positioning "00100100",
individual buttons is left to the program. Icons for the buttons (designed in the "00011000" ,
pattern editor) are drawn by the program. "00011000",
"00100100",
Data Representation "01000010",
"10000001"
An important issue in the implementation of most applications is how to ]
represent the data that is to be manipulated. In the pattern editor, the main
concern in this regard is the representation of bi-Ievel pattern specifications. In this representation,
Although the string representation of patterns used by DrawStringO and Pat- imx[3. 6] := "0"
ternO is convenient for storing specifications in files and representing patterns
literally in programs, this representation is not appropriate for operations on also clears the bit in the third row and sixth column to the background.
patterns, such as setting or clearing bits or for transformations like rotation. The To see how operations on these two representations compare, consider
obvious representation for these purposes is a matrix in which each bit of the a procedure that creates a blank i-by-j pattern. In the list-of-lists representation,
pattern is 0 for background and 1 for foreground. In Icon, a matrix can be this procedure is
represented as a lists of lists.
304 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 305
Another advantage of the string representation is that Icon's extensive link imxform # pattern utilities
string-manipulation repertoire can be applied on a row-by-row basis. For link vsetup # VIB library
example, to invert the foreground and background using the list-of-lists repre- Defined constants are used to parameterize the program:
sentation, every bit must be set separately, as in
$define ButtonSize 16 # size of buttons
procedure imxinvert(imx) $define MaxBits 32 # maximum pattern dimension
local i, j $define MaxCell 24 # maximum size of grid cell
every i := 1 to *imx do ButtonSize specifies the size of the icons for the transformation buttons. MaxBits
every j := 1 to *imx[i] do determines how large a pattern can be. This value is somewhat arbitrary. It also
imx[i, j] := 1 - imx[i, j] is somewhat constrained by the size of the editing grid region, but many
return imx platforms do not support patterns even this large. MaxCelllimits grid cells to a
reasonable maximum size in the case of small patterns.
end
Global variables are needed for values that must be accessible to more
For the list-of-strings representation, the procedure is, again, simpler: than one procedure. Because of the organization around callback procedures, a
procedure imxinvert(imx) program like this one needs many global variables, including one for the pattern
local i matrix, parameters determined by the interface specification, and so on. See the
program listing at the end of this chapter for a complete list of global identifiers.
every i := 1 to *imx do All local identifiers are declared, so if you see an undeclared identifier in a
imx[i] := map(imx[i], -10-, -01-) procedure, you can assume that it is global.
return imx
end
306 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 307
draw-patternO I { fail
Notice("Can't draw pattern.") end
fail
} If the event is on a cell of the grid, setbitO is called with a string value
corresponding to setting or clearing the corresponding bit:
return
procedure setbit(row, col, c)
end local x, y
The procedure draw_pattern(), which also is called whenever the pattern if imx[row, col] == c then
is changed, fills in the viewing region: return # skip processing if no-op
procedure draw-pattern() imx[row, col] := c # modify the pattern
Pattern(pattgc, imxtoims(imx)) I fail touched := 1
FiIIRectangle(pattgc, patCxpos, patt_ypos, patt_width, patt_height) y := grid_yoff + (row - 1) * cellsize + 1
return x := grid_xoff + (col - 1) * cellsize + 1
end if c == "1" then FiIIRectangle(x, y, cellsize - 1, cellsize - 1)
else EraseArea(x, y, cellsize - 1, cellsize - 1)
Note that draw_pattern() fails if the pattern cannot be set. This happens if the
specification is too large for the platform on which the pattern editor is run. In draw_patternO
this case, setupO posts a notice and passes the failure back to its caller. return
Event Processing end
Before proceeding, a check is made to see if the event would change the
There are four callback procedures: one to handle mouse events in the pattern. If not, the procedure returns without taking any further action. Avoid-
edit region, one to handle mouse events in the transformation region, one for the ing unnecessary computation is mainly significant for client-server graphics
file menu, and one for user responses to dialogs. In addition, shortcuts() handles
systems connected by a slow communication link. Otherwise, the pattern matrix
keyboard shortcuts. is changed and touched is set to a nonnull value to indicate that the pattern may
The callback procedure for the edit region processes only presses and need to be saved before starting a new one or quitting the application. Once this
drags for the left and right mouse buttons; all other events in this region are is done, the edit grid is updated, filling or clearing the relevant cell as appropri-
ignored. For relevant events, the procedure first determines if the location is on ate. Finally, the view area is redrawn to show the effect of the modified pattern.
the grid; if it isn't, the event also is ignored: The callback procedure for the transformation region also checks that
procedure grid_cb(vidget, e) the event is relevant, computes the row and column of the correspondingbutton,
local x, y, row, col and calls xformO to perform the appropriate transformation:
if e === (&Ipress I &rpress I &Idrag I &rdrag) then { procedure xform_cb(vidget, e)
row := (&y + cellsize - grid_yoff) / cellsize local col, row
col := (&x + cellsize - grid_xoff) / cellsize if e === (&Ipress I &rpress I &mpress) then {
if «row I col) < 1) I (row> vbits) I (col> hbits) then fail col := (&x - xform_xpos) / ButtonSize
if e === (&Ipress I &Idrag) then setbit(row, col, "1") row := (&y - xform_ypos) / ButtonSize
else setbit(row, col, .0") if not xform(row, col) then fail
return touched := 1
}
310 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 311
touched := &null If the current pattern has not been modified since its creation, there is no
loadname := dialog_value need to save it. Otherwise, the user is asked if the pattern is to be saved. The
setupO current file name is provided, so that the user need not re-enter the name if it is
return to be used. If the user response is positive ("Yes"), the current file name is
} updated (since the user may have specified a new one in the dialog) and it is
saved. If saveO fails, which might happen if the file could not be written, the user
end
is given the opportunity to try again, possibly using a different file name. If this
Because the application edits only one pattern at a time, loading a new also fails, indicating that the user wants to cancel the operation, check_saveO
pattern means losing the old one. The procedure check_saveO displays a dialog fails to notify the procedure that called it. If the response is "No", nothing is done
for saving the old pattern if it has not been saved already. If the user cancels the and check_saveO returns. If the response is "Cancel", check_saveO fails.
dialog, check_saveO fails and consequently loadO also fails, aborting the load
operation and continuing with the old pattern. The Complete Program
If check_saveO succeeds, the user is presented with a dialog in which to link imxform # pattern utilities
specify the name of a file for the new pattern. A repeat loop is provided in case link vsetup # VIB library
the specified file can't be opened. This makes it simple for the user to correct a
spelling error or a mistaken name. $define ButtonSize 16 # size of buttons
$define MaxBits 32 # maximum pattern dimension
Once a file is opened, the string specification in it is converted to a pattern $define MaxCell 24 # maximum size of grid cell
matrix. If this fails (as in the case of a syntactically erroneous specification), the
user is notified and the attempt to load a new pattern is abandoned. A check also # geometry
is made to ensure that the resulting pattern is not too large. If all is well at this global xform_xpos, xform_ypos # offset of transformation area
point, imx is updated, marked as untouched, the new file name is recorded, and global grid_xpos, grid_ypos # position of grid area
the editing grid and view areas are set up. Note that the new pattern initially is global grid_width, grid_height # size of grid area
assigned to load_imx, not imx. This prevents a pattern that is too large from global grid_xoff, grid_yoff # offset of grid
destroying the current pattern. global cellsize # size of cell in grid
As mentioned above, a check is made to see if the current pattern needs global patt_ypos, patt_xpos # position of pattern area
to be saved before attempting to load a new one: global patt_width, patt_height # size of pattern area
global pattgc # graphics context for pattern
procedure check_saveO
# pattern
if \touched then {
case SaveDialog(, loadname) of { global imx # matrix representation of pattern
"Yes": { global hbits, vbits # bits in pattern
loadname := dialog_value global touched # pattern-modification switch
saveO I save_asO I fail global loadname # name of loaded pattern file
} global vidgets # table of vidgets
"No": return
"Cancel": fail # Main procedure
}
} procedure mainO
return vidgets := uiO # set up interface
end
314 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 315
return fail
end end
# Draw pattern area. # Initialize global variables and set things up.
procedure draw_patternO procedure initO
Pattern(pattgc, imxtoims(imx)) I fail # Get layout values from the vidgets
FiIlRectangle(pattgc, patCxpos, patCypos, patCwidth, patt_height)
xform_xpos := vidgets["xform"].ax
return xform_ypos := vidgets["xform"].ay
grid_xpos := vidgets["grid"].ax
end
grid_ypos := vidgets["grid"].ay
grid_width := vidgets["grid"].aw
# Process event for the file menu. Procedures are used, since the
grid_height := vidgets["grid"].ah
# same functionality for most items is needed for keyboard shortcuts
patCxpos := vidgets["pattern"].ax
# also.
patt_ypos := vidgets["pattern"].ay
procedure file_cb(vidget, menu) patt_width := vidgets["pattern"].aw
patt_height := vidgets["pattern"].ah
case menu[1) of {
"load @L": 10adO imx := imxcreate(8, 8) # initial 8-by-a blank pattern
"new @N": newO
loadname := "untitled.ims" # default file name
"save @S ": saveO
touched := &null # pattern not yet modified
316 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 317
# Draw the transformation buttons. place(row, col, pattern) draws the procedure 10adO
# pattern at the specified row and column of the transformation region. local input, load_imx
place(O, 1, "16,#3ffe6003408141c143e140814081408140814081II II check_saveO I fail
140814081408160033ffeOOOO") # shift up
repeat {
place(1, 0, 116,#3ffe6003400140014001401140195ffd4019401" II
case OpenDialogO of {
1140014001400160033ffeOOOO") # shift left
"Okay": {
place(1, 2, 116,#3ffe600340014001400144014c015ffd4c014401" II
if input := open(dialog_value) then break else
140014001400160033ffeOOOO") # shift right
Notice("Can't open" II dialog_value II •.")
place(2, 1, "16,#3ffe60034081408140814081408140814081408" II }
1143e141c1408160033ffeOOOO") # shift down "Cancel": fail
place(4, 0, 116,#3ffe600340014f014e014e014901408140494039" II }
140394079400160033ffeOOOO") # flip right }
place(4, 1, "16,#3ffe6003408141 c143e140814081408140814081II II
"43e141 c1408160033ffeOOOO") # flip vertical load_imx := imstoimx(readims(input)) I { # get a new matrix
place(5, 0, 116,#3ffe600340014079403940394049408149014e01" II Notice("No pattern specification.")
14e014f01400160033ffeOOOO") # flip left c1ose(input)
place(5, 1, 116,#3ffe600340014001400144114c195ffd4c19441" II fail
1140014001400160033ffeOOOO") # flip horizontal }
place(7, 0, "16,#3ffe600340014781404140214021402140f94071II II c1ose(input)
140214001400160033ffeOOOO") # rotate ccw
place(7, 1, 116,#3ffe6003400140f141014201420142014f814701" II if (*Ioad_imx I *load_imx[1]) > MaxBits then {
142014001400160033ffeOOOO") # rotate cw Notice("Pattern too large.")
place(7, 2, "16,#3ffe6003400141c1420144014401440144414261II II fail
141114061404160033ffeOOOO") # rotate 180 }
place(9, 0, 116,#3ffe600340014001400140014001400140014001" II else {
140014001400160033ffeOOOO") # clear imx := load_imx
place(9, 1, 116,#3ffe60ff40ff40ff40ff40ff40ff7OOf817f8" II touched := &null
117f817f817f817f833ffeOOOO") # invert loadname := dialog_value
setupO
# Set up graphics context for pattern area and draw border. return
pattgc := Clone("fillstyle=textured") }
DrawRectangle(patCxpos - 1, patt_ypos - 1, patt_width + 1, end
patt_height + 1)
# Set up the grid and pattern areas. # Create a new blank pattern.
setupO procedure newO
local new_vbits, new_hbits
return
check_saveO I fail
end
repeat {
# Load pattern from a file. case TextDialog("New: ", ["height", "width"], [*imx, *imx[1]], 3) of {
318 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 319
touched:= 1 return
local col, row To begin with, it's not obvious what changes to the pattern should be
if e === (&Ipress I &rpress I &mpress) then { undoable and what changes should not. Presumably, a user would not find it
col := (&x - xform_xpos) / ButtonSize particularly useful to be able to undo the change of a single bit in the pattern. On
row := (&y - xform_ypos) / ButtonSize the other hand, undoing the results of dragging the mouse with the cursor on the
if not xform(row, col) then fail editing grid might be handy - it's all too easy to pass over the wrong cells. And,
touched := 1 most likely, a user would want to be able to undo the results of a transformation
setupO that didn't turn out as planned. In addition, undo itself should be reversible.
} Some applications offer several levels of undo, allowing the user to
return backtrack through many previous operations. But a user may have trouble
remembering the sequence of past operations and get lost. If a significant
end operation is irreversible, it may be helpful to alert the user to this fact before the
operation is performed to allow the user to decide whether or not to perform the
#===«vib:begin»=== modify using vib; do not remove this marker line operation. If nothing else, a warning places the responsibility for the conse-
procedure uLattsO quences on the user. Perhaps the most important aspect of the design of an undo
return ["size=630,330", "bg=pale gray", "label=Pattern Editor") facility is that it be coherent and easy to understand; if the user isn't sure of what
end can and can't be undone, the facility may not be used as well as it might be and,
procedure ui(win, cbk) worse, important changes to a pattern may be lost.
return vsetup(win, cbk, In order to undo a change, it's necessary either to save the pattern before
[":Sizer:::O,0,630,330:Pattern Editor",), the operation or to record enough information to reverse changes. In some kinds
["file:Menu:pull::0,0,36,21 :File",file_cb, of applications, recording user actions may be the best approach, but in this one
["load @L","new @N","save @S","save as","quit @Q"ll, it's easier just to save the entire pattern. Some operations, like clearing a pattern,
[lline:Line:::O,20,630,20:",), require this in any event. If only a single level of undo is supported, only one
["xform:Rectinvisible::30,99,48, 160:",xform_cb), more global variable is needed to save a copy of the pattern. For multilevel
["pattern:Rectinvisible::442,57,160,240:",], undos, a stack can be used.
["grid:Rectinvisible::112,31,299,287:",grid_cb],
) There are efficiency concerns also. If only a single level of undo is
end provided, saving the informatioh to undo changes doesn't take a lot of memory.
#===«vib:end»=== end of section maintained by vib But an unlimited, multilevel undo facility may present problems with memory
utilization.
Making a copy of a pattern is a relatively expensive process. It's not
Tips, Techniques, and Examples sufficient to assign the current pattern to another variable, as in
imx_save := imx
Undoing Changes Because of Icon's pointer semantics, the result of this assignment is that both
imx_save and imx point to the same list. A subsequent change to imx changes
An application like this pattern editor really needs a facility whereby the imx_save also! Instead, what's needed is
user can recover from mistakes, "undoing" (rescinding) unfortunate changes to
the pattern. ("Undo" is an ugly word, but it's commonly used and there doesn't imx_save := copy(imx)
seem to be a better choice.) Although copYO does not copy the elements of imx (which represent the
The design of an undo facility is difficult and its implementation is tricky. rows of the pattern), this is not a problem, since the rows are strings and any
We'll discuss some of the issues involved, but we won't supply an implementa- change to a string creates a new one rather than modifying the current one. Thus,
tion - we'll leave that to you as an "exercise". changing a row in imx does not change the corresponding row in imx_save. It's
324 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 325
worth noting that in the list-of-lists representation discussed earlier in the There's generally less cost in expanding an existing category of features
chapter, copying a pattern matrix requires copying all the row lists too, making than there is to adding a completely new kind offeature. For example, additional
the operation considerably more expensive. transformations can be added to the pattern editor without significantly increas-
ing program complexity or making major changes to the interface.
The tricky part of an undo facility is being sure to save the current pattern
before any change that should be undoable is made - and only then. This Possible additional transformations include increasing the size of the
requires careful analysis of the program. It's clear, for example, that before a background area, trimming off the background area surrounding the rest of the
transformation is applied to a pattern, the current pattern should be saved so pattern, and cropping to change the pattern size in an arbitrary way. You'll find
that the transformation can be undone. But what if the transformation does not procedures in imxform.icn to do such things. All you need to do is create
actually change the pattern? appropriate patterns for the new transformation buttons, decide where they go
(which may involve enlarging or even rearranging the application window),
In the case of a single-level undo facility, saving the current pattern
and adding appropriate code to xformO corresponding to the locations of the
destroys the previously saved one, and hence a transformation that does nothing
new buttons.
prevents the user from going back to the previous pattern. This also is a case
where functionality must be balanced against programming effort, program Another feature that's useful, easy to implement, and commonly found
size, and program correctness - it's not trivial to determine if an operation has in similar applications, is the ability to revert to the last saved version of the
changed a pattern. If a particular operation usually changes a pattern, it's pattern. This facility normally would appear as a new item in the file menu and
probably unwise to check for exceptions. It's unlikely to be a significant problem a corresponding keyboard shortcut. The code needed to load the last saved
in practice, and if you do manage to detect such a case, the user may be surprised pattern is simpler than that found in 10adO, but some of the code there may be
and react inappropriately. useful.
A facility for undoing changes needs to be accessible to the user through A feature that is particularly useful in a pattern editor is symmetric
the interface. This typically is implemented by an item in the file menu and a editing. In a symmetry mode, an editing action not only affects the cell of the
corresponding keyboard shortcut. The necessary procedure is simple: editing grid where the mouse action occurs, but also has a corresponding effect
on cells in symmetric positions. There are eight symmetries for a square corre-
procedure undoO
sponding to the three rotations, four flips, and the "identity" symmetry, in
imx_save :=: imx which an action affects only the cell on which the cursor is positioned. Symme-
setupO tries can, of course, be applied in combination.
return In order to implement symmetric editing, there must be a way to specify
it in the interface. Eight symmetry buttons, each of which can be on or off,
end
provide an intuitive representation. For seven of the symmetries, the same
Notice that the values of imx_save and imx are exchanged. This allows the undo button patterns as for the transformations can be used. The identity symmetry
to be undone, as itwere. A multilevel undo facility would need a separate "redo" might be represented by a single dot in the center of the button. Some way of
operation. indicating which symmetries are in effect is needed. Highlighting, by reversing
the foreground and background of the button, is visually intuitive. Highlight
More Features patterns for the buttons can, of course, be produced easily in the pattern editor
itself.
An application like a pattern editor virtually begs for additional features.
It's so easy to add features that the result may be "creeping featurism": the In order to add symmetry buttons to the application window, it's
accumulation of so many features that the application becomes difficult to learn necessary to redesign the interface. The window needs to be larger, and some
and use. rearrangement of existing components may be desirable. Since the same pat-
terns are used for transformation buttons and symmetry buttons, it may be
Good design dictates that the value of a new feature be weighed against useful to label the two areas so that the user can distinguish them easily.
its cost - programming effort, program size, program correctness, documenta-
tion, and learning effort must be balanced against utility.
326 A Pattern Editor Chapter 15
The Application
Basic Functionality
327
328 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 329
is ready to record the coordinates of the right eyebrow. The program uses the
terms "left" and "right" from the user's standpoint; the "right" eye is actually the
subject's left eye.
We'll assume that a suitable image is available in a format that Icon can
read. Digitized images ofphotographs can be produced by a scanner or obtained
from sources such as bulletin boards and networks. The Caricature Generator Figure 16.2
The next mouse click on the image will be recorded as the location of the
The User Interface comer of an eyebrow.
The program presented here is the end result of an iterative process. We To the left of the image is a sample face on which a target indicates the
started with a sketch on paper and then created an interface using VIR As usual, point that is needed next. Although the caricature seen earlier was drawn with
our early experiences with the program suggested new ideas and highlighted smooth curves, the sample face is drawn with straight line segments to empha-
problems, leading to several changes in the design and implementation. size the point locations.
The main functions of this application are: At the lower left is a slider controlling the amount of distortion to be
applied in constructing a caricature. With the slider in the center, the drawing
• displaying an image and collecting points
reflects the contours as entered. Moving the slider to the right adds increasingly
• saving and reloading coordinate values larger amounts of exaggeration. Moving it to the left subtracts the exaggeration;
• generating and displaying caricatures At -100%, this cancels all of the differences from the sample face to produce a
copy of the sample face. Moving further left produces an "anti-caricature" - a
Figure 16.2 shows the caricature generator in the process of collecting caricature of the sample face with respect to the subject.
features. On the right is the image being entered; this same area also is used to
display the caricature. Above this is a prompt string indicating that the program The File menu is similar to that of the pattern generator, and again
330 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 331
keyboard shortcuts are provided. Entries are provided for loading an image, for Broken outlines delimit the sample face region and the main display region. A
loading or saving a set of points, and for exiting the application. horizontal region in the menu bar is used to position a prompt string. The
distortion slider at the bottom is accompanied by three unchanging labels.
The Display menu, shown in Figure 16.3, selects what is shown on the
Above the slider, another label and region are used to display the slider setting
right side of the window. The user can choose to view the image, the resulting
as a percentage.
caricature, or even both (as illustrated). The combined display is produced by
partitioning the display area with a fine checkerboard pattern, using half of the
cells for the image and half for the caricature. The checkerboard cells are two
pixels wide by one pixel high; this works better for dithered images than the
more obvious single-pixel cell pattern. -,
File D1splllY
""'"
"""
I":.
""
""
""
'"
distortion: ,---_ ..
The midpoints between the pupils of the two faces are found first; these Program Heading
determine the necessary translation (sliding movement). The ratio of the dis-
tances between the eyes becomes the multiplier used for scaling. The link declaration specifies the library packages required:
The two similar expressions in the while loop function in pairs: The first link graphics # graphics library
handles an x-coordinate and the second a y-coordinate. This is done by consum- link vsetup # VIS library
ing a copy of a curve. Another approach would be to reference only the Defined constants are used for some dimensions and flag values:
individual curve, iterating with an increment of two.
$define PupilRadius 2 # radius for drawing pupils of eyes
In effect, the face is moved so that the midpoint is at the origin; then the
coordinate values are scaled; and finally the results are moved to the destination. $define TargetRad1 5 # radii for guide display target
$define TargetRad2 20
The actual exaggeration procedure for creating a caricature is relatively
simple: $define ImageMode 1 # drawing modes
$define DrawMode 2
# distort(f, b, m) -- return distortion of face f from face b by factor m $define DualMode 3
procedure distort(f, b, m)
The event-driven nature of the program makes it necessary to store most
local r, t, i, j, curve, base
of the persistent state information in global variables, so there are many of these.
r := [] The global variable vidgets holds the table of interface objects:
every i := 1 to *f do { global vidgets # vidget table
base := b[i]
put(r, curve := copy(f[i])) The vidget table is followed by global variables that hold the locations
if /curve[-1] I /base[-1] then and dimensions of screen regions:
next # incomplete placeholder global display_xoff, display_yoff # image area
every j := 1 to *curve by 2 do { global display_width, display_height
curveU] +:= m * (curveU] - baseU]) global image_xoff, image_yoff # centered image
curveU + 1] +:= m * (curveU + 1] - baseU + 1])
} global guide_xoff, guide_yoff # guide area
} global guide_width, guide_height
return r global prompt_xoff, prompt_yoff # prompt area
global prompCwidth, prompCheight
end
global dmeter_xoff, dmeter_yoff # distortion meter
The result r is built by copying the curves of f, one at a time, and adjusting each global dmeter_width, dmeter_height
coordinate value. The adjustment amount is calculated by scaling the difference
from the base face b by the factor m. Although the subject window, &window, is used for most operations,
some global windows are used for special purposes:
Program Organization global image_win # scanned image
global targeCwin # binding for point targets
The program consists of header information, the main procedure, other global display_win # binding for image or caricature
procedures (in alphabetical order), and the vm interface specification. High- global overlay_win # binding for dual-mode display
lights of the program are given here; a full listing appears at the end of the The global variable image_win is a hidden window that holds the image. This
chapter. image is copied to the main window when its display is desired.
336 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 337
The global variable targecwin, a done of the main window, is used for vidgets := uiO
drawing targets on the guide face. Clipping is enabled to confine the target WAttrib("pointer=circle") # may fail, but at least try
drawing to its region, and a drawop=reverse attribute allows the targets to be iniCgeometryO
drawn reversibly.
The uiO call opens the window and creates a table of vidgets, which is stored in
The global variable display_win is used for displaying the image and a global variable. The program then attempts to turn the mouse pointer into a
caricature; overlay_win is used for displaying the caricature atop the image. circle, although this may not work on all graphics systems. The iniCgeometryO
These windows also use dipping to confine the output. procedure extracts the layout information from the vidget table, setting several
Four global variables hold face information: global variables with code such as this:
global pointfile # file name for saving coordinates Next, the procedure init_stdfaceO is called to set the coordinates of the
global touched # has data changed since last save? standard face. With many lines of data elided, init_stdfaceO looks like this:
The distortion factor is set by a procedure that also displays it on the screen as end
a percentage. The distortion value is updated and displayed according to the position of the
Next, the guide face is created and drawn. The standard face is scaled and slider. If the caricature is not currently being displayed (that is, if only the image
aligned by scalefaceO, described earlier, based on pupil locations. To specify the is shown), the display mode is changed to add the caricature atop the image.
destination, a face structure consisting of only two pupil locations is calculated Finally, redisplayO is called to redraw the picture using the current display mode
from the location and size of the guide region. The code to do this follows: and distortion value.
1:= guide_xoff + 3 * guide_width / 8 Mouse events are handled by poinCcbO. A click of the left button sets the
r := guide_xoff + 5 * guide_width / 8 coordinates of the point requested on the guide display. A click of the right
Y := guide_yoff + guide_height / 2 button advances to the next curve, clearing all points of the current curve. Action
guideface := scaleface(stdface, [[I, y], [r, y]]) occurs on the release of the mouse button. The code is as follows:
drawface(&window, guideface, DrawLine) # poinCcbO -- handle event in display region
The last initialization step is to load the image:
procedure poinCcb(vidget, e)
newO I exitO
if Itcurve then # if no points are left unset
newO is called to open a dialog and load a file specified interactively. It persists return
until it is successful or until it is cancelled. If cancelled, it fails, and the program
exits. case e of {
With initialization complete, the main procedure then enters the main &Irelease: { # left button sets current point
event loop: sketch[tcurve, 2 * tpoint - 1] := &x
sketch[tcurve, 2 * tpoint] := &y
GetEvents(vidgets["root"], , shortcuts) touched := 1
if mode -= ImageMode & *sketch[tcurve] = 2 * tpoint then
340 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 341
The first step is to remove the current target from the screen. Recall that # caricatureO -- draw sketch distorted by current distortion factor
target_win has a drawop=reverse attribute, which causes two identical se-
procedure caricatureO
quences of drawing operations to cancel each other out. Accordingly, redrawing
local base, face, win
the current target (if tx and ty are not null) causes it to disappear. The prompt
region, which displays the description of the current curve, also is cleared. if /sketch I /sketch[1, 1] I /sketch[2, 1] then
fail # must have both pupils to draw
Next, the global variables tcurve and tpoint, the current point indices, are
set to the starting point of the search. This starting point is usually specified by if mode = DrawMode then
parameters, but if the pupil locations have not been set, the search starts at the win:= display_win # use all the display area pixels
beginning. else
win := overlay_win # use subpattern of display pixels
The indices then are advanced until an unset point is found, proceeding
in an end-around fashion. If the starting point is reached again, there are no Fg(win, "white")
unset points, so targetO returns. FiIlRectangle(win) # clear clipped area using fiIIstyle
Fg(win, "black")
If an unset point is found, the index values are used to find the corre-
sponding coordinates on the guide face, and a new target is drawn. Finally, a base := scaleface(stdface, sketch)
new prompt string is generated and displayed. face := distort(sketch, base, distortion)
drawface(win, face, DrawCurve) # draw distorted face
Displaying Faces
return
The display region shows an image, a caricature, or both, depending on end
the global variable mode. The mode can be set from the Display menu, by a
The window binding win is set depending on the display mode. While
keyboard shortcut, or in some cases by internal program logic. When the display
display_win gives full access to the display region, overlay_win contains the
region is to be redrawn, the following redisplayO procedure is called:
checkerboard pattern that allows writing to only half of the pixels. The
# redisplayO -- display image and/or drawing, depending on mode fillstyle=masked attribute of this window causes any pixels destined for unset
procedure redisplayO areas of the pattern to be discarded.
if mode -= DrawMode then The FiIIRectangleO call, with a white foreground, clears out the pixels
CopyArea(image_win, display_win, , , , , image_xoff, image_yoff) allowed by the fill style. In overlay mode, this is the caricature portion of the
if mode -= ImageMode then checkerboard pattern. A base face is created by scaling the standard face to align
caricatureO with the data points, and then face is assigned a distorted caricature. Finally,
drawfaceO displays the caricature on the screen, again filtered by the pattern.
return
end Loading Images
The CopyAreaO call displays the image by copying it to the display region from The procedure rdimageO reads an image in any format supported by
the hidden canvas. The caricatureO call draws the caricature. Note that there are Icon. The initialization that is needed for a new image also is performed here. If
three possible values of mode given by defined constants in the header. If mode an image cannot be loaded, rdimageO fails. Here is the code:
has the value DualMode, then both CopyAreaO and caricatureO are called.
# rdimage(fiIename) --load image from file, failing if unsuccessful
The caricatureO procedure consists mostly of bookkeeping and display
procedure rdimage(filename)
control, with the actual construction done by the distortO procedure shown
local curve
earlier. Here are the details:
image_win := WOpen("image=" II filename, "canvas=hidden") I fail
344 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 345
pointfile := &null The final step is to clear the display area (in case a previous image was larger) and
touched := &null call redisplayO.
# Calculate offsets that center the image in display area.
Data Files
image_xoff := display_xoff +
(display_width - WAttrib(image_win, "width")) /2 It takes time for a user to create a caricature, so it is important to provide
image_yoff := display_yoff + a way to save the results of this effort. Saving the digitized points allows the
(display_height - WAttrib(image_win, "height")) / 2 caricature to be reconstructed at a later time.
# Initialize a new set of (unset) points. A simple file format is easily generated. Each curve appears as one line,
a single colon followed by the coordinates. For example, a typical data file begins
sketch := []
like this:
every curve := !stdface do
put(sketch, list(*curve, &null)) : 107168
target(1, 1) # reset to start with first point : 168168
: 84 160 87 154 93 150 102 145 114 145 125 149
# Ensure that current mode includes the image, and update the display.
: 147150156147163146175146183152189158
if mode = DrawMode then
mode:= ImageMode
EraseArea(display_xoff, display_yoff, display_width, display_height) Coordinate values in the file are relative to the upper-left comer of the underly-
redisplayO ing image. This allows the program's region sizes and locations to change
without invalidating data files. Zero values serve as placeholders for missing
return
coordinates. The actual file writing is simple:
end
# wtface(f, face) -- write face data to file f
The image is first loaded into a hidden window. rdimageO fails immedi-
procedure wtface(f, face)
ately if this is unsuccessful. Global variables are reset to remove any association
local curve, i
with a coordinate file and to indicate that no points have been added with the
mouse. every curve := !face do {
writes(f, ":")
The next two assignments calculate where the corner of the image should
every i := 1 to *curve by 2 do {
be placed to center it within the display region. The image size is obtained from
writes(f, " ", (\curve[i] - image_xoff) I 0)
the window that was created to contain it. If the image is too large, the corner may
writes(f, " ", (\curve[i + 1] - image_yoff) I 0)
lie outside the region, but this requires no special consideration: The image is
}
clipped to the region boundaries when it is displayed, and the center portion
write(f)
appears, which is probably the best choice.
}
The current coordinate set, sketch, is initialized to contain only null
return
values. The number of curves, and the number of points per curve, is determined
by iterating through the standard face. end
Because there are no coordinates, no caricature can possibly be drawn; Reading is a little more complex than writing, because the formatted data
so, if the display is currently in caricature-only mode, it is changed to show the must be decoded and the possibility of bad data must at least be considered. In
image instead. this program, files that are obviously bad are handled gracefully, but individual
coordinate values are not validated. Only lines with colons are processed, and
At this point the image still is not visible; it's only in the hidden window.
all other characters except digits are ignored; that is sufficient to avoid Icon run-
346 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 347
time errors. After the data is collected, the number of curves and the number of The Complete Program
points on each curve are verified. That can be expected to catch most cases of
link graphics # graphics library
content problems. Here is the code:
link vsetup # VIS library
# rdface(f) -- read face coordinates from file f
# constant definitions
procedure rdface(f)
$define PupilRadius 2 # radius for drawing pupils of eyes
local face, line, curve, i, n
$define TargetRad1 5 # radii for guide display target
face := []
$define TargetRad2 20
while line := read(f) do line? {
=":" I next # ignore line missing ":" $define ImageMode 1 # drawing modes
curve := [] $define DrawMode 2
$define DualMode 3
while tab(upto(&digits)) do {
n := integer(tab(many(&digits))) # vidgets and geometry
if n -= 0 then n +:= image_xoff else n := &null
global vidgets # vidget table
put(curve, n)
global display_xoff, display_yoff # image area
tab(upto(&digits)) I break
global display_width, display_height
n := integer(tab(many(&digits)))
global image_xoff, image_yoff # centered image
if n -= 0 then n +:= image_yoff else n := &null
put(curve, n) global guide_xoff, guide_yoff # guide area
} global guide_width, guide_height
put(face, curve) global prompt_xoff, prompCyoff # prompt area
} global prompCwidth, prompt_height
# Validate the number of curves and points. global dmeter_xoff, dmeter_yoff # distortion meter
global dmetecwidth, dmeter_height
if *face -= *stdface then fail
every i := 1 to *stdface do # windows and bindings
if *face[i] -= *stdface[i] then fail global image_win # scanned image
return face global targeCwin # binding for point targets
global display_win # binding for image or caricature
end
global overlay_win # binding for dual-mode display
String scanning is applied to each line of the file. The expression
# face data
tab(upto(&digits)) #
# (A face is a list of curves, beginning with the left and right pupils;
finds the next numeric field; the expression
# a curve is a list of x and y coordinates.)
integer(tab(many(&digits)))
global descriptions # labels for facial curves
consumes it and converts it to integer. Nonzero coordinates are adjusted for the
global stdface # standard (average) face
location of the corner of the image; the two sections of nearly identical code differ
here, with one adding an x-offset and the other a y-offset. Zero values tum into global guideface # scaled/translated guide face
global sketch # points from subject face
null-valued placeholders.
348 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 349
# Open the window, extract layout information, initialize dialogs. if Isketch I Isketch[1, 1] I Isketch[2, 1] then
fail # must have both pupils to draw
vidgets := uiO
WAttrib(lpointer=circle") # may fail, but at least try if mode = DrawMode then
iniCgeometryO win := display_win # use all the display area pixels
else
# Make two clipped bindings for displaying the image and sketch. win := overlay_win # use subpattern of display pixels
display_win := Clone(llinewidth=2") Fg(win, "white")
Clip(display_win, display_xoff, display_yoff, display_width, FiIlRectangle(win) # clear clipped area using fiIIstyle
display_height) Fg(win, "black")
overlay_win := Clone(display_win, "fillstyle=masked",
Ipattern=4,#9696") base := scaleface(stdface, sketch)
face := distort(sketch, base, distortion)
# Make a clipped binding for displaying targets on the guide display. drawface(win, face, DrawCurve) # draw distorted face
target_win := Clone("drawop=reverse") return
Clip(targeCwin, gUide_xoff, guide_yoff, guide_width, guide_height)
end
# Initialize globals.
init_stdfaceO # coordinates of "standard" face # check_saveO -- check to see if previous coordinate needs to be saved
mode := ImageMode # display mode #
setdist(O) # distortion factor # check_save fails if cancelled.
procedure check_saveO
# Use the standard face to create a guide display for locating targets.
# Calculate eye locations to use for scaling; then draw the face if \touched then
# with straight lines to emphasize the individual point locations. case SaveDialog("Save coordinates first?", pointfile) of {
"Yes": {
I := guide_xoff + 3 * guide_width / 8 pointfile := dialog_value
r := guide_xoff + 5 * guide_width / 8 saveO I save_asO I fail
Y := guide_yoff + guide_height / 2 }
guideface := scaleface(stdface, [[I, y], [r, y]]) "No": return
350 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 351
# drawface(win, f, proc) -- draw face from curve list using proc # init_stdfaceO -- initialize standard face and description list
procedure drawface(win, f, proc) procedure iniCstdfaceO
local curve local spec
every curve := copy(!f) do { descriptions := []
if /curve[-1] then # null-eoordinate stdface := []
next # incomplete curve every spec := ![
if *curve = 2 then ["Ieft pupil 145, 203],
II , # must be first
FillCircle(win, curve[1], curve[2], Pupil Radius) [" right pupil", 255, 203], # must be second
else { ["top of left eyebrow", 101, 187, 105, 177, 126, 168, 153, 170, 177,
push(curve, win) 176,181,185],
proc! curve ["top of right eyebrow", 219,185,223,176,247,170,274,168,295,
} 177,299, 187],
} ["bottom of left eyebrow", 102, 188, 124, 177, 151, 181, 181, 185],
return ["bottom of right eyebrow", 219,185,249,181,276,177,298,188],
["topof lefteye", 114, 199, 141, 187, 172, 198],
end ["top of right eye", 228,198,259,187,286,199],
["bottom of left eyelid", 116, 207, 143, 194, 170, 206],
352 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 353
case menu[1] of { }
}
"load @L": 10adO
"new @N": newO end
"save @S": saveO
"save as II. save_asO # point_cbO -- handle event in display region
"quit @Q": quitO procedure poinCcb(vidget, e)
"image @I": {
if Itcurve then # if no points are left unset
mode := ImageMode return
redisplayO
} case e of {
"drawing @D": { &Irelease: { # left button sets current point
mode := DrawMode sketch[tcurve, 2 * tpoint - 1] := &x
redisplayO sketch[tcurve, 2 * tpoint] := &y
} touched := 1
"both @B": { if mode -= ImageMode & *sketch[tcurve] = 2 * tpoint then
mode := DualMode redisplayO # redraw if new curve done
redisplayO target(tcurve, tpoint) # update target display
} }
}
&rrelease: { # right button skips a curve
return every !sketch[tcurve] := &null # clear all points on curve
end if (tcurve +:= 1) > *sketch then
tcurve := 1
# newO -- load new image target(tcurve, 1) # set target to next curve
}
procedure newO
local input, f }
["header_line:Line:: :0,22,639,22: 11 ,1, "latch on" to the nearest point, allowing it to be dragged to a new location by
["label1 :Label:::11 ,409,77,13:distortion:",1, mouse movement. The case of multiple points close together would need
["labeI2:Label:::9,460,28, 13:anti",1, addressing; one solution would be to move them as a unit, possibly maintaining
["labeI3:Label:::1 04,460,42, 13:normal",], their relationships with each other.
["labeI4:Label:::213,460,28, 13:wild ll ,1,
Implementing all of this would add quite a lot of code to the program. A
[l vert_line:Line:::250,23,250,479:" ,1,
simpler but less flexible approach would be to allow whole curves to be selected
["dmeter:Rectinvisible::104,41 0,41,10:",1,
for replacement. Mouse manipulations would identify a curve, and its points
["promptRectinvisible::252,1 ,387,19:",1,
would be replaced by null values. Then the curve could be re-input using the
["guide:Rectinvisible:: 1,24,247,280:",1,
existing code.
[llimage: Rect invisible::252,24,387,455:",poinecb1,
)
Other Possibilities
end
#===«vib:end»=== end of section maintained by vib After a caricature is drawn, it would be useful to be able to save it to a file
as an image. This is relatively simple to do.
Tips, Techniques, and Examples The current coordinate file format is intimately tied to the number and
order of curves configured in the program. If some identifying information were
Using the Program added to each curve in the file, it would then be possible to enlarge or reorder the
program's set of curves without invalidating existing data files.
Large images produce the best caricatures because the points of the face
can be placed with greater relative precision. Utility programs can be used to The program as given produces caricatures and blends with respect to a
enlarge small images. Even when this enlargement produces visible artifacts, the predefined standard face. Allowing the replacement of this face with values
result is easier to use for making caricatures. The program as presented a from a coordinate file would allow the blending and mixing of any two faces.
640-by-480 window, but it can easily be modified to take advantage of a larger A caricature works by drawing attention to a person's unusual features.
screen. The program presented here has no provision for beards, eyeglasses, or personal
The best images are well-lit, detailed, frontal photographs of unsmiling trademarks such as a pipe or a hat. What could you do in these cases?
subjects. (Big smiles tend to exaggerate into wild sneers.) Color, of course, is not
a factor in the final caricature.
Adjusting Datapoints
Once the last point of a curve has been placed, all the points on a curve
are "locked in". The only way to change one is by editing a coordinate file, a very
error-prone activity. A way to adjust the point locations to correct mistakes or
fine-tune the drawing would be extremely useful.
The obvious approach would be to allow points to be moved with the
mouse. The center mouse button, currently unused, could be reserved for this
purpose. But where are the points? They're not obvious on the drawn figure.
Another display mode could be added to show the points more prominently, for
instance by circling them.
Pressing the center mouse button within one of these circles would then
The Appendices
The appendices that follow contain reference material both for the basic Icon
language and for its graphics facilities.
Appendix A describes Icon's syntax, and Appendix B describes the Icon
preprocessor.
Appendices C though F cover Icon's computational repertoire, includ-
ing features that are not described in the body of this book. In these appendices,
data types are indicated by the following letter codes:
c cset L list
f file N numeric (i or r)
i integer R record (any record type)
n null S set
p procedure T table
r real a any type
S string A any structure type (R, L, S, or T)
Some features of Icon that are used only in special situations unrelated to
graphics are not included in these appendices. See Griswold and Griswold
(1996) for a complete description of Icon.
Appendices G through K include reference material for Icon's graphics
facilities. Appendix L describes Icon's interface tools, and Appendix M is a
reference manual for VIB. Appendix N lists implementation details that vary
among platforms.
Appendix a contains a brief description of how to run an Icon program,
and Appendix P lists resources that are available to Icon programmers. Finally,
Appendix Q describes the contents of the CD-ROM that accompanies this book.
365
Appendix A
Syntax
This appendix presents an informal summary of the syntax of the Icon language.
Some details have been omitted in the name of simplicity and clarity. A more
rigorous presentation appears in Griswold and Griswold (1996).
Italic brackets [like this} indicate optional components; ellipses (...)
indicate repeated items. An ellipsis on a line by itself means that the item on the
preceding line can be repeated; an ellipsis preceded by a punctuation character
means that the preceding item can be repeated by using that punctuation
character as a separator.
program:
link ident ,_ ..
global ident ,...
record ident ( fident ,...) )
procedure
procedure:
procedure ident ( [ident ,.. .) )
local ident , .
static ident , .
initial expr
expr
end
367
368 Syntax Appendix A Appendix A Syntax 369
expr: binop:
ident Binary operators are grouped in classes of decreasing precedence. Op-
expr . ident erators of equal precedence group to the left except as noted.
keyword
\ !
literal
1\ (right associative)
(expr , )
{expr ; } * / 0/0 **
[expr , ] + ++
expr [expr ,... ] II III
expr [ expr sectop expr ] < <= = -= >= > « «= -- -== »= » --- -===
expr ( [ expr ,... ] ) I
unop expr to
expr binop expr := :=: op:= <- <-> (right associative)
?
if expr then expr [ else expr ]
&
case-expression
repeat expr Note: The operator to is an abbreviation for to-by, which actually is a
every expr [ do expr ] ternary operator.
while expr [ do expr ] op:
until expr [do expr ]
An op is any binop except :=, :=:, <-, <->, or to.
next
break [ expr ] sectop:
return [ expr ] +:
suspend [ expr ] [ do expr ]
fail literal:
12316rFFCO integer literal
case-expression: 1.236e23 real literal
case expr of { "violin" string literal
expr: expr 'aeiou' cset literal
default: expr The following escape sequences are recognized in string and cset literals:
} \b backspace
\d delete
unop: \e escape
Unary (prefix) operators have higher precedence than binary (infix) \f form feed
operators, except for field selection (expr. ident), which has the highest \1 line feed (same as \n)
precedence of all. \n newline
\r return
. + - * - I \ = ? ! I not
370 Syntax Appendix A
\t tab
\v
\'
vertical space
single quote
AppendixB
\" double quote
\\ backslash
octal character
\000
\xhh hexadecimal character Preprocessing
\I\c control character
keyword:
Keywords are described in Appendix F.
&ascii &features &mrelease &rrelease
All Icon source code passes through a preprocessor before translation. Prepro-
&c1ock &host &null &shift
cessor directives control the actions of the preprocessor and are not passed to the
&col &input &output &subject Icon compiler. If no preprocessor directives are present, the source code passes
&control &interval &phi &time through the preprocessor unaltered.
&cset &Icase &pi &trace
A source line is a preprocessor directive if its first non-whitespace
&date &Idrag &pos &ucase
character is a $ and if that $ is not followed by another punctuation character. The
&dateline &Ietters &progname &version general form of a preprocessor directive is
&digits &Ipress &random &window
&dump &Irelease &rdrag &x
$ directive arguments # comment
&e &mdrag &resize &y Whitespace separates tokens when needed, and case is significant, as in Icon
&errout &meta &row proper. The entire preprocessor directive must appear on a single line, which
&fail &mpress &rpress cannot be continued but can be arbitrarily long. The comment portion is
optional. An invalid preprocessor directive produces an error except when
ident: skipped by conditional compilation.
An identifier is composed of any number of letters, digits, and under- Preprocessor directives can appear anywhere in an Icon source file
scores; the initial character cannotbe a digit. Upper- and lowercase letters without regard to procedure, declaration, or expression boundaries.
are distinct. The following words, including two relating to features of
Icon not discussed in this book, are reserved; these words cannot be used Include Directives
as identifiers:
An include directive has the form
break every next suspend
by fail not then $include filename
case global of to An include directive causes the contents of another file to be interpolated
create if procedure until in the source file. The file name must be quoted if it is not in the form of an Icon
default initial record while identifier.
do invocable repeat Included files may be nested to arbitrary depth, but a file may not include
else link return itself either directly or indirectly. File names are looked for first in the current
end local static directory and then in the directories listed in the environment variable LPATH.
371
372 Preprocessing Appendix B Appendix B Preprocessing 373
Relative paths are interpreted in the preprocessor's context and not in relation The current definition of name is removed, allowing its redefinition if desired. It
to the including file's location. is not an error to undefine a nonexistent name.
Conditional Compilation
Control Structures
Conditional compilation directives have the form
$ifdef name
and
$ifndef name Icon's control structures are summarized in this appendix. Most are introduced
$ifdef or $ifndef cause subsequent code to be accepted or skipped, depending on
by reserved words.
whether name has been previously defined. $ifdef succeeds if a definition exists; Control structures are expressions, and as such they can produce results,
$ifndef succeeds if a definition does not exist. The value of the definition does not although some simply fail after performing their intended actions. The notation
matter. used to introduce each control structure indicates its possible result sequence:
A conditional block has this general form: cstruct no result (always fails)
$ifdef name or $ifndef name cstruct : n at most one null result
... code to use if test succeeds ...
cstruct : a at most one result, any type
$else
... code to use if test fails ... cstruct : a1, a2,... multiple results possible
$endif
Some descriptions refer to related Icon operators, which may be found in
The $else section is optional. Conditional blocks can be nested provided that all AppendixD.
of the $if/$else/$endif directives for a particular block are in the same source
file. This does not prevent the conditional inclusion of other files via $include as
long as any included conditional blocks are similarly self-contained. break expr: a - break out of loop
break expr exits from the enclosing loop and produces the outcome of
Error Directives expr.
Default: expr &null
An error directive has the form
See also: next
$error text
An error directive forces a fatal compilation error displaying the given text. This case expr of { ... } : a - select according to value
typically is used with conditional compilation to indicate an improper set of
definitions. case expr of {... }produces the outcome of the case clause that is selected
by the value of expr. It fails if expr fails or if no case clause is selected.
375
376 Control Structures Appendix C Appendix C Control Structures 377
every expr1 do expr2 - generate evety result Default: exprl &null (only if the do clause is omitted)
every exprl do expr2 evaluates expr2 for each result generated by exprl; See also: fail and return
it fails when exprl does not produce a result. The do clause is optional.
until expr1 do expr2 - loop until result
fail - fail from procedure
until exprl do expr2 evaluates expr2 each time exprl fails; it fails when
fail returns from the current procedure, causing the call to fail. exprl succeeds. The do clause is optional.
See also: return and suspend See also: while exprl do expr2
if expr1 then expr2 else expr3 : a - select according to outcome while expr1 do expr2 - loop while result
if exprl then expr2 else expr3 produces the outcome of expr2 if exprl while exprl do expr2 evaluates expr2 each time exprl succeeds; it fails
succeeds, otherwise the outcome of expr3. The else clause is optional. when exprl fails. The do clause is optiona1.
See also: until exprl do expr2
next - go to beginning of loop
next transfers control to the beginning of the enclosing loop. expr11 expr2: a1, a2, ... - evaluate alternatives
See also: break exprll expr2 generates the results of exprl followed by the results of expr2.
Operators
Prefix Operators
+N : N - compute positive
+N produces the numeric value of N.
See also: N1 + N2
-N : N - compute negative
-N produces the negative of N.
See also: N1 - N2
379
380 Operators Appendix D Appendix 0 Operators 381
N1 - N2 : N3 - compute difference
!a : a1, a2, ..., an - generate values
N1 - N2 produces the difference of N1 and N2.
If a is a file, !a generates the remaining lines of a.
See also: -N
If a is a string, !a generates the one-character substrings of a and
produces variables if a is a variable.
N1 * N2 : N3 - compute product
If a is a list or record, !a generates the elements of a from beginning to
end. N1 * N2 produces the product of N1 and N2.
If a is a set, !a generates the members of a in no predictable order. See also: *a
If a is a table, !a generates the elements of a1 in no predictable order.
N1/ N2: N3 - compute quotient
Returned elements of lists, records, and tables are variables.
N1 f N2 produces the quotient of N1 and N2.
See also: key() and a ! A
See also: fa
la : a - check for null value
N1 % N2 : N3 - compute remainder
/a produces a if the value of a is the null value, but fails otherwise. It
produces a variable if a is a variable. N1 % N2 produces the remainder of N1 divided by N2. The sign of the
result is the sign of N1.
See also: N1 / N2
382 Operators Appendix D Appendix D Operators 383
R • f: a - get field of record The value comparison operators produce a2 if the condition is satisfied,
but fail otherwise.
R .f produces a variable for the f field of record R.
See also: .a a1 := a2 : a1 - assign value
a1 := a2 assigns the value of a2 to a1 and produces the variable a1.
a1 & a2 : a2 - evaluate in conjunction
See also: a1 op:= a2, a1 :=: a2, and a1 <- a2
a 1 & a2 produces a2. It produces a variable if a2 is a variable.
a1 op:= a2 - augmented assignment
a 1 op:= a2 performs the operation a1 op a2 and assigns the result to a1;
it produces the variable a1. For example, i1 +:= i2 produces the same
384 Operators Appendix D Appendix D Operators 385
result as i1 := i1 + i2. There are augmented assignment operators for all a[a1] : a2 - subscript
infix operations except assignment operations.
If a is a string, a[a1] produces a one-character string consisting of
See also: a 1 := a2 character a 1 of a.
If a is a list or record and a1 is an integer, a[a1] produces element a1 of
a1 :=: a2 : a1 - exchange values a.
a 1 :=: a2 exchanges the values of a 1 and a2 and produces the variable a 1. If a is a record and a 1 is a string, a[a 1]produces the field of a whose name
See also: a1:= a2 and a1 <-> a2 is a1.
If a is a table, a[a1] produces the element corresponding to key a1 of a.
a1<- a2 : a1 - assign value reversibly In all cases, a1 may be nonpositive.
a 1 <- a2 assigns the value of a2 to a 1 and produces the variable a 1. Except when a1 is a string that is not a variable, a1 [a2] produces a
It reverses the assignment if it is resumed. variable.
See also: a 1 := a2 and a 1 <-> a2 In all cases, the subscripting operation fails if the subscript is out of range.
See also: a[a1, a2, ... , an], a[i1 :i2], a[i1+:i2], and a[i1-:i2]
a1 <-> a2 : a1 - exchange values reversibly
a[a1, a2, ... , an] : am - multiple subscript
a1 <-> a2 exchanges the values a1 and a2 and produces the variable a1.
It reverses the exchange if it is resumed. a[a1, a2, ... , an] is equivalent to a[a1][a2] ...[an].
link graphics incorporates all procedures listed here with the exception of the
Defaults: w, h to edge of window
turtle graphics library (which must be linked explicitly).
The Icon program library is constantly evolving and expanding. This Clone(W1, W2, 51, 52, •••, 5n) : W3 - create new context
appendix lists the stable set ofcore procedures thatis most important in graphics
programming. It includes Icon's built-in graphics procedures and all library CloneO produces a new window value that combines the canvas of W1
procedures used in this book. For information about the full library, see Griswold with a new graphics context. The new graphics attributes are copied
and Townsend (1996). from W2 and modified by the arguments of CloneO. If W2 is omitted,
graphics attributes are copied from W1. If W1 is omitted, the subject
ActiveO : W - produce active window window is cloned. Invalid arguments produce failure or a run-time error
as in WAttribO.
ActiveO returns a window that has one or more events pending, waiting
See also: CoupleO, SubWindowO, and WAttribO
if necessary. Successive calls avoid window starvation by checking the
open windows in a different order each time. Active() fails if no window
is open. Color(W, i, k1, ......) : k2 - set or query mutable color
See also: PendingO ColorO returns the setting of mutable color i if k1 is omitted. If k1 is
supplied, color iis changed as specified, with an immediate effect on any
Alert(W) : W - alert user visible pixels of that color. Additional index and color pairs may be
supplied to set multiple entries with one call. ColorO fails if a color
AlertO produces a beep or other signal to attract attention. specification is invalid.
See also: NewColorO
Bg(W, k1) : k2 - set or query background color
8g0 returns the background color. If k1 is supplied, the color is first set ColorDialog(W, L, k, p, a) : 5 - display color selection dialog
to that specification; failure occurs if the request cannot be satisfied.
ColorDialogO displays a color selection dialog box with Okay and Cancel
Setting the background color does not change the appearance of the
buttons. The box is headed by zero or more captions specified by the list
window, but subsequent drawing operations that use the background
L, or a single string argument if passed in place of a list. If k is supplied,
color are affected.
it specifies a reference color to be displayed below the color being
See also: EraseArea(), Fg(), and FreeColor() adjusted.
If a callback procedure p is supplied, then pea, s) is called whenever the
CenterString(W, x, y, s) : W - draw centered string color settings are adjusted. The argument a is an arbitrary value from the
CenterString() draws a text string that is centered vertically and horizon- ColorDialogO call; s is the new color setting in the form returned by
tally about (x,y). ColorValueO·
Link: gpxop The color initially is set to k, if supplied, or otherwise to the foreground
color.
See also: DrawStringO, LeftStringO, and RightStringO
The final color setting, in ColorValueO form, is stored in the global
Clip(W, x, y, W, h) : W - set clipping rectangle variable dialog_value. ColorDialogO returns the name of the button that
was selected.
CIiPO sets the clipping region to the specified rectangle; subsequent
Default: L "Select color:"
output extending outside its bounds is discarded. If CliPO is called with
no arguments, clipping is disabled and the entire canvas is writable. Link: dialog
390 Procedures Appendix E Appendix E Procedures 391
ColorValue(W, k) : s - translate color to canonical form DrawCurve(W, x1, y1, x2, y2, ..., xn, yn) : W - draw curve
ColorValueO interprets the color k and returns a string of three comma- DrawCurveO draws a smooth curve through the points given as argu-
separated integer values denoting the color's red, green, and blue ments. If the first and last point are the same, the curve is smooth and
components. ColorValueO fails if k is not a valid color specification. closed through that point.
See also: DrawLineO and DrawPolygonO
CopyArea(W1, W2, x1, y1, w, h, x2, y2) : W1 - copy rectangle
CopyAreaO copies a rectangular region (x1, y1, w, h) of window W1 to Drawlmage(W, x, y, s) : i-draw rectangular figure
location (x2, y2) on window W2. If W2 is omitted, W1 is used as both
DrawlmageO draws an arbitrarily complex figure in a rectangular area
source and destination. If W1 is omitted, the subject window is used.
at (x,y). s has one of these forms:
Defaults: x1, y1 upper-left pixel
"width,palette,data" character-per-pixel image
w, h to edge of window
"width,#hexdigits" bi-Ievel image
x2, y2 upper-left pixel
"width,-hexdigits" transparent bi-Ievel image
Couple(W1, W2) : W3 - couple canvas and context DrawlmageO normally returns the null value, but if some colors cannot
be allocated, it returns the number of colors that cannot be allocated.
CoupleO produces a new window value that binds the canvas of W1
Defaults: x, y upper-left pixel
with the graphics context of W2. Both arguments are required.
See also: PatternO and ReadlmageO
See also: CloneO and WAttribO
DrawLine(W, x1, y1, x2, y2, ..., xn, yn) : W - draw line
DrawArc(W, x, y, w, h, theta, alpha, ......): W - draw arc
DrawLineO draws line segments connecting a list of points in succession.
DrawArcO draws an arc of the ellipse inscribed in the rectangle specified
by (x, y, w, h). The arc begins at angle theta and extends by an angle alpha. See also: DrawCurveO, DrawPolygonO, and DrawSegmentO
Defaults: x, y upper-left pixel
w, h to edge of window DrawPoint(W, x, y, ......): W - draw point
theta 0
DrawPointO draws a point at each coordinate location given.
alpha 21t
See also: DrawCircleO and FiIIArcO DrawPolygon(W, x1, y1, ..., xn, yn): W - draw polygon
DrawStringO draws a string of characters without altering the location of See also: 890, FreeColorO, and ShadeO
the text cursor. The integer x specifies the left edge of the first character,
and y specifies the baseline. FiIIArc(W, x, y, W, h, theta, alpha, ......) : W - draw filled arc
FillArcO draws a filled arc of the ellipse inscribed in the rectangle
Enqueue(W, a, x, y, s, i): W - append event to queue specified by (x, y, w, h). The arc begins at angle theta and extends by an
EnqueueO adds event a to the window event list with an event location angle alpha.
of (x,y). The string s specifies a set of modifier keys using the letters c, m, Defaults: x, y upper-left pixel
and s to represent &control, &meta, and &shift, respectively. i specifies a w, h to edge of window
value for &interval, in milliseconds. theta 0
Defaults: a &null alpha 21t
x 0 See also: DrawArcO and FillCircleO
y 0
1111
S
FiIICircle(W, x, y, r, theta, alpha, ......) : W - draw filled circle
i 0
Link: enqueue FiIICircleO draws a filled arc or circle of radius r centered at (x,y). theta is
the starting angle, and alpha is the extent of the arc.
See also: PendingO
Defaults: theta 0
alpha 21t
EraseArea(W, x, y, w, h, ......) : W - clear rectangular area
See also: DrawCircleO and FillArcO
EraseAreaO fills a rectangular area with the background color.
Defaults: x, y upper-left pixel FiliPolygon(W, x1, y1, x2, y2, ..., xn, yn) : W - draw filled polygon
w, h to edge of window
FiIIPolygonO draws and fills the polygon formed by connecting the given
See also: FiIIRectangleO points in order, with x1 ,y1 following xn,yn.
See also: DrawPolygonO
394 Procedures Appendix E Appendix E Procedures 395
FiIIRectangle{W, x, y, w, h, ......) : W - draw filled rectangle GotoXY(W, x, y): W - move text cursor to coordinate position
FiIIRectangle() draws a filled rectangle. GotoXYO sets the text cursor position to the specified coordinate posi-
tion.
Defaults: x, y upper-left pixel
w, h to edge of window Defaults: x, y 0, 0
See also: DrawRectangleO and EraseAreaO See also: GotoRCO
Font{W, 51) : 52 - set or query text font LeftString(W, x, y, 5) : W - draw left-justified string
FontO returns the text font. If s1 is supplied, the font is first set to that LeftStringO draws a text string that is left-justified at position x and
specification; failure occurs if the request cannot be satisfied. centered vertically about y.
Link: gpxop
FreeColor{W, k, ......): W - free color
See also: CenterStringO, DrawStringO, and RightStringO
FreeColorO informs the graphics system that the color k no longer
appears in the window. This may allow the system to reclaim some Lower(W) : W - lower window to bottom of window stack
resources. Unpredictable results can occur if the color is still present in
the window. LowerO sets a window to be "below" all other windows, causing it to
become obscured by windows that overlap it.
See also: 890, F90, and NewColorO
See also: RaiseO
GetEvent5{R, p1, p2, p3) : a - get events
NewColor(W, k) : i-allocate mutable color
GetEvents() repeatedly calls ProcessEvent(R, p1, p2, p3). GetEvents()
does not return. NewColorO allocates a changeable entry in the color map and returns a
small negative integer that serves as a handle to this entry. If k is
Link: vidgets
supplied, the color map entry is initialized to that color. NewColorO fails
See also: ProcessEvent() if no mutable entry is available.
See also: ColorO and FreeColorO
GotoRC{W, i1, i2) : W - move text cursor to row and column
GotoRCO sets the text cursor position to row i 1 and column i2, where the Notice(W, 51, 52, ..., 5n) : 5m - display strings and await response
character position in the upper-left corner of the window is 1,1 and
NoticeO posts a dialog box with an Okay button and returns "Okay" after
calculations are based on the current font attributes.
response by the user. Each string sn is displayed centered on a separate
Defaults: x, y 1, 1 line in the dialog box.
See also: GotoXY0 Link: dialog
See also: TextDialogO
396 Procedures Appendix E Appendix E Procedures 397
OpenDialog(W, 51,52, i) : 53 - display dialog for opening file PaletteKey(W, s1, k) : s2 - return character of closest color in palette
OpenDialogO displays a dialog box allowing entry of a text string of up PaletteKeyO returns the character indexing the color of palette s 1 that is
to i characters, normally a file name, along with Okay and Cancel closest to the color k.
buttons. 51 supplies a caption to be displayed in the dialog box. 52 is used
Default: s1 "c1"
as the initial value of the editable text string. The final text string value
is stored in the global variable dialog_value. OpenDialogO returns the See also: PaletteCharsO, PaletteGraysO, and PaletteColorO
name of the button that was selected.
Defaults: 51 ·Open:" Pattern(W, s) : W - set fill pattern
1111
52 PatternO sets a pattern to be used for drawing when the fill style is set to
i 50 "masked" or "textured". s can be a known pattern name or a specification
Lin1e dialog of the form "width,#data" where the data is given by hexadecimaldigits.
PatternO fails in the case of a bad specification or unknown name.
See also: SaveDialogO and TextDialogO
See also: DrawlmageO
PaletteChar5(W, 51) : 52 - return characters of color palette
Pending(W) : L - produce event list
PaietteChar50 returns the string of characters that index the colors of
palette 51. PendingO returns the list that holds the pending events of a window. If
no events are pending, this list is empty.
Default: 51 "c1"
See also: EnqueueO and EventO
See also: PaletteColorO, PaietteGraY50, and PaletteKeyO
Rai5e(W) : W - raise window to top of window stack SelectDialog(W, L1, L2, 51, L3, i) : 52 - display selection dialog
RaiseO sets a window to be "above" all other windows, so that it is not SelectDialogO constructs and displays a dialog box and waits for the
obscured by any other window. user to select a button. The box contains zero or more captions specified
by the list L1, zero or more radio buttons specified by L2 and 51, and one
See also: LowerO or more buttons specified by L3. i specifies the index of the default
button, with a value of 0 specifying that there is no default button. Any
Readlmage(W, 51, x, y, 52) : i-load image file of the list arguments Ln can be specified by a single nonnull value which
is then treated as a one-element list.
ReadlmageO loads an image from file s 1, placing its upper-left corner at
x,y. If a palette s2 is supplied, the colors ofthe image are mapped to those For the radio buttons, L2 specifies the button names and 51 specifies the
of the palette. ReadlmageO fails if it cannot read an image from file s1. name for the default button. If L2 is omitted, there are no buttons.
It normally returns the null value, but if some colors cannot be allocated, SelectDialogO returns the name of the button that was selected to
it returns the number of colors that cannot be allocated. dismiss the dialog. The global variable dialog_value is assigned the
Defaults: x, y upper-left pixel name of the selected radio button.
See also: DrawlmageO and WritelmageO Defaults: L1 []
L2 []
L3 ["Okay", "Cancel"]
RightString(W, x, y, 5) : W - draw right-justified string
1
RightStringO draws a text string that is right-justified at position x and Link: dialog
centered vertically about y.
See also: TextDialogO and ToggleDialogO
Link: gpxop
See also: CenterStringO, DrawStringO, and LeftStringO Shade(W, k) : W - set foreground for area filling
ShadeO sets the foreground color to kon a color or grayscale display. On
SaveDialog(W, 51,52, i) : 53 - display dialog for saving file
a bi-Ievel display, it sets the fill style to textured and installs a dithering
SaveDialogO displays a dialog box allowing entry of a text string of up pattern that approximates the brightness of color k.
to i characters, normally a file name, along with Yes, No, and Cancel Link: color
buttons. s1 supplies a caption to be displayed in the dialog box. s2 is
used as the initial value of the editable text string. The final text string See also: FgO
value is stored in the global variable dialog_value. SaveDialogO returns
the name of the button that was selected. SubWindow(W, x, y, w, h): W - clone a subwindow
Defaults: s1 "Save:" SubWindowO produces a subwindow by creating and reconfiguring a
s2 1111
clone of the given window. The original window is not modified. In the
i 50 clone, which is returned, clipping bounds are set by the given rectangle
Link: dialog and the origin is set at the rectangle's upper-left corner.
See also: OpenDialogO and TextDialogO Link: wopen
Defaults: x, y upper-left pixel
w, h to edge of window
See also: WOpenO
400 Procedures Appendix E Appendix E Procedures 401
ToggleDialogO returns the name of the button that was selected to TSave() saves the turtle window,location, heading, and scaling factor on
dismiss the dialog. The global variable dialog_value is assigned a list an internal stack.
containing the states of the toggle buttons. Link: turtle
Defaults: L1 [] See also: TRestore()
L2 []
L3 []
L4 ["Okay·, "Cancel"]
i 1
404 Procedures Appendix E Appendix E Procedures 405
WAttrib{W, 51, 52, ..., 5n) : a1, a2, ..., an - set or query attributes WFlush(W) : W - flush pending output to window
WAttribO sets and generates window attribute values. Each string of the WFlushO forces the execution of any window commands that have been
form name=va[ue sets a value. A string with just a name is an inquiry. buffered internally and not yet executed.
First, any requested values are set. Then, WAttribO generates the values
See also: flushO, WCloseO, WDelayO, and WSyncO
of all referenced attributes. Each value has the data type appropriate to
the attribute it represents. WAttribO ignores illegal values, producing no
result; if all values are illegal, WAttribO fails. WOpen(s1, s2, ..., sn) : W - open and return window
WOpenO creates and returns a new window having the attributes
WCI05e{W) : W - close window specified by the argument list. Invalid arguments produce failure or
error, as in WAttribO. If &window is null, the new window is assigned as
WClo5eO closes a window. The window disappears from the screen, and
the subject window.
all bindings of its canvas are rendered invalid. Closing the subject
window sets &window to the null value. Link: wopen
Link: wopen See also: openO, WAttribO, SubWindowO, and WCloseO
See also: c105eO, UncoupleO, WFlu5hO, and WOpenO
WQuit(W) : W - check for "quit" event
WDefault{W, 51, 52) : 53 - get default value from environment WQuitO consumes events until a q or Q is entered, at which point it
returns. If the event queue is exhausted first, WQuitO fails.
WDefaultO returns the value of option 52 for the program named 51 as
registered with the graphics system. If no such value is available, or if the Link: wopen
system provides no registry, WDefaultO fails. See also: WDoneO
408 Procedures Appendix E Appendix E Procedures 409
WRead(W) : s - read line from window WWrite(W, s1, s2, ..., sn): sn - write line to window
WReadO accumulates characters typed in a window until a newline or WWriteO writes a string to a window at the text cursor position. The area
return is entered, then returns the resulting string (without the newline behind the written text is set to the background color. Newline, return,
or return). Backspace and delete characters may be used for editing. The and tab characters reposition the cursor. An implicit newline is output
typed characters are displayed in the window if the echo attribute is set. following the last argument.
Link: wopen Link: wopen
See also: readO, EventO, and WReadsO See also: writeO, DrawStringO, and WWritesO
WReads(W, i) : s - read characters from window WWrites(W, s1, s2, ..., sn) : sn - write partial line to window
WReadsO returns the next i characters typed in a window. Backspace WWriteO writes a string to a window at the text cursor position. The area
and delete characters may be used for editing prior to entry of character behind the written text is set to the background color. Newline, return,
i. The typed characters are displayed in the window if the echo attribute and tab characters reposition the cursor. Unlike WWriteO, no newline is
is set. added.
Default: 1 Link: wopen
Link: wopen See also: writesO, DrawStringO, and WWriteO
See also: readsO, EventO, and WReadO
Basic Procedures
Writelmage(W, s, x, y, W, h) : W - write image to file
The procedures listed here are basic in the sense that they do not involve
WritelmageO writes an image of the rectangular area (x,y,w,h) to the file graphics. No link declarations are needed for access; all are built into Icon.
s. It fails if s cannot be written or if the specified area, after clipping by
the window's edges, has a width or height of zero. The file is normally abs(N1) : N2 - compute absolute value
written in GIF format, but some forms of file names may select different
formats on some graphics systems. absO produces the absolute value of N1.
Defaults: x, y upper-left pixel
w, h to edge of window acos(r1) : r2 - compute arc cosine
See also: ReadlmageO acosO produces the arc cosine of r1 in the range of 0 to 1t for r1 in the range
of -1 to l.
WSync(W) : W - synchronize with server See also: cosO
W8yncO synchronizes the program with the graphics server on a client-
server graphics system, returning after all pending output has been any(c, s, i1, i2) : i3 - locate initial character
processed. On systems that maintain synchronizationat all times, W8ync() anyO succeeds and produces the position after the first character of
has no effect. s[i1 :i2] if that character is in c. It fails otherwise.
See also: WFlushO Defaults: s &subject
i1 &pos if s is defaulted, otherwise 1
i2 0
410 Procedures Appendix E Appendix E Procedures 411
detab(51 i1, i2, ..., in) : 52 - replace tabs by blanks Defaults: s2 &subject
i1 &pos if s2 is defaulted, otherwise 1
detabO produces a string based on s1 in which each tab character is
i2 0
replaced by one or more blanks. Tab stops are at i1, i2, "0' in, with
additional stops obtained by repeating the last interval. See also: balO, matchO, and uptoO
Default: i1 9
flU5h(f) : f - flush output
See also: entabO
flushO flushes any accumulated output for file f.
dtor(r1) : r2 - convert degrees to radians See also: closeO
dtorO produces the radian equivalent of r1 given in degrees.
get(L) : a - get value from list
See also: rtodO
getO produces the left-most element of Land removes it from L, but fails
entab(51, i1, i2, ..., in) : 52 - replace blanks by tabs if L is empty. get is a synonym for pop.
See also: popO, pullO, pushO, and putO
entabO produces a string based on s1 in which runs of blanks are
replaced by tabs. Tab stops are at i1, i2, ... , in, with additional stops
obtained by repeating the last interval. getenv(51) : 52 - get value of environment variable
Default: i1 9 getenvO produces the value of the environment variable s 1, but fails if
s1 is not set or if environment variables are not supported.
See also: detabO
Default: normal exit (machine dependent) See also: icomO, iorO, ishiftO, and ixorO
numerieO produces an integer or real number resulting from converting pos(i 1) : i2 - test scanning position
a, but fails if the conversion is not possible.
pos() produces &pos if i1 or its positive equivalent is equal to &pos but
See also: integerO and realO
fails otherwise.
See also: &pos and &subject
open(51, 52) : f - open file
openO produces a file resulting from opening s1 according to options pull(L) : a - pull from list
given in s2, but fails if the file cannot be opened. The options are:
pull() produces the right-most element of L and removes it from L, but
character effect fails if L is empty.
II
rn open for reading See also: get(), pop(), push(), and put()
II
W" open for writing
"all open for writing in append mode push(L, a1, a2, ... an) : L - push onto list
"b" open for reading and writing
·e" create file push() adds a1 , a2, ..., an to the left end of Land produces L. Values are
IIgil open window for graphics added to the left in the order a1, a2, ... , an, so an becomes the left-most
lip" open a pipe to or from command s1 (not available element of L. If no value to add is given, a null value is added.
on all platforms) See also: get(), pop(), pull(), and put()
"t" translate line termination sequences to linefeeds
"u" do not translate line termination sequences to
linefeeds put(L, a1, a2, ... an) : L - put onto list
put() adds a1, a2, ..., an to the right end of Land produces L. If no value
The default mode is to translate line termination sequences to linefeeds to add is given, a null value is added.
on input and conversely on output. The untranslated mode should be
used when reading and writing binary files. See also: get(), pop(), pull(), and push()
418 Procedures Appendix E Appendix E Procedures 419
readsO produces a string consisting of the next i characters from f, or the Defaults: i 1
52 II II (blank)
remaining characters of f if fewer remain, but fails on an end of file. In
readsO, unlike readO, line termination sequences have no special signifi- See also: centerO and leftO
cance. readsO should be used for reading binary data.
Defaults: f &input rtod(r1) : r2 - convert radians to degrees
1
rtodO produces the degree equivalent of r1 given in radians.
See also: readO
See also: dtorO
sinO produces the sine of r1 given in radians. See also: exitO and writeO
sort(A, i) : L - sort structure stringO produces a string resulting from converting a, but fails if the
conversion is not possible.
sortO produces a list containing values from A. If A is a list, record, or set,
sortO produces the values of A in sorted order. If A is a table, sortO
system(s) : i-call system function
produces a list obtained by sorting the elements of A, depending on the
value of i. For i = 1 or 2, the list elements are two-element lists of key/ systemO calls the C library function system to execute S and produces the
value pairs. For i =3 or 4, the list elements are alternative keys and values. resulting integer exit status. This procedure is not available on all
Sorting is by keys for i odd, by values for i even. platforms.
If Acontains multiple types, the elements ofeach type are sorted together
and the types are sorted in this order: null, integer, real, string, cset, file, tab(i) : s - set scanning position
procedure, list, set, table, and finally record types.
tabO produces &subject[&pos:i] and assigns i to &pos, but fails if i is out
Default: 1 of range. It reverses the assignment to &pos if it is resumed.
See also: sortf 0 See also: moveO
where(f) : i-produce position in file Keywords in Icon are global names that have a special notation (an identifier
preceded by an ampersand) and sometimes have special behavior. Some key-
whereO produces the current byte position in f. The first byte in the file words can be assigned a value; these variable keywords are indicated in the
is at position 1. individual descriptions.
See also: seekO
&ascii : c - ASCII characters
write(a1, a2, ..., an) : an - write line The value of &ascii is a cset consisting of the 128 ASCII characters.
writeO writes strings a1, a2, ... , an with a line termination sequence
added at the end. If ai is a file, subsequent output is to ai. Initial output &clock : s - time of day
is to standard output.
The value of &clock is a string consisting of the current time of day in
Default: ai 1111 (empty string) the form hh:mm:ss, as in "19:21 :00".
See also: writesO
&col : i-mouse column
writes(a1, a2, ..., an) : an - write string The value of &col is normally the column location of the mouse at the
writesO writes strings a1, a2, ... , an without a line termination sequence time of the last received window event. If a window is open, &col also can
added at the end. If ai is a file, subsequent output is to ai. Initial output be changed by assignment, which affects &x, or as a side effect of
is to standard output. assignment to &x.
Default: ai 1111 (empty string)
&control : n - state of control key during window event
See also: writeO
The value of &control is the null value if the control key was depressed
at the time of the last received window event; otherwise, a reference to
&control fails.
&pos : i-scanning position &shift : n - state of shift key during window event
The value of &pos is the position of scanning in &subject. The scanning The value of &shift is the null value if the shift key was depressed at the
position may be changed by assignment to &pos. Such an assignment time of the last received window event; otherwise, a reference to &shift
fails if it is out of range of &subject. fails.
The value of &x is normally the x-coordinate of the mouse at the time of Initial attribute settings can be passed as arguments to WOpenO or
the last received window event. If a window is open, &x also can be CloneO. For an existing window, attributes can be read or written by calling
changed by assignment, which affects &col, or as a side effect of assign- WAttribO. In case of duplicate attributes, the last one applies. Specific procedures
ment to &col. also exist for reading or writing certain attributes; these are noted in the See also
sections of the individual attribute descriptions.
&y : i-mouse y-coordinate In the tables that follow, the letter R indicates attributes that can be read
by WAttribO and the letter W indicates attributes that can be written - either
The value of &y is normally the y-coordinate of the mouse at the time of initially or by calling WAttribO. Writable graphics context attributes also can be
the last received window event. If a window is open, &y also can be set by CloneO.
changed by assignment, which affects &row, or as a side effect of
assignment to &row.
429
430 Window Attributes Appendix G Appendix G Window Attributes 431
The graphics context attribute clipw specifies the width of the clipping Initial value: "off"
region. See also: col, echo, row, x, and y
Initial value: &null (clipping disabled)
depth - number of bits per pixel
See also: cliph, clipx, c1ipy, and CliPO
The read-only canvas attribute depth gives the number of bits allocated
clipx - x-coordinate of clipping region to each pixel by the graphics system.
The graphics context attribute clipx specifies the left edge of the clipping
region. descent - text font descent
434 Window Attributes Appendix G Appendix G Window Attributes 435
The read-only graphics context attribute descent gives the distance, in to the y value of every coordinate pair before interpretation.
pixels, that the current text font extends below the baseline.
Initial value: 0
See also: ascent and fheight
See also: dx
fg - foreground color
displaywidth - width of display screen
The graphics context attribute fg specifies the current foreground color.
The read-only canvas attribute displaywidth gives the width in pixels of
the display screen on which the window is placed. Initial value: "black"
See also: displayheight See also: bg, drawop, gamma, reverse, and FgO
The graphics context attribute pattern specifies the particular pattern to The canvas attribute pointery specifies the vertical position of the mouse
be used for drawing when the fillstyle attribute is set to "textured" or in pixels.
"masked". See also: pointer, pointercol, pointerrow, and pointerx
Values: "black", "verydark", "darkgray", "gray", "Iightgray",
"verylight", "white", "vertical", "diagonal", "horizontal", pos - position of window on display screen
"grid", "trellis", "checkers", "grains", "scales", "waves",
"width,#hexdigits" The canvas attribute pos specifies the window position as a string
containing comma-separated x- and y-coordinates. Attempts to read or
Initial value: "black" write the position fail if the canvas is hidden.
See also: fillstyle and PatternO See also: posx and posy
See also: pointer, pointercol, pointerrow, and pointery Values: "on", "off"
Initial value: "off"
See also: bg, fg, and drawop
440 Window Attributes Appendix G
Appendix H
row - text cursor row
The canvas attribute row specifies the vertical position of the text cursor,
measured in characters.
See also: cot cursor, x, and y Palettes
rows - window height in characters
The canvas attribute rows specifies the number of text lines available
using the current font.
Initial value: 12 Palettes are predefined sets of colors that are used with DrawlmageO. Palettes
See also: columns and height also can be used to limit the colors used by ReadlmageO. These procedures,
along with others for obtaining information about palettes, are described in
Chapter 8.
size - window size in pixels
This appendix documents the contents of Icon's palettes and serves as a
The canvas attribute size specifies the window size as a string containing reference for the programmer. It is difficult, though, to understand a palette just
comma-separated width and height values. by reading about it. The program palette, which displays and labels the colors
Initial value: enough for 12 lines of 80-column text of the palette, provides a clearer introduction.
See also: columns, height, rows, and width Grayscale Palettes
width - window width in pixels Icon's grayscale palettes contain colorless shades that range from black
to white in 1 to 255 equal steps.
The canvas attribute width specifies the width of the window.
For the g2 through g64 palettes, the n shades are labeled from black to
Initial value: enough for 80 columns of text white using the first n characters of this list:
See also: columns, height, and size 0123456789ABC ... XYZabc ... xyz{}
Figure H.I illustrates several of these grayscale palettes, with their labels.
x- text cursor x-coordinate
The canvas attribute x specifies the horizontal position of the text cursor, g2 Small Grayscale Palettes
measured in pixels. g3 2
Grayscale palettes through
See also: col, cursor, row, and y g4 2 3 964 are labeled using print-
g5 3 4 able characters.
g6 345
y - text cursor y-coordinate
g16 01234567 9 ABC 0 E F
The canvas attribute y specifies the vertical position of the text cursor,
g32 01234567 89ABCO E F IJICL.NOPQRSTUV
measured in pixels.
g64 02468ACEGIKMOQSU cegikmoqsuwy{
See also: col, cursor, row, and x 13579BOFHJ lNPRTV dfhjlnprtvlCz}
Figure H.t
441
442 Palettes Appendix H Appendix H Palettes 443
For the 965 through 9256 palettes, the shades are labeled using the first Uniform Color Palettes
n characters of &cset. An example appears in figure H.2.
Programs that compute images can more easily use a palette having
A Larger Grayscale Palette colors that are in some sense "equally spaced". The e2, e3, e4, c5, and e6 palettes
965 \xOO \x05 \n \xOf \x14 \x19 \xl e 2 7 <
\x01 \x06 \v \xlO \x15 \xl a \xl f 3 8 "Unprintable" characters are organized in this way. The larger palettes allow better color selection and
\x02 \x07 \f \xll \x16 \e I 4 9 >
are shown in hexadecimal. subtler shadings but use up more of the limited number of simultaneous colors.
\x03 \b \r \x12 \xl? \xl c + 0 5
\x04 \t \xOe \x13 \x18 \xl d 1 6 @
FigureH.2 For any of these en palettes, the palette provides n levels of each RGB
primary color; letting m = n -I, these levels range from a (off) to m (full on). The
palette also provides all the colors that can be obtained by mixing different levels
The c1 Palette of the primaries in any combination. Mixing equal levels produces black (0,0,0),
white (m,m,m), or a shade of gray. Mixing unequal levels produces colors.
The palette c1, shown in Plate 8.1, is designed for constructing color
images by hand. It is based on Icon's color-naming system and is defined by the Each en palette also provides (n -1)2 additional shades of gray to allow
table below. better rendering of monochrome images. n -1 intermediate shades are added in
each interval created by the original n regular achromatic entries, giving a total
hue deep dark medium light pale weak of n 2 - n + 1 grayscale entries.
black 0 The lists below specify the characters used by each palette. The n3 regular
entries are ordered from (0,0,0) to (m,m,m), black to white, with the blue
gray 1 2 3 4 5
component varying most rapidly and the red component varying most slowly.
white 6 These are followed in the right column by the additional shades of gray from
brown ! p ? C 9 darkest to lightest.
red n N A a # @
orange 0 0 B b $ % e2: kbgermyw x
red-yellow p P C c & I e3: @ABCDEFGHIJKLMNOPQRSTUVWXYZ abed
yellow q Q D d e4: 0123456789ABC ... XYZabe... wxyz{} $%&*-/?@
I
The complete set of grayscale entries in c3, merging regular and extra
entries, is @abMcdZ (from black to white). (For any palette p, PaletteGrays(p)
produces a string that enumerates the merged grayscale entries.) Appendix I
The sizes of c5 and c6 require that they include some nonprinting
characters, so they are better suited for computed images than direct specifica-
tion.
PIate 8.1 shows all the color palettes as displayed by the palette program.
For each of the uniform color palettes, the n3 regular entries appear first. They are
Drawing Details
followed by the grayscale entries, including duplicates from the regular portions
plus the extra grayscale entries.
Sometimes it's important to know exactly which pixels are drawn by graphics
procedures. Experimentation can be helpful, but it also can be misleading
because in some cases the same program can produce different results on
different graphics systems.
This appendix describes some of the finer points of graphical output in
Icon. The specifications given here apply to all graphics systems. Many details
are left unspecified, however; these may vary, depending on the particular
graphics system.
Most of the sections that follow are accompanied by illustrative ex-
amples. Each figure's caption contains the code that was used to draw the figure.
Lines
A line segment includes the two endpoints and all pixels in between. For
slanted lines, the precise meaning of "in between" may vary. A line drawn from
point A to point B is identical to a line drawn from B to A.
If the Iinewidth attribute is set greater than 1, wide lines are drawn. Wide
lines are centered on the path between the endpoints and project beyond the
endpoints by approximately half the line width. If the line width is even, it is
honored even if that requires drawing the line off-eenter in a system-dependent
manner.
When the Iinestyle attribute is set to "dashed" or "striped", the details of
the line breaks are system-dependent.
445
446 Drawing Details Appendix I Appendix I Drawing Details 447
DrawLineO with Two Line Widths For partial circles or ellipses, the measurement of angles may be inexact;
WAttrib(llinewidth=7", "fg=pale gray") tiny gaps may appear between sectors that are mathematically adjacent.
DrawLine(O, 0, 0, 15, 20, 15, 10, 0)
WAttrib(llinewidth=1", "fg=black") DrawCircle() and Tangent Rectangle
DrawLine(O, 0, 0, 15, 20, 15, 10, 0)
o
Fg("pale gray")
DrawRectangle(O, 0, 24, 24)
Figure 1.1 Fg("black")
DrawCircle(12, 12, 12)
Rectangles
Figure 1.3
For outlined rectangles produced by DrawRectangle(x, y, w, h), the
width and height are measured between the centers of the lines surrounding the Filled Figures
rectangle. The points (x, y) and (x + W, Y + h) are always part of the outline. The
interior of the rectangle has dimensions w -linewidth and h -Iinewidth; exterior A filled figure covers all of the pixels in the interior of the corresponding
dimensions are w + linewidth and h + linewidth. outlined figure drawn with a line width of 1. Additionally, the filling procedure
may set none, some, or all of the border pixels. To draw a figure with an outline,
DrawRectangleO with Two Line Widths fill the interiorfirst and then draw the outline. These rules apply for FillRectangleO,
WAttrib(llinewidth=7", "fg=pale gray") FiliPolygonO, FiIICircleO, and FillArcO.
DrawRectangle(O, 0, 20, 15)
WAttrib(llinewidth=1 ,lfg=black")
1
DrawCircle() then FiIICircle()
DrawRectangle(O, 0, 20, 15)
Fg("black")
DrawCircle(12, 12, 12)
Figure 1.2 Fg("pale gray")
FiIlCircle(12, 12, 12)
o
Circles and Arcs FiIlCircle(12, 12, 12)
Fg("black")
A circle produced by DrawCircle(x, y, r) is tangent to the outlined DrawCircle(12, 12, 12)
rectangle produced by DrawRectangle(x - r, y - r, 2 * r, 2 * r). The exact set of
points forming the circle is system-dependent.
Figure 1.5
An outlined ellipse produced by DrawArc(x, y, w, h) is tangent to the
outlined rectangle produced by DrawRectangle(x, y, w, h). The exact set of
points drawn is system-dependent.
448 Drawing Details Appendix I
Rectangular Areas
For many procedures, a rectangular area is specified by four parameters
AppendixJ
(x, y, w, h). If wand h are positive, the point (x, y) is the upper-left comer of the
area, which measures w by h pixels. This means that the point (x + w, Y+ h) is just
outside the rectangle.
If wor h is zero, the rectangle has no area. This is legal with all procedures Keyboard Symbols
except WritelmageO, where it results in failure.
If w or h is negative, the effect is as if x or y (respectively) is adjusted by
that amount and the absolute value of w or h is used as the width or height. In
either case, the original point (x, y) is just beyond the edge of the resulting
rectangle.
For FillRectangleO, this rule is consistent with the general rules for filled Pressing a key on the keyboard produces an Icon event unless the key is a
figures but more precise. modifier key, such as the shift key. Releasing a key does not produce an event.
Keyboard events can be explored using the sample program illustrated in
FiIIRectangle() and DrawRectangle()
Chapter 10 (Figure 10.2), which prints the value of each event along with other
information.
Fg("pale gray")
A key that represents a member of the ASCII character set, including
r - --I FiIiRectangle(O, 0, 30, 20)
traditional actions such as return and backspace, produces a string containing a
Fg("black")
I I WAttrib("linestyle=dashed") single character. The control and shift modifiers can affect the particular charac-
1 ter produced. For example, pressing control-H produces "\b" (the backspace
oJ DrawRectangle(O, 0, 30, 20)
character).
Figure 1.6
Other keys, such as function and arrow keys, produce integer-valued
events. These values may be referenced symbolically by including the defini-
tions contained in the library file keysyms.icn, as in
$include "keysyms.icn"
The following table lists the values of some of the most commonly used
keys.
449
450 Keyboard Symbols Appendix J
451
Appendix L
Vidgets
Vidgets are implemented by Icon records, with a different record type for each
kind of vidget. The set of vidgets is fixed and there are no provisions for adding
new kinds of vidgets.
Vidget Fields
Vidgets have fields that contain their attributes. Some fields are common
to all kinds of vidgets, while some are peculiar to a particular kind of vidget. The
attributes of a vidget can be accessed through these fields.
Every vidget has an id field, which is the identifying name given to the
vidget in VIR
The most commonly used fields are the ones that give the locations and
sizes of vidgets:
ax x coordinate of the upper-left comer of the vidget
ay y coordinate of the upper-left comer of the vidget
aw width of the vidget
ah height of the vidget
The a stands for" absolute". All vidgets except for lines have these attributes.
Lines are specified by their end points:
x1 x coordinate of the beginning of the line
y1 y coordinate of the beginning of the line
x2 x coordinate of the end of the line
y2 y coordinate of the end of the line
Regions also have attributes that give their usable dimensions inside the
decorating border they may have:
453
454 Vidgets Appendix L Appendix L Vidgets 455
ux x coordinate of the upper-left comer of the usable area text list selected lines highlighted
uy y coordinate of the upper-left comer of the usable area text-entry current text displayed
uw width of the usable area
uh height of the usable area slider slider thumb position
scrollbar slider thumb position
Vidget States and Callbacks
Vidget Activation
The following vidgets can have callbacks. Some maintain states; for
these, the values passed generally are the same as their states at the time the A vidget is activated by pressing a mouse button while the mouse
callback occurs. pointer is positioned within the area of the vidget. (Note that the entire area
For text lists, the state always is a list of integers; this differs from the occupied by a vidget may not be visually evident.) For a vidget that has a callback
callback value. The first integer indexes the top line currently displayed; this procedure, the callback occurs in the following situations following the activa-
reflects the position of the scrollbar thumb. Additional integers, if any, index the tion of the vidget:
currently selected items.
vidget callback time
vidget state callback value
button when the mouse button is released
regular button 1 radio buttons when the mouse button is released
toggle button j 1 if on, null if off menu when the mouse button is released with the mouse
radio buttons j text of selected button cursor on a selected item
menu list of selected items text list when the mouse button is released
single-selection text list j selected item, or null if nothing text-entry when return is entered with the mouse pointer
is selected within the field
multiple-selection text list j list of selected items slider if unfiltered, when the mouse is dragged on the
slider; otherwise when the mouse button is re-
text-entry j text entered
leased
slider j numerical value for position scrollbar if unfiltered, when the mouse is dragged on the
scrollbar j numerical value for position slider or released on an end button; otherwise
when the mouse button is released
region event and the x,y coordinates
where it occurred region when any keyboard or mouse event occurs within
the region
The state of some vidgets when they are not activated is indicated
visually: The state of some vidgets is indicated visually while they are activated:
vidget visual indication vidget visual indication
toggle button highlighted if on (foreground and background button highlighted (foreground and background re-
reversed), not highlighted if off versed)
radio buttons selected button highlighted
456 Vidgets Appendix L
457
458 VIS Appendix M Appendix M VIS 459
FigureM.3
The Select menu allows one of the vidgets to be selected for manipula-
tion, as shown in Figure MA.
buttDnl
Every vidget has an identifying name, as shown in this
button2 menu.
cal1brate
-.J line
-.wl
occupati on
references
scroll
511 der boundary 1
The VIB Window Figure M.I sl1der boundary 2
stations
The VIB window has a menu bar at the top. Below this there is a vidget top
bar with icons representing the various kinds of vidgets. The remaining
portion of the VIB window contains the canvas for the interface, which Figure M.4
is indicated by a rectangular area with a box on its lower-right comer.
The File menu provides several services related to files and the overall Vidgets
application, as shown in Figure M.2
VIB provides several kinds of vidgets. These are shown in Figures M.5
The File Menu through M.14.
--_85
The new item creates a new file for an interface. open opens an Buttons
apen existing file. save and save as write interface files. refresh
redraws the canvas. prototype is used to run the interface to There are two kinds of buttons; regular
refresh III buttons thatjust produce callbacks and
pratDtype II' see what it will look like in an application. quit terminates the
quit VIB session. togglebuttons that also maintainstates.
Different styles provide different ap-
Figure M.2 pearances.
Figure M.5
The Edit menu provides services related to manipulating vidgets, as
shown in Figure M.3.
460 VIS Appendix M Appendix M VIS 461
Menus Regions
rne A menu's items are exposed when the Regions are rectangular areas that can
seve 1 :1
accept user events. Regions are avail-
menu's button is pushed. Menus can
{:85 _in>
- >1:Z have submenus. able in several styles to provide differ-
quit 1 :4
.1 :1 ent visual appearances.
FigureM.7 FigureM.12
Sliders
The Application Canvas
Sliders have thumbs that canbe moved
to select a numerical value within a The application canvas is configured by using the box on its lower-right
specified range. Sliders can be vertical
comer. Dragging this box resizes the application canvas. Clicking the right
or horizontal.
mouse button on this box brings up a dialog, as shown in Figure M.tS.
FigureM.I0
462 VIS Appendix M Appendix M VIS 463
H:_==..:",:_ -:_- __ _ ... -. -,- -- - - - ---
f ;' • .'. j
I
Fne Edlt select
procedure _ :
wtndow 1...1: A
wtdth:
Might:
C8nCe1
The VIB window can be resized by using the window manager if it is not
large enough to accommodate the application canvas.
Creating Vidgets
A vidget is created by clicking on its icon in the vidget bar and dragging
it into position on the application canvas. It can be repositioned later. The result A Newly Created Vidget Figure M.16
of creating and placing a slider vidget is shown in Figure M.16.
Although it's usually worthwhile to position a newly created vidget
approximately where you expect you will want it, fine-tuning is best left
until later.
Vidget Attributes
Vidgets have a variety of attributes, some that are common to all vidgets
and some that are specific to the type of the vidget.Every vidget has an ID
attribute. Vidgets are identified by their IDs, both in VIB and in the application
that uses the interface. All vidgets also have a position, given as the x-y
coordinates ofits upper-left comer. Most kinds of vidgets have size attributes as
well. All vidgets except for labels and lines have a callback attribute that names
the application procedure that is called when the user manipulates the vidget.
When a vidget is created, it has default attributes. These attributes can be
changed later.
Clicking with the right mouse button on a vidget brings up a dialog
showing the vidget's attributes. The attribute dialog for a button is shown in
Figure M.17.
464 VIS Appendix M Appendix M VIS 465
This dialog shows the default attribute Three items are pro-
1I11e1:
D: values for a button. Il: 52 vided initially with de-
-JD: 1.1: y: 135
callb8dc: c:allbeck:
fault names. The items
canbe edited, and items
y: can be added and de-
wtdth: leted in the manner
height: used for radio buttons.
FigureM.19
Figure M.17
Clicking on create submenu brings up a dialog for a submenu, as shown
The label attribute is what appears on the button. The radio buttons in the in Figure M.20.
center and the outline button at the right provide choices for the visual appear-
ance of the button. The toggle button allows a choice of a regular button or one Dialog for a Submenu
that maintains an onloff state. The dialog default button is used to designate a Except for the label, ID,
default button that in turn is used to dismiss a custom dialog when the user position, and callback
enters return. attributes, the dialog for
del
Figure M.18 shows that attribute dialog for a set of radio buttons.
"'---'-"--------.....:--...-- dill a submenu is the same
del as for a menu.
Dialog for a Slider The attribute dialog for a region is shown in Figure M.25.
The orientation and dimensions for a Dialog for a Region
m: slider can be changed as indicated.
callback:
ID: IIIIIIIIIIIJ
The dialog of a region provides four
II: 211
y: T1 1:811 bKk: regt Oll.dJl choices for the appearance of the bor-
1engttI: IiO x: 3GS width: 32 invisible
der.
width: lS filter y: 202 2lJ SIricen
+-vertical + grooved
lion zonal raised
Okay C8ncel
Figure M.25
Figure M.23
Okay C8ncel
,I
FigureM.26
-.... - - ...."..--- - . - _. - - .. --_. _.- - .-. Dialog for a Line left edges of vidgets with the left edge of the anchor vidget, while vertical
L . j·H.:i;:"t '
A line vidget differs from other vidgets in alignment aligns the tops.
D: having x,y coordinates for its end points. When an alignment is chosen from the Edit menu, the cursor is changed
X1:
n,
to ¢:::> or depending on the choice of alignment. (These cursor shapes may be
different on some platforms.) Subsequently, clicking on a vidget aligns it with
the anchor vidget. Several vidgets can be aligned in this manner. Clicking on the
FigureM.27
canvas off any vidget restores the cursor to an arrow and its normal functional-
ity.
Manipulating Vidgets Figure M.28 shows four scrollbars that need to be aligned horizontally
with the topmost one selected as the anchor.
Selecting and Deselecting Vidgets
Resizing Vidgets
A vidget can be resized by selecting it, pressing the mouse on one of its
four comers, and dragging that comer to a new location. The diagonally
opposite comer remains anchored, and the size changes. Some vidgets enforce
constraints on resizing. For example, a button cannot be made smaller than its
label. Other vidget types, such as menus, cannot be resized at all.
Custom Dialogs
A custom dialog is very similar to a visual interface for an application:
Vidgets with various functionalities can be positioned as desired.
There are two noticeable differences between custom dialogs and visual
r, "
interfaces:
• Menus, regions, and text lists cannot be used in custom dialogs. They
may be created and placed, but they are ignored when the dialog is
saved.
• A custom dialog must have at least one regular button, so that it can
be dismissed. VIB refuses to save a custom dialog without a regular
button.
VIB must be told that it is creating a custom dialog rather than a visual
interface, which is the default. This is done in the canvas dialog that comes up
as a result of clicking with the right mouse button on the lower-right comer of
the application canvas.
Two things are needed to create a custom dialog: providing a procedure
name by which the dialog will be called and setting the dialog window toggle.
Figure M.30 shows an example.
Aligned Vidgets FigureM.29
Note that the anchor vidget still is selected. Custom Dialog Settings
-I 1·1-1
The procedure name field
Deleting Vidgets procedure _ : IIEIIIIII
wl ndow 1abel : attn butes
is ignored for a visual in-
wldth: 370
terface butis required for
The selected vidget can be deleted by choosing delete from the Edit height: 400 • dialog wlndow a custom dialog.
menu, by pressing the delete key, or by entering @ X. This operation can be
undone by choosing undelete from the Edit menu or by entering @ U, provided Okay cancel
no other action has been performed since the vidget was deleted.
Figure M.30
Copying Vidgets
One button can be designated as the default button for dismissing a
The selected vidget can be copied by choosing copy from the Edit menu custom dialog. This is done by setting the dialog default toggle in the attribute
or by entering @C. dialog for the button, as shown in Figure M.31.
The copied vidget is selected when it is created. It is offset vertically and
horizontally from the vidget from which it was copied. Attributes other than the
ID, callback, and position are inherited from the vidget from which it was
created.
472 VIS Appendix M
y:
lent to clicking on the default button.
Platform Dependencies
width:
Might: cI1alOl1 default
FigureM.31
Microsoft Windows
Prototyping
Icon for Windows runs on pes with Windows 95 and above, Windows
The application canvas as shown by VIB is very similar to the way it NT, and Windows 3.1 (with Win32s). Windows machines vary greatly in their
appears when the application is run. The dashed lines that show the boundary hardware capabilities, so we can't describe precisely how things work in all
of a region with an invisible border do not, of course, show when the visual situations.
interface is used in an application.
The exact appearance of the interface can be obtained by selecting Font Specifications
prototype (@ P) from the File menu. This constructs and launches an application
with the current interface and a dummy program for handling events. Windows comes with very few fonts. The set of fonts available on a given
machine is a function of the set of applications and fonts that have been installed.
Manipulating vidgets on the prototype produces information about As a result, Windows machines vary widely in their interpretation of font
callbacks, IDs, and vidget values. requests. The same specification in Icon can produce fonts of different appear-
A prototype for a visual interface can be terminated by typing q with the ance on different machines.
cursor not on a vidget. The prototype for a custom dialog can be dismissed by Windows' native font specifications are complex structures that specify
clicking on one of its regular buttons. A dialog is presented to confirm that you a set of desired characteristics, from which a "nearest match" is chosen by
wish to terminate the prototype instead of continuing to test it. Windows when a font is requested. Windows has fonts based on different
character sets. The standard Icon font names (fixed, sans, serif, mono, and
Limitations typewriter) return a font that uses the so-called ANSI character set.
VIB has several limitations that should be considered before designing Color Specifications
an interface:
Windows does not provide a built-in set of color names, so Icon's
• VIB can handle only one interface section in a file.
standard color names comprise the complete set of recognized names.
• The location and attributes of vidgets cannot be changed when an
application is running. Depending on the hardware configuration, Windows may use dithered
colors in response to any particular color request. This results in an unattractive
• There is no provision for adding new kinds of vidgets. appearance in applications where solid colors are expected. Most colors are
• There is no provision for decorating vidgets with images. dithered on 16-color machines, and color-intensive applications are ugly or
473
474 Platform Dependencies Appendix N Appendix N Platform Dependencies 475
Icon uses Windows scan codes as integer event codes for special keys. The X Window System provides both tuned and scalable fonts using a
Symbolic definitions given in Appendix J and located in file keysyms.icn allow complex naming scheme. The font chosen by Font("Helvetica,19") may be
applications to refer to these special keys in a system-independent way. designated
Cursors and Pointers -adobe-helvetica-medium-r-normal- -19-0-75-75-p-O-iso8859-1
which is actually a scaled instance of the master font
The text cursor is a slowly flashing solid block. It is visible only when the
cursor attribute is "on" and the program is awaiting input from WReadO or -adobe-helvetica-medium-r-normal- -O-O-O-O-p-o-iso8859-1
WReadsO· Icon translates font specifications into X form, so that this underlying
The pointer attribute can take any of the values shown in Figure N.1. complexity can be ignored by the programmer.
In interpreting a font specification, Icon recognizes the following font
Windows Pointers characteristics and tries to match them as well as possible against the available
The appearance of these pointers X fonts:
+ I
varies somewhat with the version
arrow cross ibeam uparrow wait of Windows used. condensed, narrow, normal, wide, extended
light, medium, demi, bold, demibold
Figure N.1
roman, italic, oblique
Limitations mono, proportional
sans, serif
• The attribute linestyle is ignored by Windows when the line width is
greater than 1; line widths greater than 1 are always drawn using a The same specification can produce fonts of different appearance on different
solid line style. servers.
• The attribute fill style does not support the value "masked". When If a font specification is not understood or matched by Icon's font-
masked fills are requested, textured fills are performed instead. naming system, it is passed verbatim to X as a font name. This allows the use of
native X font specifications, including wild cards. As a special case, a font
• Mutable colors do not work correctly.
specification of "fixed" (without any size or other characteristics) is passed to X
as a font name without interpretation.
476 Platform Dependencies Appendix N Appendix N Platform Dependencies 477
Color Specifications The mouse location indicator, setby the pointer attribute, is selected from
the X cursor font. The default is nleft ptrn. The available values and the corre-
The X implementation of Icon is limited to a maximum of 256 colors at sponding cursor shapes are shown in Figure N.2.
anyone time, even if the hardware supports more.
Color specifications that are not recognized by Icon are passed to X for
interpretation. X servers typically offer large sets of color names, including x X cursor [J dotbox X man 'I:J,J sizing
Color correction is controlled by the gamma attribute. The default value T based a r row down
;( draft large e monse spraycan
of gamma is based on the color returned by X for the device-independent Xcolor J; based a r row np
/
draft small pencil A star
specification RGSi:.5/.5/.5. On older X systems that do not recognize this
boat 1:':1 draped box ,t; pi rate 0 target
specification, a configuration default value is used.
The interpretation of RGSi:.5/.5/.5 depends on properties associated with m bogosity """" exchange + plns + tcross
the root window. These properties are set by the xcmsdb utility. The library I..!::. bottom 1eft co rne r + flenr ? questi on arrow '" top 1eft a r row
program xgamma can be used to set the properties to approximate a particular .::!.I bottom ri ght co rne r '[J" gobbler right ptr r;::; top 1eft corner
gamma value. .±. bottom side 'lit> gnmby --+1 right side
'3i1
top ri ght co rne r
.L bottom tee
Images -#
handl -l right tee
'T"
top side
IJ] box spi ral 'V> hand2 III ri ghtbntton T top tee
In ReadlmageO, if an image file is not a valid GIF file, an attempt is made
center ptr Q heart Ei!J rtl logo trek
to read it as an X Bitmap or X Pixmap file. +
0 ci rcle C icon sailboat r nl angle
In WritelmageO, if the file name ends in .xbm or .xSM, an X Bitmap file
m
-
clock i ron cross sb down a r row nmbrella
is written. If the file name ends in .xpm or .XPM, an X Pixmap file is written. If lfJl 1"
the file name ends in .xpm.Z, a compressed X Pixmap file is written. In all other coffee mng
It
left ptr sb h donble arrow .., nr angle
-
9F cross If- 1eft si de _sb left arrow CZ> watch
labeled "3", "PgDn", and "R15". Whether this key produces an Icon event "3",
Key_PgDn, Key_R15, or even something else, depends on the X configuration.
The library file keysyms.icn lists many of the possible codes. For maxi- X Window Cursors FigureN.2
mum portability, use only those that appear in Appendix J.
X Windows provides many cursors, some of them whimsical. Can you
think of uses for all of them?
Cursors and Pointers
X Resources
The text cursor is an underscore character. It is visible only when the
cursor attribute is "on" and the program is awaiting input in WReadO or Under X, WDefaultO returns values registered with the X Resource
WReadsO. The cursor does not blink and may be difficult to locate in a window Manager. These values often are set by an .Xresources or .Xdefaults file.
containing a large amount of text.
Appendix 0
479
480 Running Icon Appendix 0 Appendix 0 Running Icon 481
The suffix .icn is assumed if none is given, so this can be written more simply as Command-Line Arguments
icont hello
Arguments on the command line following an icode file name are available to
The result is an executable icode file. The name of the icode file depends on the the executing Icon program in the form of a list of strings. This list is the
platform on which Icon is run. On some platforms, notably UNIX, the name is argument to the main procedure. For example, suppose args.icn consists of
the same as the name of the source file, but without the suffix. On these
procedure main(arguments)
platforms, the compilation of hello.icn produces an icode file named hello. For
Microsoft Windows, the name is hello.bat. Other platforms have other naming every write(!arguments)
conventions. end
After compilation, entering This program simply prints the command-line arguments with which it is
hello executed. Thus,
runs the program. icont args
args Hello world
An Icon program can be compiled and run in a single step using the -x
writes
option following the program name. For example,
icont hello -x Hello
world
compiles and executes hello.icn. An icode file also is created, and it can be
executed subsequently without recompiling the source program. When -x is used, the arguments follow it, as in
There are command-line options for icont. Options must appear before icont args -x Hello world
file names on the icont command line. For example,
icont -5 hello Arguments are separated by blanks. The treatment of special characters,
methods of embedding blanks in arguments, and so forth, vary from platform
suppresses informative messages that icont ordinarily produces. to platform.
BLKSIZE (500000) The initial size of the allocated block re- Editing, Compiling, and Executing
gion, in bytes.
IPATH (undefined) Double-clicking the Windows Icon icon launches Wi, the Windows Icon
The location of files specified in link decla-
programming environment. Wi is written in Icon and allows you to edit,
rations. IPATH is a blank-separated list of
compile, and execute programs. To start, select the name of a file to edit, as in
directories. The current directory is always
Figure 0.2:
searched first, regardless of the value of
IPATH.
LPATH (undefined) The location of source files specified in
preprocessor $include directives. LPATH is
a blank-separated list of directories. The
current directory is always searched first,
regardless of the value of LPATH.
MSTKSIZE (10000) The size, in words, of the interpreter stack.
STRSIZE (500000) The initial size of the allocated string region,
in bytes. Fileneme:
TRACE (undefined) The initial value of &trace. FielI 01 JYpe: Icon Sources(".icn)
Frequently Graphics
i#
IPI. Ubrary Uninstall
Asked 0... Facilities Reference Windo...
Figure 0.1
484 Running Icon Appendix 0 Appendix 0 Running Icon 485
end
Icon Resources
The CD-ROM
The CD-ROM that accompanies this book includes almost all Icon
material except books and newsletters. See Appendix Q.
On-Line Access
The Icon home page on the World Wide Web is located at
http://www.cs.arizona.edulicon/
The Icon Web site includes general information about Icon, reference
material, the current status of Icon, implementations, the Icon program library,
documentation, technical support, and so on.
Updates to this book will be posted on the Icon Web site.
The address for anonymous FrP is
ftp.cs.arizona.edu
From there, use cd /icon and get README for instructions on navigating.
Implementations
All implementations of Icon are in the public domain and available as
described in the preceding section.
487
488 Icon Resources Appendix P
AppendixQ
The current version, Version 9, presently is available for the Acorn
Archimedes, the Amiga, Macintosh/MPW, Microsoft Windows, MS-DOS, many
UNIX platforms, VAX/VMS, and Windows NT. Icon's graphics facilities pres-
ently are supported for Microsoft Windows, UNIX, and Windows NT.
About the CD-ROM
Documentation
Documentation on Icon is extensive. In addition to this book, there two
other books devoted to Icon:
The Icon Program Language (Griswold and Griswold, 1996) contains a
description of Version 9.3 of Icon, including a detailed reference manual.
The Implementation of the Icon Programming Language (Griswold and The CD-ROM that accompanies this book contains a vast amount of material
Griswold, 1986) contains a detailed description of how Icon is implemented. related to Icon, including:
Although it describes an earlier version, it still is a useful reference. • programs, procedures, and images from this book
There are two newsletters: • additional example programs and images not found in this book
fJ11e Icon 9{g.wsfetter (Griswold, Griswold, and Townsend, 1978-) is • the Icon program library
published three times a year and contains material of a topical nature, such as • implementations of Icon for Microsoft Windows and UNIX
work in progress and new implementations. This newsletter also is available on
the Icon Web site. • implementations without graphics support for other platforms, in-
cluding the Amiga, MS-DOS, and the Macintosh
mlfe ;Ucon J\nal\!lid (Griswold, Griswold, and Townsend, 1990-) pro- • C source code for the implementation of Icon
vides in-depth coverage of technical matters related to Icon, including program-
ming techniques and applications. • user's manuals, technical reports, and other documentation
There are many technical reports and user manuals for various plat- • Adobe Acrobat Reader for viewing PDF documents
forms. • images and 3-D models depicting Icon program behavior
The newsgroup comp.lang.icon discusses issues related to Icon. There
also is a mailing list connected to the newsgroup via a gateway. To subscribe, How to Use the CD-ROM
send mail to
The CD-ROM can be used on Windows, UNIX, and other platforms. It is
icon-group-request@cs.arizona.edu
like a Web site that is self-contained except for a few links to external Web pages
Information about Icon also is available from related to Icon.
Icon Project All you need to use the CD-ROM is a Web browser, such as Netscape
Department of Computer Science Navigator or Internet Explorer. You do not need an Internet connection unless
The University of Arizona you want to access external sites.
P.O. Box 210077
Tucson, Arizona 85721-0077 Start by launching your browser and opening index.htm at the top level
U.S.A. of the CD-ROM. From there, you can get to everything on the CD-ROM.
Navigational aids are provided.
voice: (520) 621-6613
fax: (520) 621-4246
489
e-mail: icon-project@cs.arizona.edu
490 About the CD-ROM Appendix Q Appendix Q About the CD-ROM 491
The CD-ROM Web page should look something like this (the appearance .TAZ compressed UNIX tar files (.Z format)
varies somewhat from browser to browser): .TGZ gzipped UNIX tar files (.gz format)
.TXT simple text files
8, .WRL VRML 1.0 worlds
View Go Window Help
.ZIP ZIP format compressed archives
Graphics Programming in Icon none generally, simple text files
Web pages, images, text files, and source code can be viewed directly using
Software a Web browser. Some other formats, including PostScript, PDF, and VRML, can
• Icon for Windows be viewed from a browser if the appropriate plug-in or helper application is
• Icon for UNIX
• Icon for other platforms installed. The files that remain are generally not intended for display but rather
• Inside Icon
for other uses. These files can be saved or "downloaded" to your hard disk using
• The Icon program library
• Adobe Acrobat Reader
the browser.
IntroductolY Documentation Most text files incorporate line terminators in the style of MS-DOS (return
• An Overview of Icon (Griswold)
followed by linefeed). This is the standard format under Microsoft Windows.
• A Brief Introduction to Icon (Hanson)
• Frequently Asked Questions
Under UNIX, such files can be read without problems by Icon programs, web
browsers, and many other programs. The return character may be visible when
Reference Documentation
viewing one of these files in a text editor.
• Language Reference
• Program library indexes
• Documentation index
• Books about Icon External Links
From the Graphics Book
• Images and sample code There are a few links on Web pages on the CD-ROM that reference
The Icon Web Site external sites. If you have an Internet connection, you can access these to get
• On the Internet: http://www.cs.arizona.edu/icon/
more information and see what others are doing with Icon. If not, you'll get an
• On this CD- ROM: web site snapshot error message if you try to follow an external link.
Be sure to visit the Icon Web site. It is updated frequently and contains
the latest information and software.
File Formats The external links on the CD-ROM were functional at the time the CD-
ROM was prepared. Realize, however, that Web sites change location and
The CD-ROM contains files in several different formats, with the format of sometimes disappear.
each file indicated by its extension. Most files fall into one of the categories listed
here:
.C and.H C source code
.GIF GIFimages
.HTM HTML documentation ("Web pages")
.ICN Icon source code
.PDF Acrobat documentation (a viewer is supplied)
.PS PostScript documentation
.R and .RI Icon run-time system source code
References
Abelson, Harold, and diSessa, Andrea. 1980. Turtle Geometry. Cambridge, Mass.:
MIT Press.
Apple Computer Inc. 1987. Human Interface Guidelines: The Apple Desktop Interface.
Reading, Mass.: Addison-Wesley. .
Barry, Phillip J., and Goldman, Ronald N. 1988. A Recursive Evaluation Algo-
rithm for a Class of Catmull-Rom Splines. In SIGGRAPH '88 Conference Proceed-
ings. New York: Association for Computing Machinery.
Berk, Toby; Brownston, Lee; and Kaufman, Arie. A New Color-Naming System
for Graphics Languages. IEEE Computer Graphics and Applications, May 1982, 37-
44.
Brennan, Susan E. 1985. Caricature Generator: The Dynamic Exaggeration of
Faces by Computer. Leonardo 18:170-178.
Delahaye, Jean-Paul. 1986. Geometric and Artistic Graphics. London: Macmillan
Education Ltd.
Dewdney, A. K. 1988. Facebender. In The Armchair Universe: An Exploration of
Computer Worlds. New York: W. H. Freeman.
Forman, Yale, et aI., eds. 1980. Colour. London: Grange Books.
Gardner, Martin. 1989. Penrose Tiles to Trapdoor Ciphers. New York: W. H.
Freeman.
Gerritsen, Frans. 1988. Evolution in Color. West Chester, Pa.: Schiffer Publishing.
Griswold, Ralph E., and Griswold, Madge T. 1996. 3d ed. The Icon Programming
Language. San Jose, Calif.: Peer-to-Peer Communications.
Griswold, Ralph E.; Griswold, Madge T.; and Townsend, Gregg M., eds. 1978-.
The Icon Newsletter. Tucson, Ariz.: Department of Computer Science, The Univer-
sity of Arizona and The Bright Forest Company.
493
494 References
Index
Griswold, Ralph E.; Griswold, Madge T.; and Townsend, Gregg M., eds. 1990-.
Symbols &shift 186, 427, 451
The Icon Analyst. Tucson, Ariz.: Department ofComputer Science, The University &subject 427
of Arizona and The Bright Forest Company. $define 42, 51, 372 &time 427
$else 43, 374 &trace 56, 428
Griswold, Ralph E.; Jeffery, Clinton L.; and Townsend, Gregg M. 1996. Version 9.3 $endif 43, 374 &ucase 428
of the Icon Programming Language. Tucson, Ariz.: Department of Computer Sci- $error 374 &version 428
ence, The University of Arizona, IPD278. $ifdef 43, 374 &window 59, 165, 166, 387, 428
$ifndef 374 &x 185, 428, 451
Griswold, Ralph E., and Townsend, Gregg M. 1997. The Icon Program Library: $include 43, 46, 371 &y 185, 428, 451
Version 9.3.1. Tucson, Ariz.: Department of Computer Science, The University of $Iine 372 ! (element generation) 25, 27, 28, 380
Arizona, IPD283. $undef 43, 372 ! (procedure invocation) 37, 386
&ascii 423 % (remainder) 22, 48, 381
Hope, Augustine, and Walch, Margaret. 1990. The Color Compendium. New York: &c1ock 423 & (conjunction) 11, 49, 382
Van Nostrand Reinhold. &col 186, 423 o (grouping) 49, 51
Jeffery, Clinton L. 1997. Version 9 of Icon for Microsoft Windows. Tucson, Ariz.:
&control 186, 423, 451
&cset 423
o (procedure invocation) 386
* (product) 22, 48, 381
Department of Computer Science, The University of Arizona; and San Antonio, &date 424 * (size) 25, 27, 28, 31, 380
&dateline 424 ** (intersection) 27, 382
Texas: Department of Computer Science, The University of Texas atSan Antonio;
&digits 30, 424 + (positive) 379
IPD271. &dump 56, 424 + (sum) 22, 48, 381
Kain, Richard Y. 1972. Automata Theory: Machines and Languages. New York: &e 102, 424 ++ (union)
&errout 40, 424 - (difference) 22, 48, 381
McGraw-Hill. &fail 424 - (negative) 22, 379
Lasseter, John. 1987. Principles of Traditional Animation Applied to 3D Com- &features 373, 424 -- (difference) 27, 382 27, 382
&host 424 . (dereferencing) 381
puter Animation. In SIGGRAPH '87 Conference Proceedings. New York: Associa- &input 40, 424 . (field reference) 24, 48, 382
tion for Computing Machinery. &interval 187, 425, 451 .bat files 44, 480
&Icase 425 .icn files 44, 479
Laurel, Brenda, eel. 1990. The Art of Human-Computer Interface Design. Reading,
&Idrag 184, 425 .u1 / .u2 files 45
Mass.: Addison-Wesley. &Ietters 30, 425 .Xdefaults 477
Lauwerier, Hans. 1991. Fractals: Endlessly Repeated Geometric Figures. Princeton, &Ipress 184, 425 .Xresources 477
&Irelease 184, 425 / (null test) 21, 380
N.J.: Princeton University Press. &mdrag 184, 425 / (quotient) 22, 48, 381
Murray, James D., and vanRyper, William. 1994. Encyclopedia of Graphics File &meta 186, 425, 451 := (assignment) 19, 49, 50, 383
&mpress 184, 426 :=: (exchange) 20, 384
Formats. Sebastopol, Calif.: O'Reilly & Associates. &mrelease 184, 426 ; (expression separator) 8, 50
Open Software Foundation. 1991. OSFIMotif Style Guide. Englewood Cliffs, N.J.: &null 21, 426 < (numerical comparison) 22, 383
&output 40, 426 <....: (reversible assignment) 17, 384
Prentice-Hall. &phi 426 <-> (reversible exchange) 384
Peitgen, Heinz-Otto; Jiirgens, Hartmut; and Saupe, Dietmar. 1992. Chaos and &pi 426 « (string comparison) 32, 383
&pos 426 «= (string comparison) 32, 383
Fractals: New Frontiers of Science. New York: Springer-Verlag. &progname 426 <= (numerical comparison) 22, 383
Prusinkiewicz, Przemyslaw, andHanan,James. 1989. Lindenmayer Systems, Fractals, &random 102, 426 = (numerical comparison) 22, 383
and Plants. Berlin: Springer-Verlag. &rdrag 184, 427 = (string matching) 34, 379
&resize 184, 185, 427 == (string comparison) 32, 383
Prusinkiewicz, Przemyslaw, and Lindenmayer, Aristid. 1990. The Algorithmic &row 186, 427 === (value comparison) 383
Beauty of Plants. New York: Springer-Verlag. &rpress 66, 184, 427 > (numerical comparison) 22, 383
&rrelease 66, 184, 427
Rossotti, Hazel. 1983. Colour: Why the World Isn't Grey. Princeton, N. J.: Princeton 495
University Press.
496 Index Index 497
>= (numerical comparison) 22, 383 of vidgets 463 CloneO 169, 389, 429 cursor
» (string comparison) 32, 383 with VIB 262 cloning 169-171 mouse See pointer
»= (string comparison) 32, 383 augmented assignment 19, 383 closeO 42, 411 text 128, 194, 212, 474, 476
? (random selection) 23, 25, 27, 28, 102, automatic type conversion 20 col 128, 433 cursor 128, 433, 474, 476
380 ColorO 146, 389 curves 85, 446
? (string scanning) 33, 49, 377 B ColorDialogO 291, 389 custornization, of graphics system 175
[-:] (subscript) 386 colors 139-153 See also gamma correction
[+:] (subscript) 385 background color 62, 64, 78, 86, 88, 144, additive 141 D
[:] (substring or section) 31-32, 385 151, 158 brightness 144
[,] (multiple subscript) 303, 385 backspace character 128, 194, 212 decimal specifications 142 data types 18
[] (subscript) 25, 27, 31-32, 385 backtracking 16, 17 dialogs 204, 291 checking 20
[...] (list creation) 24, 384 bal() 410 hexadecimal specifications 142 conversion 20, 98
\ (limitation) 15, 377 base line 132 interface design 261 errors 56
\ (nonnull test) 21, 99, 381 bg 63, 432 limited number 145, 476 notation 365
1\ (power) 22, 48, SO, 382
B90 63, 143, 388 maps 145-146 debugging 55-56
binary tree 53 mutable 146, 146-148, 474 declarations
{} (expression grouping) 10, 49
BLKSIZE 482 names 139-141, 145 global 36-37
I (alternation) 11, 18, 377
blocking 189 numerical specifications 141 initial 36
I (repeated alternation) 377
BMP image format 474 palettes 441-444 link 45
II (string concatenation) 30, 382
braces 10, 49 portability 145 local 36-37
III (list concatenation) 382
break 12, 375 primary 141 procedure 35-36
- (complement) 379
buttons 208, 237, 459, 464 random 163 record 23
-= (numerical comparison) 22, 383
-== (string comparison) 32, 383 dialog default 293, 464, 471 specification 139-144, 473, 476 static 36-37
-=== (value comparison) 383 in text dialogs 297 subtractive 142 default
ColorValueO 142, 145, 390 case clause 13
A c columns 128, 133 dialog button 293, 464, 471
columns 167, 433 parameter value 35, 102
absO 409 callbacks 191, 215-217, 271-283, 454 program options 175
sharing 264 comments 8
acos() 23, 409 comparison table value 28
ActiveO 189, 191, 258, 388 canvas 167-173, 173 default 13
attributes 168 numerical 22, 383
Alert() 192, 388 string 32, 383 delayO 411
alternation 11, 13, 18, 377 VIB 225, 461 delete character 128
canvas 173, 432
success and failure 10
analysis, string 32 value 383 deleteO 27, 411
angles 84, 106, 447 case 13, 375 depth 145, 433
CD-ROM 489-491 compilation 44, 479
animation 94-97, 151-153, 177, 181 compound expressions 10 descender 132
anyO 409 center() 31, 410 descent 133, 433
CenterString() 388
concatenation, string 30
arcs 81-85, 446 conditional compilation 43, 374 detabO 412
arithmetic 21-22 coercion See conversion dialog_value 194, 287, 289, 290, 291
char() 410 conjunction 11, 49
arrays See lists continuation lines 50 dialogs 287-298
ascender 132 character positions 31 colors 204
characters 29-34 control key 186, 197
ascent 133, 432 control structures 12-13, 375-377 custom 292-295, 471-472
ASCII 29 codes 29 field order 297
chdir() 411
conversion 20, 98
asin() 23, 410 coordinate system 60, 88-89, 100, 178 hidden 298
assignment 19, 49, 50, 383-384 circles 81-85, 446 standard 193-196, 287-291
client 192, 475 cOPYO 53, 323, 411
augmented 19, 383 CopyAreaO 174, 178, 390 standard versus custom 296
associativity 48, 49 Clip() 89, 388 difference 27
cliph 89, 432 cosO 23, 411
atanO 23, 410 CoupleO 171, 390 directives
attributes 60, 62-64, 69, 429-440 clipping 65, 89-90, 178 conditional compilation 374
clipw 89, 432 coupling 167-173
of canvases 168, 430 csetO 411
define 42, 51, 372
of graphics contexts 168, 431 c1ipx 89, 432 error 374
clipy 89, 433 csets 29-30
of lines 85 literals 30, 369 include 43, 46, 371-372
498 Index Index 499
line 372 standard error output 40 filtering, of vidget events 217, 239, 466 height 166, 436
undefine 43, 372-373 escape sequences 369 findO 14, 16, 34, 412 HLS color model 150
display 166, 434, 475 even-odd rule 80 finite state machine 198-199 HSV color model 143, 150, 291
displayheight 173, 434 event loops 190-191 floating point See real numbers hue 139-141, 143
displaywidth 173, 434 EventO 66, 184, 189, 393, 451 flushO 413
displays 145-146 events 66-67, 183-189 FontO 131, 394 I
documentation, about Icon 488, 489 artificial 189, 451 font 131, 435
double spacing 133 dispatching 190 fonts 129-133 iandO 413
DrawArcO 84-85, 390, 446 keyboard 66, 183, 188, 449-450, 474, 476 characteristics 132 icode 44, 480
DrawCircleO 81-85, 390, 446 mouse 66, 183 families 129, 130 icomO 413
DrawCurveO 85, 89, 391, 446 mouse drag 183 monospaced 129-130, 130 icon (small image) 173
Drawlmage() 155, 157, 391 multiple windows 191 portability 130, 138 Icon program library 46-47, 388, 489
drawing 71-72 polling 189 proportional 130, 133 core modules 47
details 445-448 queues 66, 180, 183, 184- sans-serif 130 organization 46-47
on interfaces 262 189, 188, 204, 451 screen 129 Icon Project 488
reversible 88, 135, 200, 475 setting keywords 185-189 serif 130 Icon resources 487-488
DrawLineO 73, 391, 446 every-do 15-16, 17, 376 size 129 iconimage 162, 173, 436
drawop 88, 135, 434, 475 exchange 20 specification 130, 473, 475 iconlabel 173, 436
DrawPointO 71, 391 execution 44 standard 130, 473 iconpos 173, 437
DrawPolygonO 79, 391, 446 exitO 412 styles 129 icont 44, 45, 56, 479
DrawRectangleO 61, 77, 78, 391, 446, 448 expO 23, 412 typewriter 130 identifiers 370
DrawSegmentO 75, 392 exponentiation 50 foreground color 62, 86, 88, 144 if-then-else 9, 12, 376
DrawStringO 134, 392 expressions 8-18 fractal stars 91-92, 116 imageO 56, 172, 413
dtorO 23, 412 compound 10 frame, window 67 image 161, 437
dx 88, 90, 180, 186, 434, 451 evaluation 9-18 FreeColorO 146, 394 image file formats
dy 88, 90, 180, 186, 434, 451 interactive evaluation 57 FfP, Icon 487 BMP 474
success and failure 10-11 functions See procedures GIF 161, 164, 474
E fwidth 133, 436 XBM 476
F XPM 476
echo 188,435 G images 155-164
echoing, keypresses 188 fail 38, 376 bi-Ievel 157
elements failure 10-11 gamma 144, 162, 436, 474, 476 drawing 155-157
list 24-26 fg 63, 435 gamma correction 144-145, 162, 474, 476 in files 161-162, 474, 476
table 27 FgO 63, 143, 393 generators 13-15, 17-18 infix operators 48-51
empty string 30 fheight 133, 435 getO 25, 413 initial 36
end 7, 35 fields, record 23-24 getenvO 413 input See reading; events
EnqueueO 189, 392 figure orientation 100-101 GetEventsO 255, 394 insertO 26, 413
entabO 412 files 39-42 GIF image format 161, 164, 474 integerO 21, 414
enter key See return key binary 40 global 36 integers 21
environment variables 45-46, 481 closing 42 global variables 36, 305 intensity See light, intensity of
EraseAreaO 64, 78, 146, 392 dialogs 193-196 goal-directed evaluation 16-17 interaction 183-204
errors opening 40 GotoRCO 128, 394 model 217-218
compilation 484 reading 10, 39-41 GotoXYO 128, 395 interactive expression evaluation 57
conversion 11 redirection 480 graphicS contexts 167-173 interface builder See VIB
data types 56 standard 40 attributes 168 interface design 221, 260
directed 374 writing 41 graphics systems 67--68, 473-477 interface tools 208-215 See also the individual
event queue 451 FiIIArcO 85, 393, 447 grouping 22, 48 tools; vidgets
offending value 56 FiliCircleO 81, 393, 447 GUI See visual interface builder choosing 219
preprocessor 374 FillPolygonO 79-81, 393, 447 interpreter 479
run-time 56 FiliRectangleO 64, 77, 78, 394, 447, 448 H intersection 27
stack overflow 101 fillstyle 158, 160, 435, 474 iorO 414
halftones 148
500 Index Index 501
records 23-24 See also structures seekO 419 nonpositive 31-32 toggle buttons 208, 290, 459, 464
fields 48 SelectDialogO 289, 399 string 31 callbacks 216
trees and graphs 53 selection, for editing 194, 212 table 27 ToggleDialogO 290, 402
rectangles 61, 64, 77, 446, 448 semicolons 8, 50 substrings 32 TRACE 482
copying 174 seqO 419 SubWindowO 180, 399 traceback 56
negative dimensions 77, 448 server 192, 475 subwindows 178 tracing 56
random 64-66 setO 26, 420 success 10-11 translation 88-89, 90, 180, 451
selection 198-203 sets 26-27 See also structures suspend-do 39, 376 transparency 155, 157, 164
recursive generation 122 ShadeO 148, 399 synchronization 191 transparent GIFs 164
regions 214, 231, 262, 461, 467 shading, three dimensional 151 syntax 367-370 tree (data structure) 53
callbacks 217 shift key 186, 197 system() 421 TResetO 107, 403
removeO 418 Sierpinski triangle 71-72 TRestoreO 106, 111, 403
renameO 418 sinO 23, 420 T TRightO 106, 111, 403
repeat 12, 376 size 60, 166, 440 trigonometric procedures 22
repeated alternation 377 sliders 212-213, 239, 460, 466 tab character 128 trimO 31, 421
replO 31, 418 callbacks 217 tabO 33, 34, 421 TSaveO 106, 111, 403
reserved words 7, 370 scaling 218 tableO 27, 421 TScaleO 404
resize 68, 184, 439 sortO 28-29, 420 tables 27-28 See also structures TSkiPO 106, 110, 404
return 35, 38, 376 so rtf0 420 default value 28 turtle graphics 105-125
return character 128, 491 sorting 28-29 sorting 28 TWindowO 404
return key 188 splines, Catmull-Rom 85 tanO 23, 421 TXO 106, 404
reverse 144, 439 sqrtO 23, 420 TDrawO 106, 111, 400 TYO 106, 404
reverseO 31, 419 stack, evaluation 101 TDrawtoO 106, 400 typeO 19, 422
reversible assignment 17 stacks 25-26 technical reports, Icon 489 types See data types
reversible drawing 88, 135, 200, 475 standard error output 40 termination dump 56
rewriting system 118 standard input 40 termination, program 42, 62
text 127-138
u
RGB color model 142, 143, 150, 291 standard output 40
rightO 31, 419 stars 74, 80-81, 83-84, 85, 90, 102 cursor 128, 194, 212, 474, 476 ucode 45
RightStringO 398 fractal 91, 116 files 39 uiO 255, 256
root vidget 253, 255 statements 8-9 justification 135-138 uLattsO 256, 262
rotation 177 static 36 positioning 128, 134 UncoupleO 172, 405
round-off 97-98 static variables 36 scrolling 127 undeclared identifiers 56
row 128, 440 stopO 41, 421 width 133 undo facility 322
rows 128 stringO 421 text lists 213--214, 460, 465 union 27
rows 167, 440 strings 29-34 callbacks 217 UNIX 44, 45, 479 See also X Window System
rtodO 23, 419 as atomic values 29 text position 128 until-do 12, 377
runerrO 419 character positions 31 text-entry fields 193, 212, 288, 460, 465 uptoO 34, 422
running programs 44, 479--485 comparison 32, 383 aligning 264
drawing 134 callbacks 217 V
s literals 30, 369 in custom dialogs 297
variables 19
scanning 32-34, 49 TextDialogO 287-289, 297, 400
saturation 139-141, 143 TextWidthO 133, 401 environment 45-46
subscripting 31
SaveDialogO 195, 287, 398 TFaceO 106, 401 global 36-37
substrings 32
scaling 218 TGotoO 106, 110, 112, 401 local 36-37
STRS1ZE 482
scanning See strings, scanning THeadingO 106, 401 parameters 35
structures 23-29 See also lists; records; sets;
scope, variable 36-37 THomeO 402 static 36-37
tables
scrollbars 213, 461, 466 thumb 212, 217, 466 undeclared 56
pointer semantics 52-55
callbacks 217 TlnitO 112 untyped 19
subject window 165, 166
scaling 218 title bar, window 67, 69, 173 VEchoO 405
submenus 211, 216, 465
scrolling 127, 176-177 TLeftO 106, 402 VGetltemsO 253, 405
subscripting
seed, random 102, 426 to-by 17, 384 VGetStateO 253, 405
list 25
VIB 224-252, 457-472
®
504 Index
creating dialogs 292-295, 471-472 opening 60, 62, 166 Other Peer-to-Peer Communications Titles
generated code 251-252 position 166-167
limitations 472 reading 127, 188 lions' Commentary on UNIX Future Volumes
menus 248, 458 resizing 68, 184, 185 6th Edition, with Source Code (see www.peer-to-peer.com/
multiple windows 256 scrolling 127 John Lions for publication dates and prices)
prototyping 247, 472 size 60, 67, 166-167, 180, 226 264 pp., $29.95 US, ISBN 1-57398-013-7
vidgets 208, 253-254, 453-456, 459-461 See stacking order 167 Volume 2: Virtual Memory
"After 20 years, this is still the best exposition
also interface tools string images 172 ISBN 1-57398-027-7
of the workings of a 'real' operating system."
activation 455 synchronization 191 Extensively describes a modified, thoroughly
-Ken Thompson, 1996
aligning 264, 468-470 writing 127 documented implementation of the Mach
This hacker classic gives the complete source
attributes 463-468 Windows 95 See Microsoft Windows virtual memory system integrated into the x86
code to an early, very elegant version of UNIX
configuring 468-470 WOpen() 60, 62, 69, 165, 166, 168, 407, 429 BSD environment. Covers topics such as x86
(Thompson and Ritchie wrote all the code) and
copying 470 WQuit() 72, 407 mmu control, clustering, copy on write, map-
also provides a brilliant commentary on the
deleting 470 WReadO 127, 188, 189, 408 ping, swapping, paging, and fault handling.
software's inner workings. Well-suited for
events 255 WReadsO 127, 188, 189, 408
record fields 254, 453 writeO 40, 41, 422 either textbook use or self-study. Volume 3: Sockets
resizing 468 Writelmage() 162, 408, 474, 476 ISBN 1-57398-003-X
selecting 468 writesO 42, 422 Operating System Source Code Secretstm Extensively describes operations conducted
states 253, 454 writing series on Berkeley and Winsock sockets including
visual feedback 454 images 162, 474, 476 William F. Jolitz and Lynne Greer Jolitz SOCKS, SSL, connection management, name
virtual machine 479 to files 41-42 binding, data and network security, the recep-
visual interface builder See VIB to windows 127 "If 386BSD had been available when I started on tion and transmission of data, determination of
VSetFontO 405 WSyncO 192, 408 Linux, Linux would probably never have status or buffering states, dynamic allocation of
VSetltems() 254, 406 WWrite() 61, 127, 409 happened." sockets on demand, domain category and meth-
VSetStateO 253, 406 WWritesO 127, 409 -Linus Torvalds, Linux developer ods, and sockets in client/server and peer-to-
This will be the most comprehensive operating peer models.
W x systems series ever published. Bill Jolitz (Princi- Volume 4: TCP/IP Networking Protocol
pal Developer of Berkeley UNIX 2.8) and his wife
WAttrib() 63, 406, 429 x 128, 440 ISBN 1-57398-007-2
Lynne began the 386BSD project in 1989, devel-
WCloseO 166, 406 X Window System 68, 475-477 Extensively describes methods of implement-
oping a complete, fully-documented x86 operat-
WDefault() 175, 406, 477 X-box buttons 209 ing an industrial strength TCP /IP protocol
ing system based on BSD UNIX but incorporat-
WDeiayO 65, 94, 192, 407 XBM image format 476 stack, from sockets through driver interfaces.
ing the best ideas from later systems (e.g. NT's
WDone() 62, 407 XPM image format 476
dynamic configuration, Mach's virtual memory
Web site, Icon 487
WFlush() 192, 407 y model, Solaris threads). Operating System SOllree
The RAIDbook, 7th edition: A Handbook
Code Secrets, the fruits of ten years of 386BSD
whereO 422 of Storage Systems Technology
Y 128, 440 work, shows in full detail how modem operat-
while-do 10, 12, 15-16, 377 RAID Advisory Board
ing systems really work, emphasizing themes of
Wi 483 300 pp., $39.95 US, ISBN 1-57398-028-5
system performance, security, scalability, modu-
width, text 133
lar configuration, and extensibility. The RAIDbook is the definitive technical
width 166, 440
handbook on RAID and other state-of-the-art
windows 165-181 Volume 1: The Basic Kernel
attributes See attributes data storage technologies. Redundant Arrays
530 pp., $49.95 US, ISBN 1-57398-026-9
closing 166, 172 of Independent Disks (RAID) offers high per-
Extensively describes fundamental kernel formance, reliability, and serviceability as well
coordinate system 60, 88, 100 functions (e.g. bootstrap, memory allocation,
focus 167 as unlimited capacity. This is a must-have book
and x86 specifics) as well as newer concepts for system managers, engineers, and program-
hidden 173 such as dynamic configuration, role-based se-
iconified 173 mers who need to understand high-end disk
curity, and threads. Published 1996. systems.
label 172
management 67-68, 180 For information on these and other Peer-to-Peer titles, visit our Web site
maximal 173 http://www.peer-to-peer.com/or your favorite technical bookstores.
normal 173
Also available
The Icon Programming Language 3/e
This book is the definitive reference manual and text for the Icon programming
It contains all you need to learn and use Icon. This third edition expands on the previo
editions and brings the description up to date with Version 9 of the language.
The language book complements Graphics Programming in Icon by providing complete at
in-depth coverage of all features of the language.
PP
SM
PEER-TO-PEER
COMMUNICATIONS 9 781573 980098