0% found this document useful (0 votes)
197 views

Graphics Programming in Icon

1998, Ralph E. Griswold, Clinton L. Jeffery, and Gregg M. Townsend

Uploaded by

yumbo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
197 views

Graphics Programming in Icon

1998, Ralph E. Griswold, Clinton L. Jeffery, and Gregg M. Townsend

Uploaded by

yumbo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 267





 





 

  
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

Window Management 67 7 Color 139


Library Resources 68 Specifying Colors 139
Tips, Techniques, and Examples 69 Color Correction 144
Portability Considerations 145
4 Drawing 71
Color Maps 145
Points 71 Mutable Colors 146
Lines 73 Monochrome Portability 148
Rectangles 77 Printing Color Images 149
Polygons 79 Library Resources 150
Circles and Arcs 81 Tips, Techniques, and Examples 150
Smooth Curves 85
Line Attributes 85 8 Images 155
Reversible Drawing 88 Drawing Images 155
Coordinate Translation 88 Patterns 157
Clipping 89 Image Files 161
Library Resources 90 Library Resources 163
Tips, Techniques, and Examples 91 Tips, Techniques, and Examples 163

5 Turtle Graphics 105 9 Windows 165


The Turtle Graphics System 105 The Subject Window 165
Implementing Turtle Graphics 110 Opening and Closing Windows 166
Library Resources 116 Window Size and Position 166
Tips, Techniques, and Examples 116 Stacked Windows 167
Graphics Contexts 167
6 Text 127
Canvas States 173
Window Input and Output 127 Copying Areas 174
Positioning Text 128 Reading the Canvas 174
Fonts 129 Customization 175
Text Width 133 Tips, Techniques, and Examples 175
Drawing Strings 134
Library Resources 135 10 Interaction 183
Tips, Techniques, and Examples 135 Events 183
Processing Event Queues 184
Polling and Blocking 189
vi vii

Event Loops 190 Standard Dialogs Versus Custom Dialogs 296


Active Windows 191 Library Resources 296
Synchronization 191 Tips, Techniques, and Examples 296
Audible Alerts 192
15 A Pattern Editor 299
Mouse Pointer 192
Dialogs 193 The Application 299
Library Resources 196 Program Design 301
Tips, Techniques, and Examples 196 Program Organization 305
The Complete Program 313
11 User Interlaces 205 Tips, Techniques, and Examples 322
An Example Application 205
16 Facial Caricatures 327
Interface Tools 208
Callbacks 215 The Application 327
The Interaction Model 217 Program Design 330
Tips, Techniques, and Examples 218 Program Organization 334
The Complete Program 347
12 Building a Visual Interlace 221 Tips, Techniques, and Examples 362
Planning the Interface 221
The Appendices 365
A Visual Interface Builder 223
More About Vidgets 253
A Syntax 367
The Organization of a Program with a VIB Interface 254
Multiple VIB Interfaces 256 B Preprocessing 371
Tips, Techniques, and Examples 260
Include Directives 371
13 Completing an Application 267 Line Directives 372
Program Organization 268 Define Directives 372
Drawing the Kaleidoscope 274 Undefine Directives 372
The Complete Program 277 Predefined Symbols 373
Tips, Techniques, and Examples 283 Substitution 373
Other Applications 285 Conditional Compilation 374
Error Directives 374
14 Dialogs 287
C Control Structures 375
Standard Dialogs 287
Custom Dialogs 292
viii ix

D Operators 379 L Vidgets 453

Prefix Operators 379 Vidget Fields 453


Infix Operators 381 Vidget States and Callbacks 454
Other Operators 384 Vidget Activation 455

E Procedures 387 M VIS 457


The vm Window 457
Graphics Procedures 387
Basic Procedures 409 Vidgets 459
The Application Canvas 461
F Keywords 423 Creating Vidgets 462
Vidget Attributes 463
G Window Attributes 429 Manipulating Vidgets 468
Canvas Attributes 430 Custom Dialogs 471
Graphics Context Attributes 431 Prototyping 472
Attribute Descriptions 432 Limitations 472

H Palettes 441 N Platform Dependencies 473


Grayscale Palettes 441 Microsoft Windows 473
The c1 Palette 442 The X Window System 475
Uniform Color Palettes 443 0 Running an Icon Program 479
I Drawing Details 445 Running Icon from the Command Line 479
Lines 445 Input and Output Redirection 480
Rectangles 446 Command-Line Arguments 481
Polygons and Curves 446 Environment Variables 481
Circles and Arcs 446 Running Icon under Microsoft Windows 482
Filled Figures 447 User Manuals 485
Rectangular Areas 448 P Icon Resources 487
J Keyboard Symbols 449 The CD-ROM 487
On-Line Access 487
K Event Queues 451 Implementations 487
Documentation 488
x

Q About the CD-ROM 489


How to Use the CD-ROM 489
File Formats 490
Preface
External Links 491

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#/)&#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

every lookup(1 to 100) Variables


There is an optional by clause in case you want an increment value other
Most programming languages, including Icon, have variables to which
than I, as in
values can be assigned. Icon, unlike most programming languages, does not
every lookup(O to 100 by 25) limit a variable to one type of data. In Icon, variables are not typed but values are.
which evaluates lookup(O), lookup(25),lookup(50),lookup(75), and lookup(1 00). That may sound a bit strange, but what we mean is illustrated by the procedure
typeO, which returns the name of the type of its argument. For example,
Alternation, described earlier, is a generator:
type(a + b)
expr1 I expr2
returns either "integer" or "real", depending on the types of a and b. You might
This expression first generates the values of expr1 and then generates the values want to make a mental note about typeO - it's handy for several purposes,
of expr2. For example, including debugging.
every lookup(1) I lookup(33) I lookup(57) Since variables are not typed, a value of any type can be assigned to any
evaluates lookup(1), lookup(33), and lookup(57). This can be written more variable. For example, it's possible to assign an integer to a variable at one time
compactly by putting the alternatives in the argument of lookupO: and a string to the same variable at another time, as in

every lookup(1 I 33 I 57) x:= 1

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.

Type Checking and Conversion The Null Value

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

write(attributes["width"]) sort(T, 1) produces a list of two-element lists, where each two-


element list corresponds to an element of T. Sorting of
writes 500. You also can change the value associated with a key by assignment,
the two-element lists is by key.
as in
sort(T,2) is like sort(T, 1) except that the two-element lists are
attributes[" width"] := 1000
sorted by value.
A default value is associated with every table. This value is fixed at the
sort(T,3) produces a list of alternating keys and associated
time the table is created and is specified by the argument to the tableO call. If no
values. The resulting list has twice as many elements as
argument is given, the null value is used for the table default.
T. Sorting is by keys.
When a table is subscripted by a value that does not match a key, the
sort(T,4) is like sort(T, 3), except that sorting is by value.
expression does not fail, but instead produces the table's default value. Continu-
ing the example above,
Characters and Strings
attributes["height"]
succeeds and produces the null value because that is the table's default value. An Characters are the material from which text is formed. Icon uses an 8-bit
expression such as ff[k] can be used to test whether k has been used to assign a character set, which contains 256 characters. Characters are represented in a
value in T. computer by small nonnegative integers in the range 0 to 255. These numbers are
called the character codes. Although you ordinarily do not need to think of
A default value of 0 is useful for tables that accumulate counts. For
characters in terms of the character codes that represent them, it's useful to know
example, if
that operations on characters, such as comparison, are based on the values of
count := table(O) character codes.
then an expression such as All modem computer systems use a superset of the ASCII character set.
As you'd expect, the code for B is greater than the code for A, and the code for
count["angle"] +:= 1
2 is greater than the code for 1. In ASCII, the codes for lowercase letters are
increments the value associated with "angle" whether or not it is the first time greater than the codes for uppercase ones. Codes for characters other than letters
count is subscripted with this key. and digits are somewhat arbitrary, and the meaning of codes greater than 127 is
The same operations that apply to lists and sets apply to tables: *T is the system-dependent.
number of elements (keys) in T, !T generates the values (not keys) in T (in no Some characters do not have symbols associated with them, but desig-
predictable order), and ?T produces a randomly selected value from T. In nate special functions; tabs, backspaces, and linefeeds are examples. Some
addition, key(T) generates the keys in T (in no predictable order). characters have no standard associations with symbols or functions but are used
for a variety of purposes depending on the application that uses them. All 256
Sorting Structures characters can be used in Icon programs. Unlike C, the null character (which has
code 0), is not reserved for a special purpose.
A structure can be sorted to produce a list with elements in sorted order.
The details of sorting depend on the kind of the structure. Data Types Composed of Characters
A list, set, or record can be sorted by sort(X), which produces a new list
with the elements of X in sorted order. Sorting for numbers is in order of Icon has two data types based on characters: strings and character sets
nondecreasing magnitude. Sorting for strings is in nondecreasing lexical (alpha- (csets).
betical) order. See Appendix E for details about sorting. A string is a sequence of characters. Strings are used for many purposes,
Sorting tables is more complicated because a table element consists of a including printed and displayed text and text stored in files. Strings in Icon are
pair of values. The way a table is sorted depends on the second argument of atomic data values, not arrays of characters. A string is a value in the same sense
sortO:
30 Icon Chapter 2 Chapter 2 Icon 31

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

arbitrarily long, limited only by the amount of available memory.


while line := readO do
A cset is just a collection of different characters. Unlike strings, there is text := text" line" " "
no concept of order in a cset and a character can only occur once in a given cset.
builds up a string of all the lines of input with a blank following each line. (This
Csets are useful in string analysis in which certain characters, such as punctua-
probably isn't something you'd actually want to do. Although Icon lets you
tion marks, are of importance, but no character is more important than another.
build long strings, a list of strings usually is easier to process.)
Strings are represented literally by enclosing a sequence of characters in
The operation *s produces the size of s - the number of characters in it.
double quotation marks, as in
For example, the value of *salutation as given above is 36. Incidentally, *s is fast
greeting := "Hello world!" and its speed is independent of the size of s.
which assigns a string of 12 characters to greeting. Escape sequences are used for Icon provides several procedures that construct strings. The procedure
characters that cannot be represented literally. For example, "\n" is a string reverse(s) returns a copy of s with its characters in reverse order. The procedure
consisting of a linefeed character, "V'C" is a control-C character, and "\"" is a string repl(s, i) produces the concatenation of i copies of s. The procedures left(s, i),
consisting of one double quotation mark. See Appendix A for a description of right(s, i), and center(s, i) position s in a field of a length i. The procedure trim(s)
escape sequences. removes trailing spaces from s. These procedures are described in more detail
Csets are represented in a similar fashion, but with enclosing single in Appendix E.
quotation marks, as in Although strings in Icon are not arrays of characters, you can get the
individual characters of a string by subscripting it. For example,
operators := '+-*/"%'
write(text[1 ])
which assigns a cset of 6 characters to operators.
Several keywords provide predefined csets. Two of the most useful are: writes the first character of text.
In Icon, unlike C and other programming languages that represent
&digits the 10 digits
strings by arrays of characters, character numbering starts at I, not O. Character
&Ietters all upper- and lowercase letters positions actually are between characters. For example, the character positions
See Appendix F for other cset-valued keywords. in "Medusa" are:
M e d u s a
Operations on Strings i iii i i i
1 2 3 4 5 6 7
Icon has a large repertoire of operations on strings. Some operations are
used to create strings, while others are used to analyze strings. We'll discuss Position 1 is before the first character and position 7 is after the last character.
string analysis in the next section. In subscripting a string, sri] is the character following position i. The
The mostfundamental way to construct a string is concatenation, s1 II s2, substring consisting of the characters between two positions can be obtained by
which creates a new string by appending the characters of s2 to those of s1. An subscripting with the positions separated by a colon. For example, the value of
example of concatenation is "Medusa"[2:5] is "edu".
salutation := greeting II " (I'm new here, myself.)" Nonpositive numbers can be used to identify the characters of a string
relative to its right end:
which forms a new string consisting of the characters in greeting followed by
those given literally. M e d usa
iii iii i
The empty string, which is given literally by"", is useful when you're -6 -5 -4 -3 -2 -1 0
32 Icon Chapter 2 Chapter 2 Icon 33

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

fields := [] procedure greet(name)


every put(fields, new_field(» write("Hi there!")
write! fields write("My name is ", name, II . " )

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

Procedure Returns procedure segment(s, n)


s?{
As shown earlier in this chapter, a declared procedure can return a value while seg := move(n) do
using a return expression, such as suspend seg
return i }

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

commonly used modes are: }

"r" open the file for reading (the default) write(sizes)


"w" open the file for writing The result might be something like
The procedure openO returns a value whose type is file. For example, 415711
42 Icon Chapter 2 Chapter 2 Icon 43

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

See Appendix B for more information about preprocessing. Libraries

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

A more abstract representation is shown in Figure 2.3.


Tips, Techniques, and Examples

Debugging

Debugging is one of the most difficult, time-consuming, and frustrating


aspects of programming. Prevention is, of course, better than cure, but that's
mostly a matter of good programming practice.
If you have a problem with a program, the easiest thing you can do is add
writeO expressions at judiciously chosen places to get more information. Al-
though this is commonly done, it requires editing the program before and after
finding the problem, and it also runs the risk of introducing its own errors.
56 Icon Chapter 2 Chapter 2 Icon 57

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.

This chapter introduces these graphics capabilities. Subsequent chapters


provide more details and cover Icon's entire graphics repertoire. Appendix E
summarizes the graphics procedures described throughout the text.
We assume initially that only one window is used. When there is just one
window, it is implicit in all graphics operations and needs no explicit mention.
The implicit window is represented by the keyword &window, which is null if no
window is open. Chapter 9 explains how multiple windows are created and
used.

The Structure of a Graphics Program

A minimal Icon graphics program contains a main procedure and a link


graphics declaration. Here is a simple example:
link graphics
procedure mainO
WOpen(ls ize=400,300")
59
60 Graphics Chapter 3 Chapter 3 Graphics 61

WWrite(" Hello world!") A Blank Window


DrawRectangle(60. 80, 50, 20) A window is blank until something is
WDoneO written on it. Windows have frames
supplied by the window system. We'll
end
talk about them later in this chapter.
The program above opens a window, writes a string in it, draws a Until then, we'll dispense with the
rectangle, and then waits for the user to dismiss it. This program can be used as frame and show just the window itself
a starting point for experimentation. We'll describe the procedure calls in the with a line around it.
next section.
Throughout the book, we'll present other programs or, more commonly,
program fragments. All programs, though, need at least a main procedure and Figure 3.2
at least one link declaration.
The library's graphics module implements many important procedures You now can write text in the window using WWriteO, as in
and is needed by all examples given. Although some graphics procedures are WWrite(" Hello world!")
actually built into Icon, this book does not distinguish them from library
procedures. The link graphics declaration gives access to the graphics library. which produces the result shown in Figure 3.3.

He 11 0 wo r 1d ! Writing Text in a Window


Basic Window Operations
Note that there is a blank at the begin-
The screen and each window are treated as portions of an x-y plane of ning of the string literal. This provides
pixels, with the origin (0,0) atthe upper-left comer. Pixel positions increase to the space between the edge of the window
and the H.
right and downward, as shown in Figure 3.1.
0,0 Coordinate System
X
Note that vertical values increase in the downward
direction. This is natural for text that is written from
the top to the bottom of an area, but it is the opposite
of what's usually expected in plotting and drawing. Figure 3.3

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

Hello world! Adding a Rectangle Hello world!


A Light Gray Background
In DrawRectangleO, the first two ar- Many monitors support at least a few
guments specify the upper-left comer colors or shades of gray and give the
of the rectangle being drawn. The third appearance shown here. Some moni-
and fourth arguments specify its width tors, however, support only black and
and height respectively. white. On such a monitor, light gray is
rendered as white, since it's closer to
white than to black. The result is, of
course, not at all like this.

Figure 3.4 Figure 3.5

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

What we've described so far is enough to write a simple program to LJ

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: '.

Tho following operations .... 4Y4l\ablo: Tips, Techniques, and Examples


1........lon .....
lft..,ila.'.ft
,-
rotation
.....rlaetlon
.",,.0..•
ect-ift.
shifting
tt'1_lng
cc.,,''''
.ltt.atM'",
Lists of Attributes
crcpplng c ,. '.ft
slrl"'lng " "i",
c:ooopos 1t 1on
Window attributes can be stored in lists and used for opening windows
t-.lng (11ell ." en• • f
...It". _11:" • ••
_,.11:•• ''''-0
tlk.
f . . . . ell

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

Drawing is an important component of many graphics applications, and Icon


provides procedures for drawing points, lines, curves, and other shapes. Com-
plicated images can be built up using these primitive operations.
Drawing is comparatively easy in Icon, and a few simple principles
apply to all drawing operations. We'll cover all the drawing operations in this
chapter, showing how they can be used. You'll find a description of various
details related to drawing in Appendix I.
In the examples that follow, we'll assume a window of appropriate size
and omit coding details. In the last section of this chapter, we'll give some
programming tips and techniques.

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

every 1 to vertices do { Regular Star


theta +:= incr This regular star was drawn by
x := cx + radius * cos(theta) # new position
rstar(200, 200, 180, 8, 3)
y := cy + radius * sin(theta)
DrawLine(xprev, yprev, x, y) What happens with skips of 4? Try
xprev:= x # update old position other combinations, like 100 vertices
yprev:= y with skips of 31.
}
return
end

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

As this example illustrates, DrawRectangleO draws only the outline of a


rectangle. FiIIRectangleO draws solid rectangles that are filled with the fore-
Figure 4.4 ground color.
Here's a code segment that draws a checkerboard. The squares are
numbered across and down, starting at (0,0). A square is filled if the sum of the
horizontal and vertical counts is odd. The result is shown in Figure 4.6.
78 Drawing Chapter 4 Chapter 4 Drawing 79

$define Size 40 Polygons


$define Edge 4
$define Offset 40 The procedure DrawPolygonO draws the polygon whose vertices are
$define Squares 8 given by successive x-y argument pairs. For example, the rows of triangles
shown in Figure 4.7 can be drawn as follows:
# draw the squares v:=(20,115,210,305]
every i := 0 to Squares - 1 do every x := !v do
every j := 0 to Squares - 1 do every y := !v do
if (i + j) % 2 = 1 then DrawPolygon(x + 40, y, x, Y+ 80, x + 80, Y + 80)
FillRectangle(Offset + i * Size, Offset + j * Size, Size, Size)
Notice that only the coordinates of the vertices need to be given; the figure is
# add border and edge
closed automatically.
DrawRectangle(Offset, Offset, Squares * Size, Squares * Size)
DrawRectangle(Offset - Edge, Offset - Edge, Triangles
Squares * Size + 2 * Edge, Squares * Size + 2 * Edge)
Try writing a procedure in which the
size of the triangles and the number of
A Checkerboard rows and columns are parameters.
What change to the code would be
needed to reverse the coloring of the
squares, so that the bottom-left square
was white?

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

If the sides of a polygon intersect, the "even-odd" rule determines the


portions of the drawing that are considered to be inside the polygon for the Figure 4.10
purposes of filling. With this rule, a point is interior to the polygon, and hence
filled, if an imaginary line drawn between the point and one outside of the figure
crosses the lines of the figure an odd number of times. This is illustrated in Figure Circles and Arcs
4.9, in which FillPolygonO is used to draw a regular star.
So far, all the drawing procedures have produced straight lines. The
Filled Star procedure
This filled star has 31 vertices drawn DrawCircle(x, y, r, theta, alpha)
with skips of 11. Since a filled polygon
draws a circular arc centered at x and y with radius r. The argument theta is the
must be drawn with a single call of
FillPolygonO, the points for this figure
starting angle measured from 0 along the positive x axis (3 o'clock). The last
were put in a list for list invocation. argument is the angular extent of the arc (not the ending angle). Angles are
measured in radians; positive values indicate a clockwise direction.
There are defaults for the last two arguments. If theta is omitted, it
defaults to 0, and if alpha is omitted, it defaults to 21t, a complete arc. Thus,
DrawCircle(100, 200, 25)
draws a circle 50 pixels in diameter centered at (100,200).
FiIICircleO is like DrawCircleO, except that the arc is filled in the fore-
ground color. If a filled arc is not complete, it is wedge-shaped. Plate 4.2 shows
Figure 4.9 a window full of "paint splatters" produced by using FiIICircleO.
Figure 4.11 shows how a wheel can be produced by adding circles to the
spokes produced by spokesO (shown earlier in Figure 4.4).
82 Drawing Chapter 4 Chapter 4 Drawing 83

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.

DrawCircle(cx, cy, radius)


FiIlCircle(cx, cy, hubradius)
tireradius := radius + rimwidth
000000
°
every i := to tirewidth - 1 do
DrawCircle(cx, cy, tireradius + i)
Figure 4.12

"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.

DrawArc(x, y, w, h, theta, alpha)


In this procedure, x, y, w, and h specify a bounding rectangle within
which arcs are drawn; theta and alpha are the starting angle and extent as in
DrawCircleO. The center is at (x + w / 2, Y+ h / 2). If wand h are different, the arc
is elliptical. For example, the following code produces the drawing shown in
Figure 4.14:
DrawRectangle(10, 10, 380, 280)
DrawLine(10, 10, 390, 290)
DrawLine(10, 290, 390,10)
Figure 4.15
DrawArc(10, 10, 380, 280, &pi /4, &pi)

Elliptical Arc DrawCurveO is designed to draw smooth, visually attractive curves


Notice that if the bounding rectangle is through arbitrarily placed points. Catmull-Rom splines (Barry and Goldman,
not square, the angles are distorted 1988) are used to accomplish this. These curves are relatively complicated
according to the rectangle. Thus, a start- mathematically and it's not easy to predict or control their curvature. They are,
ing angle of n/4 corresponds to a line however, globally smooth and pass through all the specified points.
from the center through the bottom-
right comer of the rectangle.
Line Attributes
The default width of drawn lines is one pixel, as illustrated in the
preceding figures. A different line width can be set by using the Iinewidth
Figure 4.14 attribute. For example,
86 Drawing Chapter 4 Chapter 4 Drawing 87

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")

n DrawLine(O, 88, 500, 88)


DrawLine(O, 172, 500, 172)
# side road
Fg("light gray")
FiIIRectangle(120, 50, 80, 250)
# lane stripes

# 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.

y:= 165 # initial y coordinate A Highway Intersection


w:= 1 # initial linewidth
Notice the different results
every 1 to 9 do { produced by striped lines
WAttrib(llinewidth=" II w) # set Iinewidth on the main road and
DrawLine(O, y, 500, y) #·draw full-width line dashed lines on the side
Y +:= 2 * w + 1 # increment location road.
w +:= w / 3 + 1 # increment linewidth
}
I
WAttrib("linewidth=4") # draw thick arc I
I
DrawCircle(150, 140,50, &pi / 6, -4 * &pi / 3) I
I Figure 4.17
The attribute linestyle determines the style in which lines are drawn. The
default style is "solid", as illustrated in preceding figures. The line style "striped"
produces a dashed line in which pixels are first drawn in the foreground color,
followed by pixels in the background color, and so on.
88 Drawing Chapter 4 Chapter 4 Drawing 89

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

::; hi iiS rect(x, y + d, w, h - d)


}
# draw bottom 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

In Chapter 3, we showed the interesting effects that can be obtained


using an element of randomness in drawing. Here's a more sophisticated
94 Drawing Chapter 4 Chapter 4 Drawing 95

Random Rectangles $define OX 0.35 # x-coordinate step size


At the end of Chapter 7, $define OY 1.00 # y-coordinate step size
we'll show a complete pro- procedure mainO
gram that produces such local i, x, y, w
"paintings" in randomly
chosen colors. WQpen(lIwidth=1I II Width, IIheight=1I II Height) I
ll
stop(II*** cannot open window )

# draw 1I0cean wavesII by varying the line width


y := Horizon # initial y coordinate
w := 1 # initial line width
while y - w / 2 < Height do {
WAtlrib(lIlinewidth=1I II w) # set line width
OrawLine(O, y, Width, y) # draw line across window
Y +:= 2 * w + 1 # increment location
Figure 4.21 w +:= w / 3 + 1 # increment line width
}
Animation
# initialize for drawing suns
When something on the screen appears to move, this is called animation. WAtlrib(lIlinewidth=4") # set width of perimeter
Of course, nothing is really moving; it's an illusion produced by the way things Clip(O, 0, Width, Horizon) # don't draw below horizon
are drawn and erased. x := .3 * Width # initial x position
y := Radius + 10 # initial y position
The movement of a single object on a solid background is the simplest
form of animation. This is accomplished by drawing the object, waiting a small # draw animated sun sinking to horizon
fraction of a second, erasing it and redrawing it at a slightly different location, while y - Radius < Horizon do {
and repeating this as long as motion is wanted. Fg("black ll
)

OrawCircle(x, y, Radius) # draw sun


Delaying usually is accomplished by calling WDelayO. The timing WOelay(Oelay) # delay
depends on the needs of the particular application, and in some cases the Fg(lI w hite
ll
)

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

$define Width 500 # window width


$define Height 300 # window height
$define Horizon 85 # y-eoordinate of horizon
$define Radius 50 # size of the sun
$define Delay 100 # frame delay in msec
96 Drawing Chapter 4 Chapter 4 Drawing 97

Animated Sunset Fg("white")


FiIlCircle(b.x, b.y, Radius) # fill center
The gray circles show some
Fg(Ublack")
of the positions of the sun as
it sets. DrawCircle(b.x, b.y, Radius) # draw outline
if b.x < Radius I b.x > xmax then
b.dx := -b.dx # bounce horizontally
if b.y < Radius I b.y > ymax then
b.dy := -b.dy # bounce vertically
}
WDelay(lnterval) # delay between frames
Figure 4.22 }
end
When multiple objects are in motion, a single loop is used. After delay-
ing, all objects are erased before any are redrawn; the ones drawn later will Bouncing Balls
appear to be "in front" of the others where they overlap. The following program On paper, this is not very interesting.
displays ten balls bouncing lazily within the frame of the window. See Figure On the screen, this simple animation
4.23. has an odd fascination.
$define Balls 10 # number of balls o
$define Radius 10 # ball radius
$define MaxDisp 5 # maximum displacement o o
$define Interval 20 # delay per frame o o
record ball(x, y, dx, dy) # one ball and its velocity 00
CD
procedure mainO Figure 4.23
local xmax, ymax, blist, b
WOpen(l s ize=400,300") I stop(,'*** cannot open window") On a slow system, a "flash" may be noticeable when an object is erased
xmax:= WAttrib(lwidth") - Radius and redrawn. This can be mitigated by erasing just the part of the object that is
ymax := WAttrib(lheight") - Radius not to be overdrawn again, if this can be calculated easily. On a fast system,
blist := [] # list of balls flashing may happen so infrequently - and so quickly - that it poses no
every 1 to Balls do # place entries randomly problem. The erasure step can be skipped entirely for an object that just changes
put(blist, ball(?xmax, ?ymax, ?MaxDisp, ?MaxDisp» form without moving, such as a spinning globe.

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

x := ex + radius * eos(theta) # new position Sine Curve


y := ey + radius * sin(theta)
The problem with incorrect orienta-
DrawLine(\xprev, yprev, x, y) # draw only after first pass tion is that'it's easy to overlook.
xprev:= X # update old position
yprev:= y
} .... ..,

Figure Orientation

As mentioned in Chapter 3, y values in a window increase in a down-


ward direction, which is the opposite of the conventional Cartesian coordinate
v
system. This is why positive angular values are in the clockwise direction for
procedures that draw arcs.
In many cases, the orientation and angular direction are not important.
For example, many of the figures in this chapter are symmetric with respect to Figure 4.24
the horizontal axis or can be positioned with an initial angular offset to give the
desired appearance. In other cases, however, a figure that is drawn using
Cartesian geometry is upside down when viewed in a conventional frame of This problem can be fixed by changing the sign of the y value, a technique
reference. that works in general for cases like this:

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

As illustrated by the examples in this chapter, it often is useful to put


many coordinate pairs onto a list for a single invocation of a drawing procedure.
This is, in fact, the only way to use FiIIPolygonO and DrawCurveO to produce
drawings with an arbitrary number of computed coordinate pairs.
The number of arguments in such cases can be very large. This presents
a technical problem, since expression evaluation in Icon uses a stack to store
arguments temporarily. If there are too many arguments, stack overflow may
occur. If this happens, it may be possible to work around the problem by
increasing the stack size. This can be done by setting the environment variable
MSTKSIZE to a large value before the program is run.
102 Drawing Chapter 4 Chapter 4 Drawing 103

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

If skips is omitted, a null value is provided for it in a call of rstarsO. The


expression /skips succeeds and returns the variable skips only if skips has the
null value. In this case, 1 is assigned to skips.

Randomizing Drawings

As illustrated in the examples in this chapter, many interesting drawings


can be produced by introducing an element of randomness. Using Icon's built-
in pseudo-random number generator, every time ?x is evaluated, the next value
in a pseudo-random sequence is produced. The values in the pseudo-random
sequence are determined by a "seed", given by &random. The initial value of
&random is O.
Since &random always starts at 0, the "random" values produced by a
program are the same each time the program is run. In program development
and debugging, reproducible results may be helpful. For applications that are
ChapterS

Turtle Graphics

The procedures in Chapter 4 treat drawing in an essentially algebraic manner,


in terms of computing the coordinates of points. In order to draw a line, for
example, it is necessary to specify its beginning and ending points, even if the
line begins where the last drawn line ends. Such drawing often involves
trigonometric computations even in simple situations.
In many cases, it is easier and more natural to specify drawing in a
navigational manner in which drawing is done from a current point by moving
a specified amount in a specified direction, changing direction, and so on.
This chapter describes a system, called turtle graphics, that supports a
navigational form of drawing. We'll present the concepts and drawing proce-
dures first, followed by a description of how they are implemented by program-
mer-defined procedures. At the end of this chapter, we'll present an extended
example of the use of turtle graphics.
The procedures described in this chapter are part of the Icon program
library and can be incorporated in a program by using the declaration
link turtle

The Turtle Graphics System

Concepts

The turtle graphics system is based on turtle geometry, an approach to


teaching children about some aspects ofmathematics. In turtle geometry, a turtle
(which is conceptual rather than real) moves according to commands to trace out
various shapes. Turtle graphics comes from giving the turtle the ability to draw
as it moves.

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)

An example of the results is shown in Figure 5.3.

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.

Spirals Figure 5.2 Figure 5.3


Note the difference in appearance that the random factors produce. If
you repeatedly run the code given above, you'll see many more
variations, some of which may be very different in appearance from the
ones shown here.
110 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 111

Implementing Turtle Graphics return


end
We use the term procedure in this book to describe both procedures that
are built into Icon and programmer-defined ones that are implemented in Icon The procedure TDraw(n) is like TSkip(n), except that it also draws a line
code. Most of the procedures described in previous chapters are built into Icon, between the current position and the new one:
but a few are programmer-defined and are included in programs by link procedure TDraw(n)
declarations. Appendix E indicates which procedures are built-in and which local rad, x, y
ones are programmer-defined.
rad := dtor(T_deg)
Turtle graphics are implemented by programmer-defined procedures. x := T_x + n * cos(rad)
This section describes those procedures, illustrating how such a facility can be y := T_y + n * sin(rad)
written in Icon. DrawLine(T_x, T_y, x, y)
T_x:= x
State Information T_y:= y
The essential characteristic of turtle graphics is that information about return
the window in which the turtle is located, its position, and its heading are end
maintained by the implementation. The situation is much the same as when
you're going from your house to your car. You're always at some location and The direction is changed in a similar manner. For example, TRight(d) is:
facing in some direction (although your latitude, longitude, and heading on the procedure TRight(d)
compass usually are not of interest).
T_deg +:= d
In turtle graphics, state information is maintained in global variables: T_deg %:= 360 # normalize
global T_x, T_y # current location return
global T_deg # current facing direction
global T_stack # stack for turtle state end

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)

rad := dtor(T_deg) return


T_x +:= n * cos(rad) end
T_y +:= n * sin(rad)
112 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 113

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

T_y:= y initial TlnitO


return T_deg +:= d
T_deg %:= 360 # normalize
end
return
# TDrawto(x, y) -- draw line to (x,y)
end
procedure TDrawto(x, y)
# TLeft(d) -- turn left d degrees
initial TlnitO
procedure TLeft(d)
TFace(x, y)
initial TlnitO
DrawLine(T_x, T-V, x, y)
T_x:= x T_deg -:= d
T_y:= y T_deg %:= 360 # normalize
return return
end end
# TSkip(n) -- move forward n units without drawing # TFace(x, y) -- turn to face (x,y), unless already there
procedure TSkip(n) procedure TFace(x, y)
local rad
initial TlnitO
initial TlnitO if x -= T_x I Y -= T_y then
rad := dtor(T_deg) T_deg := rtod(atan(y - T_y, x - T_x»
T_x +:= n * cos(rad) return
T_y +:= n * sin(rad)
end
return
# TXO -- return current x location
end
procedure TX(x)
# TGoto(x, y) -- move to (x,y) without drawing
initial TlnitO
procedure TGoto(x, y)
return T_x
initial TlnitO
end
T_x:= x
T_y:= y # TYO -- return current y location
return procedure TY(y)
end initial TlnitO
return T_y
# TRight(d) -- turn right d degrees
end
procedure TRight(d)
116 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 117

# 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

link turtle axiom:X


$define Axiom "X" angle:22.5
$define Angle 22.5 length:3
$define Length 5 gener:5
$define Gener 5 X->F-[[X]+X]+F[+FX]-X
F->FF
rewrite := tableO
Variables can take the place of defined constants, with the program
# Specify the replacements parsing the specification and assigning appropriate values to these variables.
rewrite["X"] := "F-[[X]+X]+F[+FX]-X" The code to process the specification might look like this:
rewrite["F"] := "FF" rewrite := tableO
# Rewrite the axiom
# Read in the grammar
current_string := Axiom while line := readO do {
every 1 to Gener do { line? {
new_string := III' if c := tab(find("->")) then {
every c := !currenCstring do move(2)
new_string 11:= (\rewrite[c] I c) rewrite[c] := tab(O)
currencstring := new_string }
} else if keyword := tab(find(":")) then {
move(1)
# Interpret the string
value := tab(O)
every c := !currenCstring do { case keyword of {
case c of { "axiom": axiom:= value
"F": TDraw(Length) "angle": angle:= real(value) I stop("*** invalid line: ", line)
"f": TSkip(Length) "length": length:= integer(value) I stop("*** invalid line: ", line)
"+": TRight(Angle) "gener": gener:= integer(value) I stop("*** invalid line: ", line)
"_". TLeft(Angle) default: stop("*** erroneous keyword: ", line)
"[": TSaveO }
"]": TRestoreO }
} else stop("*** invalid line: ", line)
} }
}
A table provides a convenient way for specifying the replacements.
Characters for which no replacement is specified are not included in the table. Notice that keyword and replacement lines can appear in any order. Some error
In the rewriting code, a test is made for such characters, which are replaced by checking is done in the code above; you might think of more that should be done.
themselves.
Once the specification is read in, rewriting can be performed. Checks
It's not much more work to write a more general program that reads in should be provided to ensure that all the necessary parts of the L-system have,
the specification for an L-System and interprets it. First we need to pick a syntax in fact, been specified. Missing parts could be treated as errors, but providing
for representing L-systems. The following syntax is straightforward and easy to defaults for parts that are not fundamental to the L-system is more useful:
process. The axiom, angle, segment length, and desired number of generations
Ilength := 5
are given by a keyword syntax. The rules simply use -> for The Plant L-
Igener:= 3
system looks like this:
122 Turtle Graphics Chapter 5 Chapter 5 Turtle Graphics 123

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.

Window Input and Output


When a window is created, it is opened for both reading and writing. The
procedures WWriteO, WWritesO, WReadO, and WReadsO can be used for
writing text to windows and reading from them. In this respect, they are
analogous to writeO, writesO, readO, and readsO as used for files. For example,
while WWrite(read())
fills the window with lines from standard input.
Output written to a window scrolls automatically, just as if the window
were the screen of a typical text terminal. When a window is full, text flows off
the top to make room for more atthe bottom. Any graphics outputin the window
is scrolled along with the text.
Reading text is illustrated by
repeat {
WWrites("command? ")
case WReadO of {
"quit": exitO
"continue": break
"erase": EraseAreaO
}
}

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

procedure setline(spacing) # typeset current line


YOU don't know about me without you YOU don't know about me without you local x, y, s
have read a book by the name of The have read a book by the name of The
Adventures of Tom Sawyer; but that Adventures of Tom Sawyer; but that /spacing := minspc # default spacing to minimum
ain't no matter. That book was made by ain't no matter. That book was made by x := Border # set initial location
Mr. Mark Twain, and he told the truth, Mr. Mark Twain, and he told the truth,
mainly. There was things which he mainly. There was things which he
y := Border + rownum * WAttrib(lleading") - WAttrib(ldescent")
stretched, but mainly he told the truth. stretched, but mainly he told the truth. while s := get(words) do { # for each word
That is nothing. I never seen anybody That is nothing. I never seen anybody DrawString(x, y, s) # display the word
but lied one time or another, without it but lied one time or another, without it
was Aunt Polly, or the widow, or maybe was Aunt Polly, or the widow, or maybe x +:= TextWidth(s) + spacing # adjust position
Mary. Aunt Polly -- Tom's Aunt Polly, Mary. Aunt Polly -- Tom's Aunt Polly, }
she is -- and Mary, and the Widow she is -- and Mary, and the Widow words := [] # clear word list
Douglas is all told about in that book, Douglas is all told about in that book,
which is mostly a true book, with some which is mostly a true book, with some linelen := 0 # and its length
stretchers, as I said before. stretchers, as I said before. rownum +:= 1 # increment row number
return
end
Unjustified and Justified Text Figure 6,5
The addwordO procedure makes the key formatting decisions. It is
On the left, there is space at the end of each line. On the right, each line called once for each word to be typeset and accumulates those words in the
is "justified" by distributing the extra space between the words. global list words. When there is not enough room on the current line for the new
word, even with minimum spacing, addwordO calls setlineO to output the
Three procedures, along with the variables they share, handle the text pending list. Spacing is calculated to result in a completely full (justified) line.
layout. The initjustO procedure initializes the shared variables: A real value is used to avoid loss of the fractional part.
$define Border 30 # margin around edges procedure addword(s)
global rownum # current row number local wordlen
global words # words awaiting formatting wordlen := TextWidth(s) # width of word to add
global linelen # their total length excluding spacing
global maxlen # maximum line width # if line can't hold this additional word, flush it out
global minspc # minimum spacing between words if Iinelen + *words * minspc + wordlen > maxlen then {
# set with spacing that fills line to maximum size
procedure initjustO # initialize text justifier setline«maxlen -linelen) / real(*words - 1»
rownum := 1 # row number 1 }
words := [] # no words in list put(words, s) # add word to list
linelen := 0 # length is zero linelen +:= wordlen # update total length
maxlen := WAttrib("width") - 2 * Border
# max line size return
minspc:= TextWidth(" ") # minimum spacing end
return A simple main procedure breaks input lines into words and calls
end addwordO for each.
Text is displayedby the setlineO procedure, which displays the elements link graphics
of the global variable words with specified inter-word spacing: procedure main(args)
local line, cs
138 Text Chapter 6

cs := &ascii -- ' \t\l\r'


WOpen("size=375,375", "font=times,20") I
Chapter 7
stop(,'*** cannot open window")
initjustO # initialize justifier
while line := trim(read()) do line? {
while tab(upto(cs)) do # for each text word
Color
addword(tab(many(cs))) # call addword
}

setlineO # flush last line


WDoneO
Color is one of the most important and potentially rewarding components of
end computer graphics - and one of the most difficult. Color often is used just to
make an application visually attractive. But color has many uses beyond
Specifying Fonts Portably decoration: attracting attention to important events or situations, distinguishing
between different kinds of objects, and so on.
The set of available fonts varies from one system to another. Programs
that specify fonts should take this into account so that they do not produce The effective use of color requires much more than just a technical
inappropriate displays or become unusable when moved to different environ- mastery of rendering color. There are difficult issues related to color vision, the
ments. human cognitive system, the psychology of color, and even artistic taste. We
won't attempt to discuss these issues here, but if you're interested in digging
The best way to do this is to provide alternatives, which is easily done
deeper into such topics, see Rossotti (1983), Hope and Walch (1990), and
using Icon's goal-directed evaluation. An example is:
Gerritsen (1988), for example.
Font(("Frutiger" I "Univers" I "Helvetica" I "sans") II ",14")
Most of what follows assumes hardware that supports the display of at
The preferred font is given first, followed by less-desirable but possibly least a few colors; otherwise this chapter is mainly academic.
more commonly available alternatives, and finally ending with one of Icon's
generic font families. A size specification is appended to each family name in Specifying Colors
tum and the result is passed to FontO. Failure in FontO causes the next alterna-
tive to be tried. As soon as FontO succeeds, evaluation ceases and the chosen font Icon provides two ways to specify color: by name or by numerical value.
remains in effect.
If no alternative is accepted by FontO, the entire expression fails and the Color Names
font is left unchanged. This can happen if none of the choices is available in a 14-
pixel size. The example above ignores such failure, leaving the previous font in Icon supports a color-naming system, inspired by Berk et aL (1982), for
effect for lack of a better solution. the most commonly used colors. These names consist of simple English phrases
that specify hue, lightness, and saturation values of the desired colors. Hue
distinguishes among different colors, such as red, yellow, and purple. Lightness
measures the perceived intensity of a color. Saturation is a measure of the purity
of a color - how far it is from a gray of equal intensity. Pink is less saturated than
red.

139
140 Color Chapter 7 Chapter 7 Color 141

The syntax of a color name is


QUIT I
lightness saturation
ale I

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.

degree of precision in discriminating among colors, so small variations in


numerical specifications usually are unnoticeable.
The procedure ColorValue(s) produces the comma-separated decimal
r: S898
g: 39321
b: 37093
h: 176
5: 85
v: 60
I
form of the color s. ColorValue(s) fails if s is not a valid color specification.
ColorValueO works even if no window is open, but system-dependent color
names may not be recognized in this case.
Figure 7.3
If you're a bit pixilated at this point by the problems with color specifi-
cation, we suggest you turn to the Icon program library for help. One useful
program is trycolor, which displays a window with a colored circle. See Figure Using Color Specifications
7.2.
Color specifications can be used to set the foreground and background
colors using F90 and BgO. For example,
144 Color Chapter 7 Chapter 7 Color 145

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.

Color Printed in Black and White


Disappearing Grid Lines Figure 7.4
red If this figure didn't have labels, could you guess
How would you arrange for a variety of colors for grid lines? the actual colors?
green
Monochrome Portability blue

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

Library Resources Three-Dimensional Shading

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

Tips, Techniques, and Examples


Q C' oJ oJ

Random Rectangles ('


oJ oJ

("> ('
oJ oJ

In Chapter 4, we showed a program for generating random rectangles in


black and white. It's easy to adapt this program to select colors at random. All Figure 7.7
that's needed are lists of darknesses and hues:
darkness := ["light", "medium", "dark", "deep"] A three-dimensional effect is produced by framing an area using two
hue := ["red", "orange", "yellow", "green", "blue", "gray"] different colors. Part of the frame is lighter than the background, as if illumi-
nated, and part is darker, as if in shadow. Where the two meet in a corner, the
Then the foreground color can be set at random before drawing: boundary is mitered at a 45 0 angle, as in a real picture frame.
Fg(?darkness II" moderate II II ?hue) The eye expects illumination to come from above. This is why the
Unlike the black-and-white case, the result is more attractive if only filled shading cues are so effective, and why rotating the figure can raise and lower the
rectangles are drawn, rather than choosing randomly between lines and rect- dominoes. The same illusion can turn valleys into ridges when an aerial photo
angles: taken in the northern hemisphere is viewed with north at the top.
For a realistic appearance, it is important that the shading be done
FiIlRectangle(x, y, w - Gap, h - Gap)
consistently. In Figure 7.7, the colors framing the pips of the dominoes show that
Figure 7.6 shows an example of the results; also see Plate 7.3. the "light source" is above and slightly to the left, and the vertical parts of the

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.

11_ J Animation Using Mutable Colors

• .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

procedure mainO return


local cv end
WOpen(lsize=600,400") I stop("*** cannot open window") All rotors use the same set of twelve mutable colors. Because no redraw-
mcol := [] ing takes place, the number of helicopters has no effect on the speed of the rotors:
every 1 to colors do # get mutable colors it is just as quick to spin a hundred of them as a single one, since only the color
put(mcol, NewColor(lwhite")) I stop(,'*** cannot get mutable color") map is changing. Figure 7.8 shows a snapshot of this action.

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

This chapter describes the remaining operations related to windows: specifying


and drawing images, and reading and writing image files.

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:

Figure 8.1 bit value hex


DrawlmageO fails if the specification is invalid. It normally returns the row 1 2 4 8 16
null value, but if one or more of the requested colors cannot be allocated, the 1 .000. 11
procedure returns a count ofthe number ofcolors that cannotbe allocated. When 2 • •00. 13
a color cannot be allocated, either black or white (whichever is closer) is 3 .0.0. 15
substituted in the drawn image. 4 .00 • • 19
5 .000. 11
Palette Inquiries

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.

black verydark darkgray gray Built-in Patterns

••
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

images also are available without copyright


1;;11;;11011;;1
lttl'Ptl'Ptl'l't WIOIWW
1'Pt1'Pt1'l'tll't WWWWW
1'Pt1'Pt1'l't1'l't @!;;IWWW
ll'tll'tIl'tIPtl!'t
ll'tll'tll'tl'l't/llt
I;;I@@WW
0WWWW
restrictions.
ll'tll'tll't1'Pt1l't WWWWW
ll'tl'ltl'l'tl'l'tl'l'tltt
1'I't1'l't1'l't1'l't1'l't1tt
1'I't1'l't1'l't1'l't1'l't1'l't
1'I't1'l't1'l't1'l't1'l't19t III
1'I't1'l't1'l't1'l't1'l't1tt I'lIIlIIlIWWWq)
1'I't1'l't1'l't1'l't1'l't1'l't
1'I'tl'l'tl'l'tl'l'tl'l'tflltltt
1tt1tt1'l't1tt""1'I't1'l't
1'I'tl'l'tl'l'tl'l'tlttl'l'tJlltltt
Figure 8.6
1'I'tll't1'l'tll't1'l't1'l't1'l't1Pt
lttlttl'l'tl'l'tl'l'tl'l'tl'l'tltt
Ittl'l'tl'l'tl'l'tl'l'tll'tflltfllt
I'Ptfl'tfl'tlPtl'l'tl'l'tlttltt
1'I't1'l't1'l'tll'tll'tI'Ptl!'tlit
1'I'tl'l'tl'l'tl'l'tl'l'tl'l'tJlltttt
ll'tll'tll'tll'tll't1'Pt1l't1tt
Another example of the use of an image is shown in Figure 8.7.
....
1'I't1'lt1'l't1'l't1'l't1Pt
1'I't1'l't1'l't1'l't

Various Patterns Figure 8.5


It often is difficult to find a pattern that will produce a desired effect, but
it's fun trying.
162 Images Chapter 8 Chapter 8 Images 163

A User Dialog Library Resources


This example shows how intricate images - ones
you'd never want to construct by drawing in a The gpxop module, which is incorporated by link graphics, includes
program - can be used to dress up user interfaces. these procedures:
Capture(p, x, y, W, h) convert area to image string
Zoom(x1, y1 , w1, h1 , x2, y2, w2, h2) copy and distort rectangle
The gifsize module contains gifsizeO for determining the size of the GIF
image in a file.

Tips, Techniques, and Examples

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

Transparent GIF images


Chapter 9
Transparent GIFs are images in which one color is designated as "trans-
parent", so that pixels of that color are ignored when the image is read. This
allows a variety of interesting visual effects. Icon can read transparent GIFs, but Windows
it cannot write them. There are, however, a number of utility programs that can
create transparent GIFs, and many transparent GIFs can be found on the Web.
Without transparent pixels, images can be read only as complete rect-
angles. With transparency, images of arbitrary shape can be added without
erasing the window contents underneath the rectangle that contains the image.
Such images can be used without regard for the underlying background color or
pattern. Figure 8.8 shows an example of this technique. So far, we've described how to draw and place text in a single window. Now it's
time to look more closely at what windows are and what can be done with them.

Nearby Restaurants The Subject Window


Nearby Restaurants
In preceding chapters, we used only one window and performed all
*** Tentazione
Italian
*** Tentazione
Italian
operations on that window. This subject window is the window used by graphics
procedures. The subject window also is the value of the keyword &window.
**** Burly Bob's
Steak House
**** Burly Bob's
Steak House Some programs need more than one window, and hence it is necessary
to have a way of opening and referring to several different windows. The

* 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

icon: iconpos, iconlabel, iconimage fg=red


text: echo, cursor, x/ y/ row, col font=mono
...
pointer: pointer, pointerx, pointery, pointerrow, pointercol Figure 9.1
screen: display, depth, displayheight, displaywidth
In most circumstances, you don't need to know that a window is a
Most of these attributes already have been described. The rest are discussed later coupling of a canvas and a graphics context. For example, WAttribO works with
in this chapter. any attribute, querying it or setting it in the canvas or the graphics context,
When you create a window with WOpen(), the result is a coupling of a depending on where the particular attribute resides. The reason that the under-
new canvas with a new graphics context. For example, lying structure is important is that graphics contexts can be created and coupled
to canvases in different ways.
win1 := WOpen("size=200,100", "fg=red". "font=mono")
produces the coupling illustrated in Figure 9.1. Cloning
The procedure Clone(win1, win2 [, attributes]) creates a window that
couples the canvas of win1 with a new graphics context. The new graphics
context is initialized with the attributes of the graphics context for win2, except
for those given in additional arguments to CloneO. If any canvas attributes are
set, they are applied to the canvas shared by win1 and the new window. CloneO
fails if an attribute cannot be set to a requested value.
For example, if
win2 := WOpen(lsize=350,200", "fg=blue", "font=serif")
then
win3 := Clone(win1, win2, "font=sans")
produces the situation shown in Figure 9.2.
170 Windows Chapter 9 Chapter 9 Windows 171

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.

WINDOW WINDOW WINDOW

win1 win3 win2 WINDOW

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

CANVAS CANVAS string produced by image(win) is window_2:4(display)"/ which indicates the


I

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.

A large image can be viewed in a smaller window by copying a portion


of the larger window into the smaller one, using scrolling to adjust the portion
of the larger image that is currently displayed. Here's a procedure that does this, Figure 9.6
using a hidden window for the larger image and user keystrokes for adjusting
the "view". A succession of images like this but rotated slightly, when copied one
procedure scroll(win, image_file) after another, produce the appearance of rotation. Since the image has 1800
local ww, wh, img, x, y, w, h rotational symmetry, it's only necessary to have images that rotate halfway
around. For successive 100 rotations, 18 images are needed. If rotation is
ww := WAttrib(win, "width") # window width counterclockwise, they look like those in Figure 9.7.
wh := WAttrib(win, "height") # window height
# load image into hidden window
img := WOpen("image=" II image_file, "canvas=hidden") I fail
w:= WAttrib(img, "width") # image width
h := WAttrib(img, "height") # image height
x := y := 0 # start in upper-left corner
repeat {
CopyArea(img, &window, x, y, w, h, 0, 0)
case EventO of {
"I": if x > 0 then x -:= 1
"r": if x + ww < w then x +:= 1
"u": if y > 0 then y -:= 1
"d": if y + wh < h then y +:= 1
"q": break
}
}
Successive Images Figure 9.7
WClose(img) For smooth animation at a slow rate, more images may be required.
178 Windows Chapter 9 Chapter 9 Windows 179

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

Some window managers impose maximum and minimum dimensions


on canvases. This may be done silently - you may just get a window whose
dimensions are different from what you specified. A maximum may be imposed
to assure the window can be manipulated within the confines of the screen. A
minimum may be imposed so that the window manager has space for the items
in its frame.
You can, of course, find out the actual size of a window by using the size
attribute or the width and height attributes.
A window that is larger than specified can be a problem. For example, if
you open a small image and the window is actually larger than specified, the part
Chapter 10

Interaction

Windows provide a mechanism for communication between a program and its


user. The generality of this mechanism allows window-based applications to be
much more flexible than traditional command-oriented programs.

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")

Processing Event Queues repeat {


case EventO of {
The procedure EventO produces the next event and removes it from the &Ipress: {
queue. Events are produced in the order they occurred. If there is no event DrawPoint(&x, &y)
pending, EventO waits for one. For example, the following loop might be x :=&x
provided to allow the user to control the program: y:=&y
}
repeat { &Idrag: {
case EventO of { DrawLine(x, y, &x, &y)
"q" I &Ipress: exitO x :=&x
"c" I &mpress: break
y:=&y
"e" I &rpress: EraseAreaO }
} &rpress I &rdrag: {
} EraseArea(&x - 2, &y - 2,5,5)
If the event is a press of the q key or the left button, the program terminates. If }
the event is a c or a middle button press, the program breaks out of the loop. If "q": exitO
the event is an e or a right button press, the window is erased. All other events }
are discarded. Compare this example to the one using WReadO in Chapter 6. }
end
186 Interaction Chapter 10 Chapter 10 Interaction 187

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.

Audible Alerts Notification 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.

Tips, Techniques, and Examples

Using the Meta Key

To prevent an accidental keypress from causing unwanted actions, it


may be useful to require that the meta key be depressed in combination with a
keyboard event, as in
e:= EventO
if &meta & (e === "q") then exitO
Tracking Mouse Movement Figure 10.7
While this provides no guarantee, it does require a coordinated action on the part The pointer shape changes to a cross when inside the circle.
of the user.
198 Interaction Chapter 10 Chapter 10 Interaction 199

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": {

}
}

Falling Behind ... and Catching Up on Pending Events

If a program is computationally intensive, pending events may queue up


faster than the application is able to process them. If the size of the list produced
by PendingO grows large, the application may be able to "catch up" by skipping
some events.
204 Interaction Chapter 10

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"

ux1_ rectlus 1I8X1_ rad1 us


111"

+ discs +d1scs
rings rings

Figure 11.1 A Kaleidoscope Figure 11.2 A Kaleidoscope


The image is produced by drawing circles. The colors, sizes, and Pressing a mouse button on the word File pulls down a list of menu
positions of the circles are chosen at random. Circles are drawn until the items. Positioning the mouse pointer on an item highlights it, and when
specified density (number of simultaneous circles) is reached, at which the mouse button is released, the operation is performed.
point the oldest circle is erased and a new one drawn. This continues
until the user intervenes. The notations @S and @Q shown in the menu indicate keyboard
shortcuts that can be used to accomplish the same thing as menu items. By
The pause button allows the user to suspend drawing, which is not convention, @ is used to indicate that the meta modifier key is held down while
resumed until the user presses this button again. The reset button clears the the following character is pressed, either in upper- or lowercase. For example,
image and starts the drawing process from scratch. The sliders allow the user to pressing the q key while holding down the meta key is the same as selecting quit
control the speed of drawing, the density, and the minimum and maximum radii from the File menu.
for circles. At the bottom, the user can choose between discs (solid circles) or
rings (outlines). The File menu allows the user to take a snapshot of the image If snapshot is selected from the File menu, a dialog box pops up for the
user to specify the name of a file in which to save the image, as shown in Figure
or quit the application, as shown in Figure 11.2.
11.3.
208 User Interfaces Chapter 11 Chapter 11 User Interfaces 209

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

Saving an Image Figure 11.3 Enable


x outline. It's only visible when it's high-
lighted.
As shown in Chapter 10, the user can enter a file name in the text-entry
field of the dialog box. Pressing return or clicking on Okay dismisses the Figure 11.5
dialog and the image is saved in the named file. Clicking on Cancel
cancels the operation, and no image is saved. X-box buttons and buttons with squares or circles at the left give the
impression that they can be set. Consequently, they are best used for toggles.
Interface Tools Radio Buttons
Icon's interface tools are called vidgets (for virtual input gadgets). They Radio buttons are collections of buttons in which only one button is on
include tools for allowing the user to make selections, set numerical values, and at any time. Pushing a button turns it on and highlights it, and turns off the
so on. There also are tools that serve only to decorate interfaces with text and previously selected button.
lines.
Only one style is provided for radio buttons; it is shown in Figure 11.6.
Buttons

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.

A Menu Selecting a Submenu Item


Releasing the mouse button with the Submenus themselves can have
mouse cursor positioned on an item submenus. There is no limit to this
selects that item. If you drag off the list hierarchical structure, but more than
and release the mouse button, the list two or three levels are confusing to
cut disappears and no item is selected. most users. Some users do not like
copy
paste submenus at all.
J<1Hr.
fill >
stroke >
invert
crop
tnt ratII

Figure 11.7 Figure 11.9


212 User Interfaces Chapter 11 Chapter 11 User Interfaces 213

Text-Entry Fields Sliders


Sliders can have different sizes, as
We've already described text-entry fields, which are designed for use in
shown in this figure.
dialogs such as those shown in Chapter 6. Figure 11.10 shows six text-entry
fields.

Text-Entry Fields

H.e: Each field has a label and space for the


EIiployee m:
user to enter text. The maximum num-
ber of characters that are allowed can
BirthdatB:
be specified; this determines the width
Ilarltal Status: of the field. A suggested value for a Figure 11.11
CUrrent Position: field can be given, as shown in the last
Des1 red Posi tion: Ifry cook text-entry field.
Scrollbars

Scrollbars are very similar to sliders, although they have a different


Figure 11.10 visual representation. See Figure 11.12.

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!

Figure 11.14 Lines


Although lines are only decoration
II
ll
,

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- - - - . . , -
...

drag on the tt..b to dIange the SC81e I


Regions
There are four region styles: sunken, \

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

! qun_cb( ...) ................. procedure mainO


Choosing Vidgets
/ ............
/ ............ end
/ ............
/ ............
In some cases, the same functionality can be provided by different kinds
/ ...... procedure qun_cbO
saveO
of
/ exnO
/ end
/
. Both sliders and scrollbars provide a way to specify a numeric value in
/ a range. Both menus and radio buttons can be used to provide the user with one
/
/ choice among several.
/ program
/ The only significant differences between sliders and scrollbars are their
/
/ visual appearances and the functionality by which a user can set a value.
/ Choosing between the two kinds of vidgets is more a matter of taste than design.

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

Building a Visual Interface

This chapter describes the process of building a visual interface, using as an


example the kaleidoscope program introduced in the previous chapter.
Good visual interface design is a difficult and complicated subject that is
beyond the scope of this book. In this and subsequent chapters, we'll illustrate
common usage by example and comment from time to time on design consid-
erations. For more information on the subject, see Apple (1987), Open Software
Foundation (1991), and Laurel (1990).

Planning the Interface


It's important to have a good idea of the functionality of an application
before designing an interface for it. It's not necessary, however, to completely
implement the functionality of the application before starting to build the
interface.
The process of building an application with a visual interface usually is
iterative, with focus shifting between the functionality of the interface and the
interface itself. Design of the interface may suggest additional functionality or
cause features to be cast in ways that are easily represented in the interface.
The interface for the kaleidoscope application presented in the last
chapter is the end product of a process that involved many changes and
refinements. We won't attempt to recapitulate the process here. Instead we'll
sketch how we might have done it.
The size of the application canvas is an important consideration. Changes
in the size of the application canvas after an interface is laid out may result in
unnecessary work. Screen space often is a limiting factor. Many personal
computers have screens that are only 64D-by-480 pixels. In designing an appli-
cation that is intended to be portable to various platforms, it's wise to work
within these dimensions.
221
222 BUilding a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 223

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

The VIB Application Figure 12.2


The menus at the top provide operations needed to use VIB. The icons
below the menus represent the vidgets described in the previous The Canvas Dialog Figure 12.3
chapter. The inner rectangle represents the canvas of the interface being Text can be entered and edited, as described in Chapter 11. The tab key
developed.
moves the cursor from one text-entry field to the next.
The icons below the VIB menu bar from left to right represent buttons, To build the interface for the kaleidoscope, we don't need the procedure
radio buttons, menus, text lists, text-entry fields, sliders, scrollbars, regions, name field or the dialog window toggle. These features are described in the next
labels, and lines. Clicking on one of these icons creates a vidget of the corre- chapter and in Appendix M. The window label refers to the label for the
sponding type and places it on the application canvas. We'll show instances of application, which we can enter now. The default width is reasonable for our
this later. design. The critical dimension is the height, which needs to be increased to
accommodate the display region and menu bar, with some space for a visual
226 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 227

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

The Kaleidoscope Canvas Figure 12.5


Although the size of the application canvas can be changed at any time,
it's generally a good idea to know approximately what size the appli-
Specifications for the Kaleidoscope Canvas Figure 12.4 cation canvas should be at the beginning, since changing it later may
The values for the canvas size cannot exceed the dimensions of the VIB involve moving many vidgets.
window. If we try to set a dimension larger than the VIB canvas, we'll
be warned and have to provide acceptable values before we can go on. The question is what to do next. There are quite a few vidgets to create,
We can, however, resize the VIB window if it's not large enough for the configure, and position. We can't be sure (unless we have a detailed drawing of
application canvas we want. the interface and are certain it's the way we want it) that the canvas size is correct.
A good approach at this point is to start laying out the portions of the interface
that depend most on the canvas size. One approach is to start by subdividing the
canvas into its main areas; first the menu bar that divides the canvas vertically,
and then the kaleidoscope display region, which is the most crucial part of the
area below the menu bar.
As mentioned in the previous chapter, lines provide visual cues for the
user (and also for the interface designer). Therefore, the first vidget we'll create
is a line to separate the menu bar from the rest of the canvas.
A vidget is created by pressing the left mouse button on its icon and
228 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 229

dragging it onto the canvas. For a line vidget, this produces a short horizontal
line, as shown in Figure 12.6.

. '"

• •

Dialog for a Line Vidget Figure 12.7


Every vidget has an ill field that serves to identify it. The dialog for a
newly created vidget provides a suggested value, but the ill can be set
to any string of printable characters excluding colons (:), backslashes (\),
A Line Vidget Figure 12.6 and double quotes n. Since the kaleidoscope interface has only one
line, we changed the ID to line. We could use something more mne-
The initial location of the line and its length and orientation aren't
monic like menu bar line.
important; they're easily changed.

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. -
. .-

p--.-.-: . -----.-.---.-: ..=.l


-
File ,,_.aft ".select
-r b;di A

r
"
A

r ,

ID: reg1 ... i


callbac1c:
x: 1. width: '400 invisible
v: )z height: 400 sunken
, grooved
• raised

The Configured Region Figure 12.11


Satisfactory placement may require some experimentation. When a
vidget is selected, it can be moved one pixel at a time using the arrow
keys on the keyboard.
The Edited Region Dialog Figure 12.10
If we don't like the effect of a raised region, we can change it later. In fact, Now we're ready to create the vidgets at the left side of the application
we may not know if the effect is what we want until we are able to run canvas. We'll start with the menu, which completes that region of the canvas.
the kaleidoscope. As explained in the next chapter, it's always possible Figure 12.12 shows the result of creating a menu vidget and the dialog for it.
to go back to VIB to modify an interface.
234 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 235

r . --
;

!
<.
1 1 I"

.. I

-,
- ..
. .. .."
x: r0-
y: 6S

0k8y I C8nce1

cancel

"

The Edited Menu Dialog Figure 12.13


A Menu Dialog Figure 12.12
There is no limit to the number of items in a menu, but if the menu, when
Since there's only one menu in the kaleidoscope application, we could pulled down, is too long to fit in the window, not all the items will be
leave the ID as it is, but an ID that corresponds to the name of the menu available. Once the dialog is dismissed, this can be tested by pressing
will make it easier to identify later on. the middle mouse button on the menu.

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

A Button Dialog Figure 12.15


The Canvas with Three Vidgets Figure 12.14 Buttons have more attributes than their apparent simplicity might
It may not seem like we're making much progress, but it didn't take suggest.
long.
The label for the button needs to be changed to pause. Here we're
Next we'll start creating the vidgets to the left of the display region, configuring the button for temporarily stopping the display. Since it's a toggle
working from top to bottom. Figure 12.15 shows the result of creating a button button, we need to check that box. The callback can be eliminated, since the state
and the dialog for it. of the button can be obtained with VGetState(). Ordinarily, we'd pick a style that
clearly shows it's a toggle when displayed on the interface, but since we have
only one other button, and it's not a toggle, we decided to use the same
appearance for both of them, for which the default style is our preference. (The
dialog default option doesn't concern us here - see Chapter 14.) Figure 12.16
shows the edited dialog.
238 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 239

pause reset

libel: .-se
,._....
ID'
r -- ;;;=====:;:::::;:;:::;;;;=1
.-se

ll: 41 • regular lIlltU..


y: 141 chedc
to9lIle
wtddl: 42 circle
height: ,20 lCbox dialog default

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

-- - .... -- - .. .. -

f" ," I: .. ' !

..
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
'-.,

The Slider in Place Figure 12.19


The Edited Slider Dialog Figure 12.18 Getting the size of the slider just right may take some experimentation.
Since the dialog has not yet been dismissed, the newly created slider is
shown in its original size and orientation. Three more sliders are needed. We could repeat the process we used for
the first slider, but we can save some work by making copies of the first slider.
Figure 12.19 shows the slider after it has been positioned. Entering @C when a vidget is selected makes a duplicate of the selected vidget.
(That's c with the meta key held down, as described in Chapter 11.) The new
vidget won't be where we want it, and we'll have to change some of its attributes,
but it will be the same size as the vidget from which we made the copy, which
is what we want in our layout. Figure 12.20 shows the four sliders in place.
242 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 243

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

reset pause reset

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

Prototyping the Application Figure 12.26


The prototype comes up in a separate window. We can click on buttons,
pull down the menu, move a slider thumb, and so forth. A listing of the
activated vidgets and their callback values is written to standard The File Menu Figure 12.27
output, where we can see if we're getting what we expected. It's wise to save an interface frequently while working on it. The refresh
item in the File menu redraws the application canvas in case something
Pressing q with the mouse cursor not on any vidget dismisses the is drawn incorrectly, as sometimes happens. Notice the keyboard
prototype, and we can go back to VIB to make adjustments or just admire our shortcuts; we've used @ P already.
work.
The Edit menu provides for copying, deleting, and aligning objects. See
VIS Menus Figure 12.28.

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

not return control to the program. GetEventsO is appropriate for applications


that are entirely"event-driven" and perform processing only in response to user
procedure _ :
events and the resulting callbacks. 111. . . 1....1:
Examples of different kinds of event loops are illustrated in following II1ddl:
height: d1e1Dl1 111. . .
chapters.
Cence1
Multiple VIB Interfaces
A single visual interface window is adequate and appropriate for most
VIB Canvas Dialog Figure 12.31
applications. There are situations, however, that require more than one inter-
face. Typical examples are multi-user games and painting and drawing applica- The name specified - control in this case - is used in place of ui for the
tions. two procedures in the interface code. In the example above, they are
named controLattsO and controlO.
Before designing an application with more than one interface window,
consider the problems: managing multiple windows adds programming com- Each interface has its own vidgets. The same vidget ID can be used in
plexities, and in single-user situations an application with more than one more than one interface, but care should be taken not to use the same callback
window requires the user to change his or her focus of attention. In addition, name in more than one interface unless a single procedure handles callbacks
applications with multiple windows require more screen space than single- from more than one window.
window applications.
If the names are changed to draw_attsO and drawO in draw.icn, the
application might begin as follows:
VIS Considerations
controLvidgets := controlO
VIE can handle only one interface section in a file. There are ways of
fooling VIE by editing the lines it places at the beginning and end of its interface draw_vidgets := drawO
code, but these are clumsy. It's usually best to break the application into multiple
files with each interface in a separate file. Note that each interface produces its own table of vidgets.
When VIB creates a file, it provides a main procedure. In the program There's a problem here: A " ui" procedure opens a window only if
organization we're using here, such main procedures should be deleted and a &window is null. If &window is null when controlO is called, and nothing else is
main procedure provided in the program that links the interface code. (VIB does done, drawO does not open a window, but instead overwrites what controlO
not add a main procedure when editing an existing file, so this needs to be done drew in the window it opened. This problem is easily fixed:
only once.)
controLvidgets := controlO
The code for a VIB interface contains two procedures, which are named &window := &null
uLatts and ui by default. The procedure ui_attsO returns the attributes used to draw_vidgets := drawO
open the interface window. In most applications, it is not needed, but it can be If there is need to refer to the windows later in the program, they can be
used to open the window with added or changed attributes. The procedure uiO
assigned to variables as follows:
opens the interface window if &windows is null, draws its vidgets, and initializes
the interface. controLvidgets := controlO
controLwin:= &window
To avoid conflicting declarations for these procedures in multiple inter-
&window := &null
faces, their names need to be changed. This is done easily in VIB by specifying
draw_vidgets := drawO
a procedure name in the canvas dialog, as shown in Figure 12.31.
draw_win := &window
258 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 259

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

Consider, for example, the interface shown in Figure 12.32:


Figure 12.33

Reversible drawing is useful if the text on the interface changes during


execution, as in
WAttrib( OI drawop=reverse Ol )
DrawString(status.ax, status.ay + status.ah, status_old) # erase old
DrawString(status.ax, status.ay + status.ah, status_new) # write new
WAttrib("drawop=copyOl)
status_old := status_new # for next time
264 Building a Visual Interface Chapter 12 Chapter 12 Building a Visual Interface 265

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.

There's no requirement that each vidget be serviced by a distinct callback depth:


procedure. Sometimes itis handy to use a shared callback procedure for multiple
vidgets. Grouping related functions together can make the code clearer.
Figure 12.34
Of course, there must be a way to distinguish among the actions that can
invoke a callback procedure. One possibility is to check the vidget ID field, as in This problem is easy to solve. Since VIB uses a monospaced (fixed-width)
this example for handling button calls: font, adding a blank to the shorter labels brings them into alignment, as shown
in Figure 12.35.
procedure button_cb(vidget, value)
case vidgetid of { Aligned Text-Entry Fields
"blur": { }
In this illustration, blanks were added after the colons of the
"sharpen": { } width:
first and third labels. To align the labels at the right, put blanks
} before, not after, the shorter labels.
height:

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

procedure menu_cb(vidget, value)


case value[1] of {
"New": { }
"Open": { }
"Save": { }
"Cut": { }
"Copy": { }
"Paste": { }
}

The individual case processing can be followed by code to perform


common actions, such as redrawing the display or marking a file as having been
updated. This need for common code may be the most important factor in
grouping vidgets to share callback procedures.

Aligning Text-Entry Vidgets

The length of a text-entry vidget's label determines the position of its


text-entry field. When related text-entry vidgets are aligned vertically, their
fields won't line up if their labels differ in length. Consider, for example, the
three fields shown in Figure 12.34.
Chapter 13

Completing an Application

In this chapter we describe what's necessary to complete an application with a


visual interface and how to put all the parts together. It continues with the
kaleidoscope application as a concrete example. A complete listing of the
program is given at the end of the chapter. Other applications with visual
interfaces are described in Chapters 15 and 16.
Usually, the construction of an application with a visual interface pro-
ceeds iteratively, with work being done alternatively on the interface and the rest
of the program, as illustrated in Figure 13.1.

interface VIB text editor program code

The Program Construction Cycle Figure 13.1


We have separated the interface construction in the last chapter and the
coding of the rest of the program in this chapter for pedagogical
reasons. Like all other program construction, the real process is more
complicated and less organized than the ideal one - and the finished
product tells little of what went into achieving it.

267
268 Completing an Application Chapter 13 Chapter 13 Completing an Application 269

Program Organization # State information


global draw_list # list of pending drawing parameters
The general organization of a program with a visual interface is de- global reset # non null when display needs resetting
scribed in the previous chapter. That organization fits the kaleidoscope applica-
tion nicely. The first section contains global variables whose values are set from the
code provided by VIB. The second section contains global variables whose
Program Header values the user can change using vidgets on the interface. The final section
contains global variables that relate to the state of the running program.
The program header for an application with a visual interface usually is Then there is a record declaration for circles:
not much different from the program header for any program. There are link
declarations, global declarations, and sometimes preprocessor definitions. # Record for circle information
The kaleidoscope program requires three procedure libraries: record circle(off1, off2, radius, color)
link interact Finally, there are defined constants for default values:
link random $define DensityMax 100
link vsetup $define SliderMax 10.0
The library interact contains a procedure snapshotO that is used for saving $define SliderMin 1.0
images. The library random contains a procedure randomizeO that assures
somewhat different results every time the kaleidoscope program is run, provid- The Main Procedure
ing a bit of variety. The library vsetup is needed for all VIB applications and
includes the graphics procedures. The main procedure is simplicity itself:

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

# Interface globals end

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.

pane := Clone("bg=black", "dx=" II (vidgets[" region"].ux + half), Callback Procedures


"dy=" II (vidgets["region"].uy + half), "drawop=reverse")
Clip(pane, -half, -half, size, size) Most of the callback procedures are quite simple and serve mainly to set
global variables that control the display. For example, the callback for the vidget
272 Completing an Application Chapter 13 Chapter 13 Completing an Application 273

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

# Parameters that can be set from the interface colors := []


every put(colors, PaletteColor(lc1", !PaletteChars(l c 1")))
global delay # delay between drawing circles
global density # number of circles in steady state pause := vidgets["pause"]
global draw-proc # drawing procedure VSetState(vidgets[ldensity"], (density / DensityMax) * SliderMax)
global max_off # maximum offset of circle VSetState(vidgets[ldelay"], delay)
global min_off # minimum offset of circle VSetState(vidgets[lmin_radius"], min_radius)
global max_radius # maximum radius of circle VSetState(vidgets["max_radius"], max_radius)
global min_radius # minimum radius of circle VSetState(vidgets[lshape"], "discs")
global scale_radius # radius scale factor
# Get graphics context for drawing.
# State information
half := size / 2
global draw_list # list of pending drawing parameters
global reset # nonnull when display needs resetting pane := Clone("bg=black", "dx=" II (vidgets["region"].ux + half),
global state # nonnull when display paused "dy=" II (vidgets[" region"].uy + half), "drawop=reverse")
Clip(pane, -half, -half, size, size)
# Record for circle data
return
record circle(off1, off2, radius, color)
end
$define DensityMax 100
$define SliderMax 10.0 # shared knowledge
procedure kaleidoscopeO
$define SliderMin 1.0
# Each time through this loop, the display is cleared and a
procedure main() # new drawing is started.
init() repeat {
kaleidoscope() EraseArea(pane, -half, -half, size, size) # clear display
end draw_list := [] # new drawing list
reset := &null
procedure init() # In this loop a new circle is drawn and an old one erased, once the
randomize() # specified density has been reached. This maintains a steady state.
vidgets := ui() repeat {
while (*PendingO > 0) I WGetState(pause) do {
root := vidgets[" root"]
ProcessEvent(root, , shortcuts)
size := vidgets[" region"].uw
if \reset then break break next
if vidgets["region"].uh -= size then stop("*** improper interface layout")
}
delay:= 0 putcircleO
density := DensityMax / 2.0 WDelay(delay)
max_radius := SliderMax # scaled later
min_radius := SliderMin # Don't start clearing circles until the specified density has been
scale_radius := (size /4) / SliderMax # reached.
draw_proc := FiIICircie if *draw_list > density then c1rcircleO
280 Completing an Application Chapter 13 Chapter 13 Completing an Application 281

} 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

max_radius := min_radius return vsetup(win, cbk,


VSetState(vidgets["max_radius"], max_radius) [" :Sizer:: :0,0,600,455:kaleido",],
} ["delay:Slider:h:1:42,120,100,15:1.0,0.0,0.0·,delay_cb],
["density:Slider:h: 1:42,180,100,15:0.0,10.0,10.0·,density_cb],
reset := 1
["file:Menu:pull::12,3,36,21 :File",file_cb,["snapshot @S·,"quit @Q"]],
return ["labeI01 :Label:::13,180,21,13:min",],
["labeI02:Label:::152, 180,21,13:max·,],
end
[llabeI03:Label:::13,240,21,13:min",],
[llabeI04:Label:::152,240,21,13:max",],
procedure reset_cb(vidget, value)
[llabeI05:Label:::13,300,21,13:min",],
reset := 1 ["labeI06:Label:::152,300,21, 13:max",],
return ["labeI07:Label:::7,120,28, 13:slow",],
["labeI08:Label:::151, 120,28,13:fast",],
end [llbLdensity:Label:::67,160,49,13:density",],
["lbLmax_radius:Label:::43,280,98,13:maximum radius" ,],
procedure shape_cb(vidget, value) ["lbLmin_radius:Label:::44,220,98,13:minimum radius" ,],
draw_proc := case value of { ["lbLspeed:Label:::74,1 00,35, 13:speed",],
"discs": FiIICircle [lline:Line:::O,30,600,30:",],
"rings": DrawCircie ["max_radius:Slider:h: 1:42,300,100,15:0.0,10.0,10.0",max_radius_cb],
} ["min_radius:Slider:h: 1:42,240,100,15:0.0,10.0, 1.0",min_radius_cb],
["pause:Button:regular:1 :33,55,45,20:pause",],
reset := 1 [lresetButton:regular::111,55,45,20:reset",reseCcb],
return ["shape:Choice::2:66,359,64,42: ,shape_cb,["discs","rings"]],
I

[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

return We deliberately designed the kaleidoscope application to use a single


end window that contains both the interface controls and the kaleidoscopic display.
Although it's often easier to use several windows for an application, a single
#===«vib:begin»=== modify using vib; do not remove this marker line window, when it will do, unifies the application and presents a more attractive
procedure ui_attsO appearance to the user.
return ["size=600,455", "bg=pale gray", "Iabel=kaleido"] In the case of the kaleidoscope application, an obvious alternative to the
end design we chose is to use two windows, one for the user interface and the other
for the display itself.
procedure ui(win, cbk)
284 Completing an Application Chapter 13 Chapter 13 Completing an Application 285

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.

Providing Other Drawing Shapes

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

NoticeO, OpenDialogO, and SaveDialogO are just interfaces to a more


general procedure, TextDialogO, which allows customized dialogs for text entry.
TextDialogO, in its most general usage, is rather complicated because text-entry
dialogs canbe complicated. Defaults are provided, however, to make TextDialogO
easy to use if all its generality is not needed.
The general form is:
TextDialog(captions, labels, defaults, widths, buttons, index)
The argument captions is a list of caption lines that serve the same
purpose as the multiple arguments in NoticeO. The arguments labels, defaults,
and widths are lists that give the details of a sequence of text-entry fields. The
argument buttons is a list of buttons, and index is the index in buttons of the
default button.
TextDialogO returns the name of the button that was selected to dismiss
the dialog. The global variable dialog_value is assigned a list containing the
values of the text fields at the time the dialog was dismissed.
287
288 Dialogs Chapter 14 Chapter 14 Dialogs 289
, __ • - - - - - -0

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

A Selection Dialog Color Dialogs


The default choice is highlighted, as shown. The user can pick
Plde. suit: another choice by clicking on another choice button. The procedure ColorDialogO allows the user to pick a color interactively
using either the RGB or HSV color model. Its general form is
ColorDialog(captions, color, callback, value)
The argument captions serves the same purpose as it does in preceding
Oby cancel dialog procedures. The optional argument color is a reference color that is
displayed at the bottom of a rectangular area where color is displayed. The initial
Figure 14.3 color for the rest of the rectangle is color, if provided, otherwise the current
foreground color. The optional argument callback is a procedure that is called
whenever the user adjusts the color setting. It is called as
Toggle Dialogs
callback(value, setting)
The procedure ToggleDialogO allows the user to set several toggles (onl
where setting is the current color setting and value is the final argument,
off states) at the same time in one dialog. Its general form is
otherwise unused, from the ColorValueO call. Thus, the user can track changes
ToggleDialog(captions, toggles, states, buttons, index) in the color setting, and value can be used to pass along an arbitrary value to the
The arguments captions, buttons, and index serve the same purpose as caller of ColorValueO. The final setting is returned as a string in dialog_value.
they do in TextDialogO and SelectDialogO. The argument toggles is a list of The following procedure call illustrates the use of ColorDialogO.
toggle names and the argument states is a list of their respective states: 1 for on,
ColorDialog("Pick a color, any color", "black")
null for off. The defaults for omitted arguments follow the same rules as for
TextDialogO and SelectDialogO. A list of the states of the toggles that the user The dialog produced by this call is shown in Figure 14.5.
chooses is returned in dialog_value.
The following procedure call illustrates the use of ToggleDialogO. A Color Dialog

ToggleDialog( The reference color, in this case black, is at


"Controls:" , Pick • color. eny color the bottom of the rectangle, as mentioned
earlier.
["generate report", "stop on bad data", "trace"],
[1, 1, ]
)
The dialog produced by this call is shown in Figure 14.4.
A Toggle Dialog
The toggles that are on are highlighted. The user can change
c:.trals: the state of any toggle by clicking on its button.
"'I'lIO report
stlIp lIIl bed dlo
Figure 14.5
tl'llCe

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.

A Custom Dialog for Setting Line Attributes

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 Default Button repeat {


Only non-toggle buttons can be used to dismiss a attributes(win, atts) == "Okay" I fail
1....1: dialog. Toggle buttons canbe used to indicate onl off
ID: okay
states. # Set attributes from table.
callback: r -
IC: 124 WAttrib(win, Ilinewidth=" II atts["linewidth"]) I {
y: (iM Notice("lnvalid linewidth.")
widtt1: fGO next
height: f3iI }
WAttrib(win, Ilinestyle=" II atts[llinestyle"])
return
}
Figure 14.8
end

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

Tips, Techniques, and Examples Text-Entry Fields in Custom Dialogs

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

Hidden Dialogs Chapter 15


If you're using an interface that produces dialogs and everything seems
frozen, the problem may be a dialog window that is waiting to be dismissed but
is hidden behind the interface window (or another window). This may happen
when there is a dialog window and you inadvertently click on the interface
window behind it, bringing it forward to obscure the dialog window. A solution
A Pattern Editor
is to move windows around until you find the dialog window and then bring it
to the front.
The trouble is that if you don't realize the source of the problem, you may
kill the application unnecessarily. This is one of those things that are learned by
painful experience.
This chapter describes a fairly substantial application. As in the preceding
chapter, we'll start by describing the functionality of the application and its
visual appearance. Then we'll discuss various aspects of the implementation:
the design of the visual interface, how data is represented, the overall structure
of the program, event handling, some special problems, and a few details of the
coding. A complete listing of the program is given at the end of the chapter.

The Application

Basic Functionality

This application is designed to enable a user to create and modify bi-


level patterns interactively. It is intended primarily for patterns that are to fill
areas, but it can be used also for small bi-Ievel patterns to be drawn by
DrawlmageO·
Features of the application are:
• easy pattern editing with the ability to set individual bits of the pattern
to the foreground or background color
• pattern transformations, such as shifting and rotating
• constant visual feedback on the appearance of the patterns when used
as a fill pattern
• saving and loading bi-Ievel pattern specification strings

299
300 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 301

The User Interface

The functionality described above can be cast in many ways. Figure 15.1
shows our choices and the visual layout we've designed.

Menu Operations Figure 15.2


It's customary to put miscellaneous items in a menu labeled File, even
if they have nothing to do with files. That seems better than having
several menus, and it's so commonly done that users don't think much
about it. So far, no one has found a better name.
The Pattern Editor Figure 15.1
The load item in the File menu allows the user to load a new pattern from
The layout of an application such as this is largely a matter of taste. The
menu bar is at the top, a conventionallocation. An editing grid, the main
a file. A dialog box is presented, in which the user can specify the file name.
area of activity, is in the center. A rectangle filled with the current The save and save as items allow the user to save the current pattern in
pattern is at the right, the natural direction of eye movement. Transfor- a file as a string specification. The save item uses the current file name, while
mation buttons are at the left. save as allows the user to specify a different file name. The new item allows the
user to start a new pattern. A dialog box is provided for the user to specify the
Clicking the left mouse button on a cell on the grid sets the corresponding dimensions of the new pattern. When a new pattern is specified, the editing grid
bit of the pattern to the foreground. The cell is filled in black to indicate that the is sized automatically to accommodate the pattern and is centered in the editing
bit is set. Dragging the mouse with the left button pressed sets the bits corre- region. The quit item serves its usual purpose.
sponding to the cells that are passed over. The right mouse button clears bits to
the background in a similar fashion. Keyboard shortcuts are provided as indicated in the menu items. The
conventions for keyboard shortcuts are the same as those described in Chapter
When the mouse is clicked on a button at the left, the pattern is trans- 11.
formed in the manner indicated by the icon for the button. The top four buttons
shift the pattern circularly by one bit in the direction indicated. The next four
buttons flip the pattern as indicated. Next are three buttons for rotating the Program Design
pattern: 90° counterclockwise, 90° clockwise, and 180°. The bottom-left button
clears the pattern, setting all bits to the background. The bottom-right button The Interface
inverts the foreground and background.
The File menu is shown in Figure 15.2. Given a rough sketch of what the interface should look like, the construc-
tion of the interface in VIS is relatively straightforward. The results are shown
in Figure 15.3.
302 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 303

The pattern shown in Figure 15.1 has the string representation


"8.#8142241818244281". The matrix representation of this pattern would be
imx:= [
[1. 0. 0. 0. 0. 0. 0. 1].
[0,1.0.0.0.0.1.0].
[0, 0, 1. 0. 0. 1. 0. 0].
D: [0.0.0,1.1.0.0.0],
r-----II
I
I
callb1dc: [0,0,0,1.1,0,0,0],
I
I wtdth: 160 +inY1s1ble [0. 0. 1, 0. 0. 1. 0. 0].
I
I 5UIlken
I
I
I
height: 240
vlraoved
[0. 1, 0, 0. 0. 0. 1, 0],
I
I
I raised [1,0,0,0,0.0,0.1]
I
I
I
]
I
_____1I

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

procedure imxcreate(i, j) Program Organization


local imx
The program organization is the same as for the application in the
imx := list(i) preceding chapter:
every !imx := listU, 0)
1. Program heading, including link declarations, defined constants, and
return imx global declarations
end 2. The main procedure
Each row must be a distinct list because of Icon's pointer semantics, as described 3. Callback, initialization, and support procedures
in Chapter 2.
4. Interface specifications provided by VIE
The list-of-strings version is simpler:
Except for the main procedure and the procedure in the VIE specification,
procedure imxcreate(i, j) procedures are ordered alphabetically.
return list(i, repl("O", j»
Program Heading
end
Separate strings are not needed for each row, since any operation that changes Link declarations are needed for procedures that manipulate patterns, as
a string creates a new one. well as for the vidgets:

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

Main Procedure with Initialization DrawRectangle(patCxpos - 1, patt_ypos -1, patCwidth + 1,


patCheight + 1)
The pattern editor is entirely event driven; that is, it only performs # Set up the grid and pattern areas.
actions in response to user events. For event-driven programs, the procedure
GetEventsO is used to handle events: setupO
procedure mainO return
vidgets := uiO end
initO The current pattern, initially an 8-by-8 blank one, is kept in the global
variable imx. The global variable loadname contains the current file name for the
GetEvents(vidgets["root"), , shortcuts)
pattern. The initial name, until the user specifies a new one, is "untitled.ims". The
end global variable touched keeps track of whether the current pattern has been
changed and hence may need to be saved if the user loads a new pattern or quits
Here are relevant sections of the initialization. See the complete listing at
the application. A nonnull value indicates the pattern has been changed but not
the end of the chapter for all the details.
saved.
procedure initO
The procedure setupO - which is called whenever a new pattern is
# Get layout values from the vidgets created - sizes, positions, and draws the editing grid:
xform_xpos := vidgets["xform").ax procedure setupO
xform_ypos := vidgets["xform").ay local row, col, x, y
grid_xpos := vidgets["grid").ax
vbits := *imx
grid_ypos := vidgets["grid"].ay
hbits := *imx[1]
grid_width := vidgets["grid").aw
grid_height := vidgets["grid"].ah cellsize := MaxCell # compute cell size
cellsize >:= grid_height / vbits
cellsize >:= grid_width / hbits
imx := imxcreate(8, 8) # initial 8-by-8 blank pattern
grid_xoff := grid_xpos + (grid_width - hbits * cellsize) /2
loadname := "untitled.ims" # default file name
grid_yoff := grid_ypos + (grid_height - vbits * cellsize) /2
touched := &null # pattern not yet modified
EraseArea(grid_xpos, grid_ypos, grid_width, grid_height)
# Draw the transformation buttons. place(row, col, pattern) draws the
every x := 0 to hbits * cellsize by cellsize do
# pattern at the specified row and column of the transformation region.
DrawLine(grid_xoff + x, grid_yoff, grid_xoff + x,
place(O, 1, "16,#3ffe6003408141c143e140814081408140814081II II grid_yoff + vbits * cellsize)
"40814081408160033ffeOOOO") # shift up every y := 0 to vbits * cellsize by cellsize do
place(1, 0, "16,#3ffe6003400140014001401140195ffd4019401 II II DrawLine(grid_xoff, grid_yoff + y, grid_xoff + hbits * cellsize,
"140014001400160033ffeOOOO") # shift left grid_yoff + y)
place(1, 2, "16,#3ffe600340014001400144014c015ffd4c014401" II
every row := 1 to vbits do
"40014001400160033ffeOOOO") # shift right
every col := 1 to hbits do
if imx[row, col] == "1" then
# Set up graphics context for pattern area and draw border. FiIIRectangle(grid_xoff + (col-1) * cellsize + 1,
grid_yoff + (row - 1) * cellsize + 1, cellsize - 1, cellsize - 1)
pattgc := Clone("fillstyle=textured")
308 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 309

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

setupO procedure file_cb(vidget, menu)


} case menu[1] of {
return "load @L": 10adO
"new @N": newO
end "save @S ": saveO
If xformO succeeds, the pattern matrix is marked as changed, and setupO "save as": save_asO
is called to redraw the editing grid and view area. It is necessary to call setupO, "quit @Q": quitO
since some transformation on patterns that are not square change their width }
and height.
return
The transformation procedure combines the row and column values in
end
a single string, so that the desired transformation can be selected in a single case
expression: All of the items in the file menu communicate with the user via dialogs.
Here's the procedure for loading a new pattern:
procedure xform(row, col)
# Load pattern from a file.
imx := case (row II"," II col) of {
"0,1": imxshift(imx, -1, "v") # shift up procedure 10adO
"1,0": imxshift(imx, -1, "h") # shift left local input, load_imx
"1,2": imxshift(imx, 1, "h") # shift right check_saveO I fail
"2,1": imxshift(imx, 1, "v") # shift down
"4,0": imxflip(imx, "r") # flip diagonally, NE/SW repeat {
"4,1": imxflip(imx, "v") # flip vertically case OpenDialogO of {
"5,0": imxflip(imx, "I") # flip diagonally, NW/SE "Okay": {
"5,1": imxflip(imx, "h") # flip horizontally if input := open(dialog_value) then break else
"7,0": imxrotate(imx, "ccw") # rotate counterclockwise Notice("Can't open" II dialog_value II".")
"7,1": imxrotate(imx, "cw") # rotate clockwise }
"7,2": imxrotate(imx, 180) # rotate 180 degrees "Cancel": fail
"9,0": imxcreate(vbits, hbits) # clear }
"9,1": imxinvert(imx) # invert }
default: fail load_imx := imstoimx(readims(input» I { # get a new matrix
} Notice("No pattern specification.")
return c1ose(input)
fail
end }
If the location does not correspond to a button, the procedure fails, as c1ose(input)
noted above. Otherwise the appropriate procedure is called to produce a
transformed pattern matrix, which is reassigned to imx. The transformation if (*Ioad_imx I *load_imx[1]) > MaxBits then {
procedures are contained in the library file imxform.icn, which is linked at the Notice("Pattern too large.")
beginning of the program. fail
}
The callback procedure for the file menu uses a procedure to perform the else {
specified action, since most actions also are available as keyboard shortcuts. imx := load_imx
312 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 313

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

initO # initialize everything "save as": save_asO


"quit @Q": quitO
# Now process events. The procedure shortcutsO looks at keyboard
}
# events regardless of where they occur in the window.
return
GetEvents(vidgets["root"), , shortcuts)
end
end
# Process events on the editing grid.
# Check to see if user wants to save pattern before creating a new one.
procedure grid_cb(vidget, e)
procedure check_saveO
local x, y, row, col
if \touched then {
# Event must be of right type and in bounds.
case SaveDialog(, loadname) of {
"Yes": { if e === (&Ipress I &rpress I &Idrag I &rdrag) then {
loadname := dialog_value row := (&y + cellsize - grid_yoff) / cellsize
saveO I save_asO I fail col := (&x + cellsize - grid_xoff) / cellsize
} if ((row I col) < 1) I (row> vbits) I (col> hbits) then fail
"No": return if e === (&Ipress I &Idrag) then setbit(row, col, "1 ")
"Cancel": fail else setbit(row, col, "0")
} return
} }

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

"Okay": { local output


new_vbits := integer(dialog_value[1]) & output := open(loadname, "w") I {
new_hbits := integer(dialog_value[2]) I { Notice("Can't write " II loadname II ".")
Notice("Non-integer specification. ") fail
next }
}
write(output, imxtoims(imx»
if «new_vbits I new_hbits) > MaxBits) I c1ose(output)
«new_vbits I new_hbits) <= 0) then {
Notice("lnvalid pattern size.") touched := &null
next return
}
else { end
imx := imxcreate(new_vbits, new_hbits)
touched := &null # Save pattern in a file with another name.
setupO procedure save_as()
return local output
}
} repeat {
"Cancel": fail case SaveDialog(, toadname) of {
} "No": return
} "Cancel": fail
"Yes": {
end if output := open(dialog_value, "w") then break else
Notice("Can't write" " dialog_value II ".")
# Place button. }
procedure place(row, col, pattern) }
}
Drawlmage(xform_xpos + col * ButtonSize,
xform_ypos + row * ButtonSize, pattern) write(output, imxtoims(imx»
close(output)
return loadname := dialog_value
end touched := &null
return
# Terminate session.
end
procedure quitO
check_saveO I fail # Set or clear bit in pattern.
exitO procedure setbit(row, col, c)
end local x, y
if imx[row, col] == c then
# Save pattern. return # skip processing if no-op
procedure saveO imx[row, col] := c # modify the pattern
320 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 321

touched:= 1 return

Y := grid_yoff + (row - 1) * cellsize + 1 end


x := grid_xoff + (col- 1) * cellsize + 1
# Check for keyboard shortcuts.
if c == "1" then FiIlRectangle(x, y, cellsize - 1, cellsize - 1)
else EraseArea(x, y, cellsize - 1, cellsize - 1) procedure shortcuts(e)

draw_pattern 0 if &meta then


case map(e) of { # fold case
return "I": 10adO
end "n": newO
"q": quitO
# Set up editing grid and pattern area based on imx. "s": saveO
}
procedure setupO
local row, col, x, y return

vbits := *imx end


hbits := *imx[1]
# Perform transformation.
cellsize := MaxCell # compute cell size
cellsize >:= grid_height / vbits procedure xform(row, col)
cellsize >:= grid_width / hbits imx := case (row II"," II col) of {
grid_xoff := grid_xpos + (grid_width - hbits * cellsize) / 2 "0,1": imxshift(imx, -1, "v") # shift up
grid_yoff := grid_ypos + (grid_height - vbits * cellsize) /2 "1,0": imxshift(imx, -1, "h") # shift left
"1,2": imxshift(imx, 1, "h") # shift right
# Draw the editing grid. "2,1": imxshift(imx, 1, "v") # shift down
EraseArea(grid_xpos, grid_ypos, grid_width, grid_height) "4,0": imxflip(imx, "r") # flip diagonally, NE/SW
"4,1": imxflip(imx, "v") # flip vertically
every x := 0 to hbits * cellsize by cellsize do "5,0": imxflip(imx, "I") # flip diagonally, NW/SE
DrawLine(grid_xoff + x, grid_yoff, grid_xoff + x,
"5,1": imxflip(imx, "h") # flip horizontally
grid_yoff + vbits * cellsize)
"7,0": imxrotate(imx, "ccw") # rotate counterclockwise
every y := 0 to vbits * cellsize by cellsize do "7,1": imxrotate(imx, "cw") # rotate clockwise
DrawLine(grid_xoff, grid_yoff + y, grid_xoff + hbits * cellsize,
"7,2": imxrotate(imx, 180) # rotate 180 degrees
grid_yoff + y)
"9,0": imxcreate(vbits, hbits) # clear
every row := 1 to vbits do "9,1": imxinvert(imx) # invert
every col := 1 to hbits do default: fail
if imx[row, col] == "1" then }
FiIIRectangle(grid_xoff + (col - 1) * cellsize + 1, return
grid_yoff + (row - 1) * cellsize + 1, cellsize - 1, cellsize - 1)
end
draw_patternO I {
Notice("Can't draw pattern.")
# Handle events on transformation buttons.
fail
} procedure xform_cb(vidget, e)
322 A Pattern Editor Chapter 15 Chapter 15 A Pattern Editor 323

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

A callback procedure is needed to record which symmetries are in effect.


Finally, the procedure setbitO needs to be modified to handle symmetries, so that Chapter 16
it performs its operation on all cells symmetric to the one on which the mouse
action occurred. If you are not familiar with symmetry, you may have to think
about the code a bit - but in that case, you'll learn something.
Adding new transformations and symmetric editing involves compara- Facial Caricatures
tively straightforward changes to the interface and the code in the program
itself. There are other features that are useful but require different design and
coding techniques. One useful set of features involves the selection of a portion
of the pattern on the editing grid, the ability to move the selection, and facilities
for cutting, copying, and pasting. Most of the bits and pieces needed for these
features can be found in this book. We'll leave it to you to put them together.
In this chapter we'll look at another large interactive application. As before, we'll
describe it first from the user's standpoint and then from the programmer's.
Many techniques and facilities from the pattern editor will reappear here, but
we'll emphasize the novel aspects. A complete listing appears at the end of the
chapter.

The Application

Basic Functionality

This program interacts with the user to produce a caricature from a


photograph or other image of a face. With the mouse, the user indicates the
contours of facial features such as the eyes, nose, and ears. The program then
compares these contours with those of an "average" face, exaggerates the
differences, and presents the result as a caricature. Figure 16.1 shows an example
of such a caricature. The techniques used here are due to the artist and scientist
Susan Brennan (Brennan 1985). The program was inspired by A. K. Dewdney's
article in Scientific American, reprinted in Dewdney (1988).

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.

Babbage and his Caricature Figure 16.1


Charles Babbage was an early inventor of calculating machinery. The
photograph was taken about 1850.

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 Interface as Seen in VIB Figure 16.4


A Dual-Mode Display Figure 16.3 Of the four regions indicated by dashed lines, only the largest accepts
events; the others are used just for placement.
In this mode, curves are drawn through the selected points while the
original image is still visible through a screen.
Control Flow
Program Design An early prototype implementation used a simple loop to collect all of
the points of the face before displaying anything. While this was easy to
The Interface program, it was difficult to use: All the data had to be collected before the results
could be seen, and once the caricature was displayed, no further changes were
Figure 16.4 shows the layout created using VIB. Two solid lines divide possible.
the main window sections. The File and Display menus are placed along the top.
332 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 333

In its current form, the program is event-driven, allowing the user to }


switch back and forth between input and display at any time or to save the work }
in progress for reloading later. This requires that the input, output, and display return
procedures be capable of working with incomplete data sets.
end
To keep the application down to a manageable size, there is no provision
for removing or adjusting points once they have been entered. This would Each curve of the face is copied and prepended with the window argument; this
clearly be desirable, and extensions such as this are discussed later. list becomes the argument list of the drawing procedure. Special checks handle
incomplete curves and the pupils of the eyes.
Data Representation
There are two situations where it is necessary to move and scale the
coordinates in a face structure:
One data structure is key to the implementation: the representation of the
contours of a face. The solution must be able to represent and display the • preparing the standard face for drawing in the guide region
digitized points from the image, the sample face, and the generated caricature. • aligning the standard face with the input face before creating a
A face is composed of curves specified by coordinate pairs. Because both caricature
DrawLineO and DrawCurveO accept coordinate lists consisting of alternating x Translation and scaling are handled by this procedure:
and y values, we use that same representation for a facial curve.
# scaleface(f, g) -- return copy of face f scaled to overlay face 9
A face structure, in tum, is a list of curves - a list of lists. The order of
lines within a face is mostly arbitrary; we have tried to choose an order that procedure scaleface(f, g)
makes sense for input. However, the left and right pupil locations are critical for local fl, fr, gl, gr, fx, fy, gx, gy, m, r, t, curve
scaling and aligning faces, so they appear first. Each pupil is specified by a single fl := f[1] I fail # left iris
coordinate pair, .the shortest possible curve. fr := f[2] I fail # right iris
It's possible for a face, or even part of a curve, to be incomplete: this gl := g[1] I fail # target left iris
happens, for example, during construction. Null values take the places of gr := g[2] I fail # target right iris
missing coordinates. fx := (fl[1] + fr[1]) /2.0 # x offset of f
fy := (fl[2] + fr[2]) / 2.0 # Y offset of f
Drawing a face is straightforward. To allow drawing with either gx := (gl[1] + gr[1]) /2.0 # x offset of 9
DrawCurveO or DrawLineO, the actual drawing procedure is passed as an gy := (gl[2] + gr[2]) / 2.0 # Y offset of 9
argument to the following procedure: m := (gr[1] - gl[1]) / real(fr[1] - fl[1]) # multiplier
# drawface(win, f, proc) -- draw face from curve list using proc r := []
procedure drawface(win, f, proc) every curve := copy(!f) do {
local curve if /curve[-1 ] then
put(r, curve) # incomplete placeholder
every curve := copy(!f) do { else {
if /curve[-1] then # null-eoordinate put(r, t := [])
next # incomplete curve while put(t, m * (get(curve) - fx) + gx) do
if *curve = 2 then put(t, m * (get(curve) - fy) + gy)
FiIlCircle(win, curve[1], curve[2], PupilRadius) }
else { }
push(curve, win)
proc! curve return r
end
334 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 335

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 stdface # standard (average) face guide_xoff := vidgets["guide"].ax


global guideface # scaled/translated guide face gUide_yoff := vidgets["guide"].ay
global sketch # points from subject face
The main procedure continues by setting up the special-purpose win-
A list of descriptions (such as "left ear") is stored in descriptions. The order of dow bindings described earlier:
this list corresponds to the order of the curves in a face data structure.
display_win := Clone("linewidth=2")
The global variable stdface contains the coordinates of the standard, Clip(display_win, display_xoff, display_yoff, display_width,
"average" face. Its coordinates are not directly useful; guideface contains the display_height)
same face after scaling and positioning for use as the guide face. Finally, sketch overlay_win := Clone(display_win, "fillstyle=masked",
contains the coordinates of a constructed caricature. "pattern=4,#9696")
Two global variables, interpreted as indices into a face structure, specify targeCwin := Clone("drawop=reverse")
the interpretation of a mouse dick that places a point: Clip(target_win, guide_xoff, guide_yoff, guide_width, guide_height)
global tcurve # index of current curve to place The pattern and fill style in overlay_win are used for overlaid drawing. Because
global tpoint # index of point within curve overlay_win is a clone of display_win, not the subject window, it inherits the line
The last few global variables handle general bookkeeping: width and clipping attributes of display_win.

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:

global mode # Image/Draw/Dual mode descriptions := []


global distortion # distortion factor (0.0 = undistorted) stdface := []
every spec := ![
Main Procedure with Initialization ["left pupil", 145, 203], # must be first
["right pupil", 255, 203], # must be second
The main procedure controls the program initialization: everything that ["top of left eyebrow", 101, 187, 105, 177, 126, 168, 153, 170, 177,
must be done before entering the event loop. Most of the logic is contained in the 176,181,185],
main procedure itself. Two large sections that would overwhelm it by their bulk ["top of right eyebrow", 219,185,223,176,247,170,274,168,295,
are bundled separately. 177,299,187],
Execution begins by opening the main window, changing the cursor, ["chin line", 180, 350, 200, 345, 220, 350]
and extracting region information: ] do {
procedure mainO put(descriptions, get(spec))
338 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 339

put(stdface, spec) Event Processing


}
return Four types of events are processed: menu events, keyboard events, slider
events, and mouse events.
end
Menu and keyboard event handling is simple; it echoes that of the
The every-do loop iterates though a large list of lists, assigning a sublist to spec pattern editor and is not listed here. Although two menus are used, one callback
on each iteration. Each sublist contains a label and some coordinates. The label handler can serve both, because the menu entries are distinct.
is removed from the list and put in the description file, and the remaining
coordinates become one curve in a face structure. Arranging the fundamental The slider callback is also simple:
data this way makes it easy to reorder the curves while maintaining synchroni- # slider_cbO -- handle adjustments of distortion slider
zation between the descriptions and coordinate lists. Unfortunately, reordering
also affects the interpretation of coordinates stored in data files, so once the order procedure slider_cb(vidget, val)
is finalized and serious use begins, further rearrangement becomes infeasible. setdist(val) # update and display value
When iniCstdfaceO returns, the main procedure sets some display if mode = ImageMode then # ensure that mode includes drawing
parameters: mode := DualMode
redisplayO # draw updated sketch
mode := ImageMode # display mode
setdist(O) # distortion factor return

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

redisplayO # redraw if new curve done }


target(tcurve, tpoint) # update target display else {
} tcurve := 1
tpoint := 1
&rrelease: { # right button skips a curve
}
every !sketch[tcurve] := &null # clear all points on curve
if (tcurve +:= 1) > *sketch then # Find the next unset point.
tcurve := 1
until /sketch[tcurve, 2 * tpoint - 1] do {
target(tcurve, 1) # set target to next curve
tpoint +:= 1 # advance to next point
}
if tpoint > (2 * *guideface(tcurve]) then {
} tpoint := 1 # need to move to next curve
return tcurve +:= 1
}
end if tcurve > *guideface then
A mouse event has no meaning if all the points have been specified; the initial tcurve := 1 # wrapped around list of curves
check detects this and ignores the event. if tcurve = curve & tpoint = point then {
tcurve := tx := ty := &null # there are no unset points
In the &Irelease case, the coordinates from a left-button click are stored return
in the current structure. If the caricature is currently on display, and if this was }
the last point on a curve, then the caricature is redrawn to incorporate the new }
curve. Finally, the target of the next point is set.
# Draw a target on the guide face.
In the &rrelease case, which calls for skipping the current curve, all of the
points in the curve are set to the null value and the target is advanced. tx := guideface[tcurve, 2 * tpoint - 1]
ty := guideface[tcurve, 2 * tpoint]
Setting the target is a complex operation. It involves updating both the FiIICircle(targeCwin, tx, ty, TargetRad1)
guide display and some global variables. The procedure targetO advances the FillCircle(targeCwin, tx' ty, TargetRad2)
targetto the next unsetpointthat is at or beyond the given indices in the evolving
face. Here is the code: # Display the prompt.

# target(curve, point) -- display next point to be placed x := prompCxoff + prompt_width / 2


Y := prompCyoff + prompCheight / 2
procedure target(curve, point) s := "locate" II descriptions[tcurve]
local s, n, x, y n := *guideface[tcurve]
static tx, ty if n > 2 then
# Undraw the previous target and erase the previous prompt. s 11:= " (select" II n / 2 II" points)"
CenterString(x, y, s)
FiIlCircle(target_win, \tx, \ty, TargetRad1)
FiIICircle(targeCwin, \tx, \ty, TargetRad2) return
EraseArea(prompCxoff, prompt_yoff, prompCwidth, prompCheight) end
# Start from specified place unless the pupils remain unplaced. The static variables tx and ty retain the coordinates of the last drawn
if \sketch[1, 1] & \sketch[2, 1] then { target; they contain null values if no target is currently displayed. This informa-
tcurve := curve tion is not needed by any other procedure, so tx and ty are local to target(). They
tpoint := point are declared static so that their values persist from one call to the next.
342 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 343

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

global tcurve # index of current curve to place drawface(&window, guideface, DrawLine)


global tpoint # index of point within curve # Load and display an image; exit if dialog is cancelled.
# miscellaneous globals newO I exitO
global pointfile # file name for saving coordinates # Enter event loop.
global touched # has data changed since last save?
GetEvents(vidgets["root"], , shortcuts)
global mode # Image/Draw/Dual mode
global distortion # distortion factor (0.0 = undistorted) end

# main program # caricatureO -- draw sketch distorted by current distortion factor

procedure mainO procedure caricatureO


local I, r, y local base, face, win

# 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

"Cancel": fail # iniCgeometryO -- extract layout information from vidgets


}
procedure iniCgeometryO
return
guide_xoff := vidgets["guide"].ax
end guide_yoff := vidgets["guide"].ay
guide_width := vidgets["guide"].aw
# distort(f, b, m) -- return distortion of face f from face b by factor m guide_height := vidgets["guide"].ah
procedure distort(f, b, m)
local r, t, i, j, curve, base display_xoff := vidgets["image"].ax
display_yoff := vidgets["image"].ay
r := [] display_width := vidgets["image"].aw
every i := 1 to *f do { display_height := vidgets["image"].ah
base := b[i]
put(r, curve := copy(f[i]))
prompCxoff := vidgets["prompt"].ax
if /curve[-1] I /base[-1] then prompCyoff := vidgets["prompt"].ay
next # incomplete placeholder prompCwidth := vidgets["prompt"].aw
every j := 1 to *curve by 2 do {
prompt_height := vidgets["prompt"].ah
curveO] +:= m * (curveO] - base[j]) dmeter_xoff := vidgets["dmeter"].ax
curveU + 1] +:= m * (curveU + 1] - baseU + 1]) dmeter_yoff := vidgets["dmeter"].ay
} dmeter_width := vidgets["dmeter"].aw
} dmeter_height := vidgets["dmeter"].ah
return r return
end end

# 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

["bottom of right eyelid", 230, 206, 257, 194,284,207], ] do {


["bottom of left eye", 120,208,142,213,170,206], put(descriptions, get(spec»
["bottom of right eye", 230, 206, 258, 213, 280, 208], put(stdface, spec)
["left iris", 144, 195, 132, 201, 144, 211, 156, 201, 145, 195], }
["right iris", 255, 195, 244, 201, 256, 211, 268, 201, 256, 195],
return
["left side of nose", 190, 193, 190,219, 190, 244, 186,257, 189,271,
200,277], end
["right side of nose", 210, 193, 210, 219, 210, 244, 214, 257, 211,
271, 200, 277], # 10adO -- load coordinate data
["left nostril", 177,250,171,258,169,269,174,277,183,271,198,
procedure 10adO
277],
local input, face
["right nostril", 223, 250, 229, 258, 231,269,226,277,217,271,202,
277], check_saveO I fail
["top of upper lip", 152, 318, 172, 311, 188, 306, 200, 311, 212, 306, repeat {
228,311,248,318], case OpenDialog("Load coordinates:") of {
["bottom of upper lip", 152,318,170,319,186,317,200,319,214, "Okay": {
317,230,319,248,318], if input := open(dialog_value) then break else
["top of lower lip", 152,318,172,318,186,317,200,319,214,317, Notice("Can't open" II dialog_value)
228,318,248,318], }
["bottom of lower lip", 152,318,169,327,184,333,200,335,216, "Cancel": fail
333,231,327,248,318], }
["left ear", 75, 212, 61,201,54,213,58,233,64,260,75,285,85, }
281], if sketch := rdface(input) then {
["right ear", 325, 212, 339, 201, 346, 213, 342, 233, 336, 260, 325, c1ose(input)
285, 315, 281], pointfile := dialog_value
["top of head", 60, 317, 28, 254, 31,189,46,108,82,47,141,4,200, touched := &null
1,259,4,318,47,354, 108,369, if mode -= ImageMode then
["hairline", 79, 200, 90,168,104,141,119,120,143,104,172,100, redisplayO
200,99,228,100,257,104,281,120,296,141,310,168,321, target(1, 1)
200], return
["left side of face", 84, 194, 79, 232, 86, 273],
["right side of face", 316, 194, 321, 232, 314, 273], }
["jaw", 85, 272, 93, 311, 108, 342, 133, 369, 167, 392, 200, 399, 233, else {
392,267,369,292,342,307,311,315,272], Notice("Not a valid coordinate file")
["left eye line", 131,221,148,220,166,214], c1ose(input)
["right eye line", 234, 214, 252, 220, 269, 221], fail
["left cheek line", 167,264,154,278,145,294], }
["right cheek line", 233, 264, 246, 278, 255, 294],
["left cheekbone", 87, 269, 95, 280, 101,292], end
["right cheekbone", 313, 269, 305, 280, 299, 292],
["chin cleft", 200, 377, 200, 389], # menu_cbO -- handle menu selections
["chin line", 180, 350, 200, 345, 220, 350] procedure menu_cb(vidget, menu)
354 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 355

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 }

check_saveO I fail return


repeat ( end
case OpenDialog("Load image:") of {
"Okay": ( # quitO -- terminate session
if rdimage(dialog_value) then
return procedure quitO
if f := open(dialog_value) then ( check_saveO I fail
c1ose(f) exitO
Notice(dialog_value II" is not a valid image")
} end
else
Notice("Can't open II dialog_value)
II
# rdface(f) -- read face coordinates from file f
} procedure rdface(f)
"Cancel": fail local face, line, curve, i, n
356 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 357

face := [] target(1, 1) # reset to start with first point


while line := read(f) do line? {
=":" I next # ignore line missing ":" # Ensure that current mode includes the image, and update the display.
curve:= [] if mode = DrawMode then
while tab(upto(&digits)} do { mode:= ImageMode
n := integer(tab(many(&digits))) EraseArea(display_xoff, display_yoff, display_width, display_height)
if n -= 0 then n +:= image_xoff else n := &null redisplayO
put(curve, n) return
tab(upto(&digits» I break end
n := integer(tab(many(&digits)))
if n -= 0 then n +:= image_yoff else n := &null # redisplayO -- display image and/or drawing, depending on mode
put(curve, n)
procedure redisplayO
}
if mode -= DrawMode then
put(face, curve)
CopyArea(image_win, display_win, , , , , image_xoff, image_yoff)
}
if mode -= ImageMode then
# Validate the number of curves and points. caricatureO
if *face -= *stdface then fail return
every i := 1 to *stdface do
end
if *face[i] -= *stdface[i] then fail
return face # saveO -- save coordinate data
end procedure saveO
local output
# rdimage(filename) --load image from file, failing if unsuccessful
if /pointfile then
procedure rdimage(filename) return save_asO
local curve
output := open(pointfile, "W") I {
image_win := WOpen(limage=" " filename, "canvas=hidden") I fail Notice("Can't write II pointfile)
II

pointfile := &null fail


touched := &null }
# Calculate offsets that center the image in display area. wtface(output, sketch)
c1ose(output)
image_xoff:= display_xoff + touched := &null
(display_width - WAttrib(image_win, ·width"» / 2
image_yoff:= display_yoff + return
(display_height - WAttrib(image_win, "height"» / 2 end
# Initialize a new set of (unset) points.
# save_asO -- save coordinate data in alternate file
sketch := []
every curve := !stdface do procedure save_asO
put(sketch, list(*curve, &null» local output
358 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 359

repeat { # setdist(val) -- set and display distortion value, in percent


case SaveDialog("Save coordinates?", "") of {
"No": return procedure setdist(val)
"Cancel": fail distortion := val / 100.0
"Yes": GotoXY(dmeter_xoff, dmeter_yoff + dmeter_height)
if output := open(dialog_value, "w") then break else WWrites(right(integer(val), 4), "%")
Notice("Can't write" II dialog_value)
} return
} end
wtface(output, sketch)
c1ose(output) # shortcuts() -- check event for keyboard shortcut
pointfile := dialog_value procedure shortcuts(e)
touched := &null
if &meta then case map(e) of {
return "I": load()
end "n": new()
"s": save()
# scaleface(f, g) -- return copy of face f scaled to overlay face 9 "q": quit()
lIi": {
procedure scafeface(f, g) mode := ImageMode
local fl, fr, gl, gr, fx, fy, gx, gy, m, r, t, curve redisplay()
fI := f[1] I fail # left iris }
fr := f[2] I fail # right iris "d": {
gl := g[1] I fail # target left iris mode := DrawMode
gr := g[2] I fail # target right iris redisplay()
fx := (fl[1] + fr[1]) /2.0 # x offset of f }
fy := (fl[2] + fr[2]) /2.0 # y offset of f "b": {
gx := (gl[1] + gr[1]) / 2.0 # x offset of 9 mode := DualMode
gy := (gl[2] + gr[2]) / 2.0 # Yoffset of 9 redisplayO
m := (gr[1] - gl[1]) / real(fr[1] - fl[1]) # multiplier }
}
r:= []
every curve := copy(!f) do { return
if /curve[-1] then end
put(r, curve) # incomplete placeholder
else {
put(r, t := []) # slider_cb() -- handle adjustments of distortion slider
while put(t, m * (get(curve) - fx) + gx) do procedure slider_cb(vidget, val)
put(t, m * (get(curve) - fy) + gy)
} setdist(val) # update and display value
} if mode = ImageMode then # ensure that mode includes drawing
mode := DualMode
return r redisplay() # draw updated sketch
end
360 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 361

return FiIICircle(target_win, tx, ty, TargetRad2)


end # Display the prompt.
x := prompCxoff + prompCwidth / 2
# target(curve, point) -- display next point to be placed
Y := prompCyoff + prompCheight / 2
procedure target(curve, point) s := "locate" II descriptions[tcurve]
local s, n, x, y n := *guideface[tcurve]
static tx, ty if n > 2 then
# Undraw the previous target and erase the previous prompt. s 11:= " (select" II n / 2 II" points)"
CenterString(x, y, s)
FiIICircle(targeCwin, \tx, \ty, TargetRad1)
FiIlCircle(targeCwin, \tx, \ty, TargetRad2) return
EraseArea(prompt_xoff, prompt_yoff, prompCwidth, prompCheight) end
# Start from specified place unless the pupils remain unplaced.
# wtface(f, face) -- write face data to file f
if \sketch[1, 1] & \sketch[2, 1] then {
tcurve := curve procedure wtface(f, face)
tpoint := point local curve, i
} every curve := !face do {
else { writes(f, ":")
tcurve := 1 every i := 1 to *curve by 2 do {
tpoint := 1 writes(f, " ", (\curve[i] - image_xoff) I 0)
} writes(f, " ", (\curve[i + 1] - image_yoff) I 0)
# Find the next unset point. }
write(f)
until /sketch[tcurve, 2 * tpoint - 1] do { }
tpoint +:= 1 # advance to next point
if tpoint > (2 * *guideface[tcurve)) then { return
tpoint := 1 # need to move to next curve end
tcurve +:= 1
} #===«vib:begin»=== modify using vib; do not remove this marker line
if tcurve > *guideface then procedure ui_atts()
tcurve := 1 # wrapped around list of curves return ["size=640,480", "bg=pale gray", "label=Caricaturist"]
= =
if tcurve curve & tpoint point then { end
tcurve := tx := ty := &null # there are no unset points
procedure ui(win, cbk)
return
return vsetup(win, cbk,
}
[":Sizer:::O,O,640,480:Caricaturist",],
}
["distort:Slider:h: 1:1 0,436,230,22:-300,300,0" ,slider_cb],
# Draw a target on the guide face. ["dmenu:Menu:pull::36,O,57,21 :Display",menu_cb,
tx := guideface[tcurve, 2 * tpoint - 1] ["image @I","drawing @D","both @B"]],
ty := guideface[tcurve, 2 * tpoint] ["fmenu:Menu:pull::0,0,36,21 :File" ,menu_cb,
FiIICircle(target_win, tx, ty' TargetRad1) ["new @N","load @L","save @S",·save as ","quit @Q"]],
362 Facial Caricatures Chapter 16 Chapter 16 Facial Caricatures 363

["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.

line Directives Predefined Names


A line directive has the form At the start of each source file, several names are automatically defined
$Iine n [filename)
to indicate the Icon system configuration. Each potential predefined name
corresponds to one of the values produced by the keyword &features. If a feature
The line containing the preprocessing directive is considered to be line n of the is present, the name is defined with a value of 1. If a feature is absent, the name
given file (or the current file, if unspecified) for diagnostic and other purposes. is not defined. The most commonly used predefined names are listed below. See
The line number is a simple unsigned integer. The file name must be quoted if Griswold, Jeffery, and Townsend (1996) for a complete listing.
it is not in the form of an Icon identifier.
predefined name &features value
Define Directives
- MACINTOSH Macintosh
A define directive has the form - MSDOS MS-DOS
$define name text - MSDOS_386 MS-DOS/386
- MS_WINDOWS_NT MS Windows NT
The define directive defines the text to be substituted for later occurrences of the - OS2 OS/2
identifier name in the source code. text is any sequence of characters except that - UNIX UNIX
any string or cset literals must be properly terminated within the definition. - VMS VMS
Leading and trailing whitespace, including comments, are not part of the
definition. The text can be empty. - GRAPHICS graphics
Duplicate definition of a name is allowed if the new text is exactly the - MS_WINDOWS MS Windows
same as the old text. This prevents problems from arising if a file of definitions -PRESENTATION_MGR Presentation Manager
is included more than once. The text must match exactly: For example, 3.0 is not _X_WINDOW_SYSTEM X Windows
the same as 3.000.
- PIPES pipes
Definitions remain in effect through the end of the current original - SYSTEM_FUNCTION system function
source file, crossing include boundaries, but they do not persist from one source
file to another.
Predefined names have no special status: Like other names, they can be unde-
If the text begins with a left parenthesis, it must be separated from the
fined and redefined.
name by at least one space. Note that the Icon preprocessor does not provide
parameterized definitions.
Substitution
It is possible to define replacement text for Icon reserved words or
keywords, but this generally is dangerous and ill-advised. As input is read, each identifier is checked to see if it matches a previous
definition. If it does, the value replaces the identifier in the input stream.
Undefine Directives No whitespace is added or deleted when a definition is inserted. The
replacement text is scanned for defined identifiers, possibly causing further
An undefine directive has the form substitution, but recognition of the original identifier name is disabled to
$undef name prevent infinite recursion.
374 Preprocessing Appendix B

Occurrences of defined names within comments, literals, or preproces-


sor directives are not altered. The preprocessor is ignorant of multi-line literals,
however, and it potentially can be fooled by these.
AppendixC
Substitution cannot produce a preprocessor directive. By then it is too
late.

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.

See also: lexpr


not expr: n - invert failure
not expr produces the null value if expr fails, but fails if expr succeeds. lexpr: a1, a2, ... - evaluate repeatedly

repeat expr - evaluate repeatedly


lexpr generates the results of expr repeatedly, terminating if expr fails.
See also: exprl I expr2
repeat expr evaluates expr repeatedly.

expr\ i : a1, a2, ..., ai -limit generator


return expr - return from procedure
expr \ i generates at most i results from the outcome of expr.
return expr returns from the current procedure, producing the outcome
of expr. See also: \a
Default: expr &null
s ? expr: a - scan string
See also: fail and suspend
s ? expr saves the current subject and position and then sets them to the
values of sand 1, respectively. It then evaluates expr. The outcome is the
suspend expr1 do expr2 - suspend from procedure
outcome of expr. The saved values of the subject and position are restored
suspend exprl do expr2 suspends from the current procedure, producing on exit from expr.
each result generated by exprl. If suspend is resumed, expr2 is evaluated See also: ?a
before resuming exprl. The do clause is optional.
Appendix 0

Operators

Icon's large repertoire of operators is summarized in this appendix. Operators


are grouped by syntactic form into three classes: prefix (unary) operators, infix
(binary) operators, and other operators.
Referenced procedures are described in Appendix E.

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

-c1 : c2 - compute cset complement


-c1 produces the cset complement of c1 with respect to &cset.

=5 : 5 - match string in scanning


=s is equivalent to tab(match(s».
See also: matchO, tabO, and N1 = N2

379
380 Operators Appendix D Appendix 0 Operators 381

*a : i-compute size \a : a - check for nonnull value


*a produces the size of a. \a produces a if the value of a is not the null value, but fails otherwise.
It produces a variable if a is a variable.
See also: N1 * N2
See also: expr \ i
?a1 : a2 - select randomly
.a : a - dereference variable
If a1 is an integer, ?a1 produces a number from a pseudo-random
sequence. If a1 > 0, it produces an integer in range 1 to a1, inclusive. If .a produces the value of a.
a1 = 0, it produces a real number in range 0.0 to 1.0.
See also: R. f
If a 1 is a string, ?a1 produces a randomly selected one-character substring
of a1 that is a variable if a1 is a variable. Infix Operators
If a1 is a list, record, or table, ?a1 produces a randomly selected element
from a1.
N1 + N2 : N3 - compute sum
If a1 is a set, ?a1 produces a randomly selected member of a1.
N1 + N2 produces the sum of N1 and N2.
Returned elements of lists, records, and tables are variables.
See also: +N
See also: s? expr

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

N1 1\ N2 : N3 - compute power N1 > N2 : N2 - numerically greater


N1 1\ N2 produces N1 raised to the power N2. N1 >= N2 : N2 - numerically greater or equal
See also: expO and 5qrtO N1 = N2 : N2 - numerically equal
N1 -= N2 : N2 - numerically unequal
a1 ++ a2 : a3 - compute cset or set union
N1 < N2 : N2 - numerically less
a 1 ++ a2 produces the cset or set union of a 1 and a2.
N1 <= N2 : N2 - numerically less or equal
a1 - - a2 : a3 - compute cset or set difference The numerical comparison operators produce N2 if the condition is
satisfied, but fail otherwise.
a1 - - a2 produces the cset or set difference of a1 and a2.

51 » 52: 52 -lexically greater


a1 ** a2 : a3 - cset or set intersection
51 »= 52 : 52 - lexically greater or equal
a1 ** a2 produces the cset or set intersection of a1 and a2.
51 == 52 : 52 - lexically equal
51 II 52 : 53 - concatenate strings 51 -== 52 : 52 - lexically unequal
51 II 52 produces a string consisting of 51 followed by 52. 51 «52 : 52 - lexically less
See also: L1 III L2 51 «= 52 : 52 - lexically less or equal
The lexical comparison operators produce 52 if the condition is satisfied,
L1 III L2 : L3 - concatenate lists
but fail otherwise.
L1 III L2 produces a list consisting of the values in L1 followed by the
values in L2. a1 === a2 : a2 - value equal
See also: 51 II 52 a1 -=== a2 : a2 - value unequal

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].

See also: a2 <- a2 and a 1 :=: a2 See also: a[a1]

Other Operators a[i1 :i2] : a1 - produce substring or list section


If a is a string, a[i1 :i2] produces the substring of a between i1 and i2.
a[i1 :i2] produces a variable if a is a variable.
i1 to i2 by i3 : i1, ..., in - generate integers in sequence
If a is a list, a[i 1:i2] produces a list consisting of the values of a in the given
i1 to i2 by i3 generates the sequence of integers from i1 to i2 in increments range.
ofi3.
In either case, i1 and i2 may be nonpositive.
Default: i3 1 if by clause is omitted
In either case, the subscripting operation fails if a subscript is out of
See also: seqO range.
See also: a[a1], a[i1+:i2], and a[i1-:i2]
[a1, a2, ... , an] : L - create list
[a1, a2, ... , an] produces a list containing thevaluesa1, a2, ... , an. a[i1+:i2] : a1 - produce SUbstring or list section
[ ] produces an empty list.
If a1 is a string, a1 [i1 +:i2] produces the substring of a1 between i1 and i1
See also: listO + i2. a1 [i1+:i2] produces a variable if a1 is a variable.
Ifa1 is a list, a1 [i1+:i2]produces a list consisting of the values of a1 in the
given range.
386 Operators Appendix D

In either case, i1 and i2 may be nonpositive.


In either case, the subscripting operation fails if a subscript is out of
Appendix E
range.
See also: a[a1], a[i1 :i2], and a[i1-:i2]

a[i1-:i2] : a1 - produce substring or list section Procedures


If a1 is a string, a[i1-: i2] produces the substring of a between i1 and i1 Icon's built-in and library procedures are described jn this appendix.
- i2. a[i1-:i2] produces a variable if a is a variable. Graphics procedures appear first, followed by basic (nongraphical) procedures.
If a is a list, a[i1-:i2] produces a list consisting of the values of a in the
given range. Graphics Procedures
In either case, i1 and i2 may be nonpositive. For graphics procedures, the type notation is extended in several ways.
In either case, the subscripting operation fails if a subscript is out of The following identifiers have meanings as indicated:
range. x, y integer coordinate location
See also: a[a1], a[i1 :i2], and a[i1 +:i2] w, h integer width and height
theta real angle (measured in radians)
a(a1, a2, ..., an) : am - process argument list
alpha real angle (measured in radians)
If a is a procedure, a(a1, a2, ... , an) produces the outcome of calling a
with arguments a1 a2, I an.
"'1
k string or integer color specification
If a is an integer having the value i, a(a1, a2, ... , an) produces the Either or both of w and h can be negative to indicate a rectangle that extends
outcome of ai, but fails if i is out of the range 1, ..., n. If i is nonpositive, leftward or upward from its given coordinates. A color specification is either an
the argument is determined with respect to the right end of the list. It integer obtained from NewColorO or a string having one of these forms:
produces a variable if ai is a variable. [lightness] [saturation] [hue[ish]] hue
Default: a -1 red,green,blue
See also: a! A #hexdigits
system-dependent-color-name
a ! A - process argument list Any window argument named W can be omitted, in which case the
If a is a procedure, a ! A produces the outcome of calling a with the subject window, &window, is used. Note that this is not the same as a default
arguments in the list or record A. If a is an integer, a! A produces A[a] but argument: to use the subject window, the argument is omitted entirely, not
fails if a is out of range of A. replaced by a null argument.
See also: a( ...) and !a The notation"......" in an argument list indicates that additional argu-
ment sets can be provided, producing the same effect as multiple calls. The
optional window argument, W, is not repeated in these additional argument
sets.
Some graphics procedures are not built into Icon itself but are instead
part of the library. For these, the corresponding link file is noted. Alternatively,
387
388 Procedures Appendix E Appendix E Procedures 389

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

DrawCircle(W, x, y, r, theta, alpha, .......) : W - draw circle


DrawPolygonO draws the outline of a polygon formed by connecting the
given points in order, with x1,y1 following xn,yn.
DrawCircleO draws an arc or circle of radius r centered at (x,y). theta is the
See also: DrawCurveO, DrawLineO, and FiIIPolygonO
starting angle, and alpha is the extent of the arc.
Defaults: theta 0 DrawRectangle(W, x, y, w, h, ......): W - draw rectangle
alpha 21t
DrawRectangleO draws the outline of the rectangle with comers at (x,y)
See also: DrawArcO and FiIICircleO
and (x+w,y+h).
392 Procedures Appendix E Appendix E Procedures 393

Defaults: x, y upper-left pixel Event(W) : a - return next window event


w, h to edge of window
EventO returns the next event from a window, waiting if necessary. The
See also: FiIIRectangleO keywords &x, &y, &row, &col, &interval, &control, &shift, and &meta are
set as side effects of calling EventO.
DrawSegment(W, x1, y1, x2, y2, ......): W - draw line segment See also: ActiveO, EnqueueO, PendingO, WReadO, and WReadsO
DrawSegmentO draws a line between two points. Additional pairs of
coordinates may be supplied to draw additional, disconnected seg- Fg(W, k1) : k2 - set or query foreground color
ments.
F90 returns the foreground color. If k1 is supplied, the color is first set
See also: DrawLineO to that specification; failure occurs if the request cannot be satisfied.
Setting the foreground color does not change the appearance of the
DrawString(W, x, y, s, ......): W - draw text window, but subsequent drawing operations are affected.

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

Pixel(W, x, y, w, h) : k1, k2, ... kn - generate pixel values


PaletteColor(W, 51, 52) : 53 - return color from palette
PixelO generates the colors of the pixels in the given rectangle, left to
PaletteColorO returns the color indexed by character 52 in palette 51.
right, top to bottom.
The result is in the form produced by ColorValueO.
Defaults: x, y upper-left pixel
Default: 51 "c1"
w, h to edge of window
See also: ColorValueO, PaietteChar50, PaietteGray50, and
PaletteKeyO ProcessEvent(R, p1, p2, p3) : a - process event

PaletteGray5(W, 51) : 52 - return grayscale entries of palette


ProcessEventO reads the next event, a, from the window associated
with the vidget R. Using x from &x and y from &y, p3(a, x, y) is called if
PaietteGray50 returns the string of characters that index the achromatic the event is a resize event. Then the event is passed to the proper vidget
entries within palette 51, ordered from black to white. for processing; p1(a, x, y) is called if the event is not accepted by a vidget.
Link: color
Finally, p2(a, x, y) is called unconditionally.

See also: PaietteChar50, PaletteColorO, and PaletteKeyO


Any procedure arguments that are omitted are not called.
ProcessEventO returns the event code a.
Link: vidgets
See also: GetEventsO
398 Procedures Appendix E Appendix E Procedures 399

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

TDraw(r) : n - move turtle forward while drawing Link: dialog


TDrawO moves the turtle forward r units while drawing a line. r can be See also: NoticeO, OpenDialogO, SaveDialogO, and SelectDialogO
negative to move backwards. The heading is not changed.
Default: r 1.0 TextWidth(W, s) : i-return width of text string
Link: turtle TextWidthO returns the width of string 5, in pixels, as drawn using the
See also: TDrawtoO, TScaleO, and TSkiPO current font.
See also: DrawStringO
TDrawto(x, y) : n - draw with turtle to (x,y)
TDrawtoO turns the turtle and draws a line to the point (x,y). The heading TFace(x, y) : r - set turtle heading
is set as a consequence of this movement. TFaceO turns the turtle to face directly towards the point (x,y). If the
Defaults: x, y center of window turtle is already at (x,y), the heading is not changed. The new heading is
returned.
Link: turtle
Defaults: x, y center of window
See also: TDrawtoO and TGotoO
Link: turtle
TextDialog(W, L1, L2, L3, L4, L5, i) : s - display text dialog See also: THeadingO
TextDialogO constructs and displays a dialog box and waits for the user
to select a button. The box contains zero or more captions specified by the TGoto(x, y, r) : n - set turtle location and change heading
list L1, zero or more text-entry fields specified by L2, L3, and L4, and one TGotoO moves the turtle to the point (x,y) without drawing. The heading
or more buttons specified by L5. i specifies the index of the default is not changed unless r is supplied, in which case the turtle then turns to
button, with a value of 0 specifying that there is no default button. Any a heading of r.
of the list arguments Lncan be specified by a single nonnull value, which
is then treated as a one-element list. Defaults: x, y center of window
For the text-entry fields, L2 specifies the labels, L3 specifies the default Link: turtle
values, and L4 specifies the maximum widths. If L2, L3, and L4 are not See also: TDrawtoO, THomeO, TSkiPO, TXO, and TYO
the same length, the shorter lists are extended as necessary by duplicat-
ing the last element. If omitted entirely, the defaults are: no labels, no
initial values, and a width of 10 (or more if necessary to hold a longer THeading(r) : r - set or query turtle heading
initial value). THeadingO returns the turtle's heading. If r is supplied, the heading is
TextOialogO returns the name of the button that was selected to dismiss first set to that value. The turtle's location is unaffected.
the dialog. The global variable dialog_value is assigned a list containing Link: turtle
the values of the text fields.
See also: TFaceO, TLeftO, and TRightO
Defaults: L1 []
L2 []
L3 []
L4 []
L5 ["Okay", ·Cancel"]
1
402 Procedures Appendix E Appendix E Procedures 403

THome() : n - move turtle to home position Link: dialog


THomeO moves the turtle to the center of the window without drawing See also: SelectDialogO and TextDialogO
and sets the heading to -900 (that is, towards the top ofthe window). The
scaling factor is not changed. TReset() : n - reinitialize turtle state
Link: turtle TResetO resets the turtle state: The window is cleared, the turtle is
See also: TGotoO and TResetO moved to the center of the window without drawing, the heading is set
to -90°, the scaling factor is reset to 1.0, and the stack of turtle states is
cleared. These actions restore the initial conditions.
TLeft(r) : r - turn turtle to left
Link: turtle
TLeftO turns the turtle r degrees to the left of its current heading. Its
location is not changed, and nothing is drawn. The resulting heading is See also: THomeO, TRestore(), and TSave()
returned.
Default: r 90.0 TRestore() : n - restore turtle state
Link: turtle TRestore() sets the turtle state to the most recenfset of saved values, then
discards that set. It fails if no unrestored set is available.
See also: TFaceO, THeadingO,and TRightO
Link: turtle
ToggleDialog(W, L1, L2, L3, L4, i) : L - display toggle dialog See also: TReset() and TSave()
ToggleDialogO constructs and displays a dialog box and waits for the
user to select a button. The box contains zero or more captions specified TRight{r) : r - turn turtle to right
by the list L1, zero or more toggle buttons specified by L2, zero or more TRight() turns the turtle r degrees to the right of its current heading. Its
toggle states (1 or null) specified by L3, and one or more buttons specified location is not changed, and nothing is drawn. The resulting heading is
by L4. i specifies the index of the default button, with a value of 0 returned.
specifying that there is no default button. Any of the list arguments Ln
can be specified by a single nonnull value, which is then treated as a one- Default: r 90.0
element list. Link: turtle
For the toggle buttons, L2 specifies the labels and L3 specifies the See also: TFaceO, THeadingO, and TLeft()
corresponding states. If L2 and L3 are not the same length, the shorter list
is extended as necessary by duplicating the last element. If omitted
entirely, the defaults are: no labels and null states. TSave() : n - save turtle state

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

TScale(r) : r - set or query turtle scaling factor Uncouple(W) : W - uncouple window


TScaleO returns the TDraw/TSkip scaling factor. If r is supplied, the UncoupleO frees the window W. If no other bindings to the same canvas
scaling factor is first multiplied by r. The turtle's heading and location are exist, the window is closed.
not changed.
See also: CloneO, CoupleO, and WCloseO
Link: turtle
See also: TDrawO and TSkiPO VEcho(R, a) : n - trace vidget callback
VEchoO writes the ill of vidget R and the value a on the standard output
TSkip(r) : n - move turtle forward without drawing file.
TSkiPO moves the turtle forward i units. r can be negative to move VEchoO is suitable for use as a vidget callback procedure, and the line is
backwards. The heading is not changed. labeled as a callback.
Default: r 1.0 Link: vidgets
Link: turtle
VGetltems(R) : L - get vidget items
See also: TDrawO, TGotoO, and TScaleO
VGetitemsO returns a list of strings representing the items displayed by
TWindow(W) : n - set turtle window the menu or text-list vidget R. If a menu vidget contains a submenu, the
submenu is represented by two entries in the returned list: a string label
TWindowO moves the turtle to the given window, retaining its coordi- followed by a list of items in the submenu.
nates and heading. The heading is not changed.
Link: vidgets
Link: turtle
See also: VSetitemsO, VGetStateO, and VSetStateO

TX(x) : r - set or query horizontal turtle position


VGetState(R) : a - get vidget state
TXO returns the turtle's horizontal position. If x is supplied, the turtle is
first moved without drawing. The turtle's heading is not changed. VGetStateO returns the current state of the vidget R. VGetStateO can be
used only with vidgets that maintain state, such as toggle buttons and
Link: turtle sliders but not menus.
See also: TGotoO and TYO See also: VSetStateO, VGetltemsO, and VSetitemsO
Link: vidgets
TY(y) : r - set or query vertical turtle position
TYO returns the turtle's vertical position. If y is supplied, the turtle is first VSetFont(W) : W - set standard vidget font
moved without drawing. The turtle's heading is not changed.
VSetFontO sets the font to one suitable for use with the vidgets. It may
Link: turtle be used independently of the vidgets by other programs seeking a
See also: TGotoO and TXO similar appearance. If the existing font has suitable dimensions, it is left
unchanged; if no suitable font can be found, the font is left unchanged.
Link: vidgets
See also: FontO
406 Procedures Appendix E Appendix E Procedures 407

VSetltem5{R, L) : L - set vidget items WDelay(W, i) : W - flush window and delay


VSetltem50 sets the list of strings representing the items displayed by WDeiayO flushes any pending output for window Wand then delays for
the menu or text-list vidget R. For a menu vidget, any string entry may i milliseconds before returning.
be followed by a list representing a submenu.
Link: vidget5
Default: 1
Link: wopen
See also: VGetltem50, VGetStateO, and VSetStateO
See also: delayO and WFlushO
VSetState{R, a) : n - set vidget state
WDone(W) - wait for "quir' event, then exit
VSetStateO sets the state of the vidget R. VSetStateO can be used only
with vidgets that maintain state, such as toggle buttons and sliders but WDoneO waits until a q or Q is entered, then terminates program
not menus. execution. It does not return.
See also: VGetStateO, VGetltem50, and VSetltem50 Link: wopen
Link: vidget5 See also: exitO and WQuitO

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

See also: manyO and matchO


See also: ordO

asin(r1) : r2 - compute arc sine


chdir(s) : n - change directory
asinO produces the arc sine of r1 in the range of -rt/2 to rt/2 for r1 in the
range -1 to 1. chdirO changes the current directory to 5, but it fails if there is no such
directory or if the change cannot be made. Whether the change in
See also: sinO directory persists after program termination depends on the operating
system on which the program runs.
atan(r1, r2) : r3 - compute arc tangent
close(f) : f - close file
atanO produces the arc tangent of r1 / r2 in the range of -1t to 1t with the
sign of r1. c1oseO closes f.
Default: r2 1.0 See also: flushO and openO
See also: tanO
copy(a1) : a2 - copy value
bal(c1, c2, c3, s, i1, i2) : i3, i4, ..., in - locate balanced characters copYO produces a copy of a1 if a1 is a structure; otherwise it produces
balO generates the sequence of integer positions in s preceding a charac- a1. Structures contained within a copied structure are not copied.
ter of c1 in s[i1 :i2] that is balanced with respect to characters in c2
and c3, but fails if there is no such position. cos(r1) : r2 - compute cosine
Defaults: c1 &cset cosO produces the cosine of r1 in radians.
c2 '('
c3 ')' See also: cosO
s &subject
i1 &pos if s is defaulted, otherwise 1 cset(a) : C- convert to cset
i2 0
csetO produces a cset resulting from converting a, but fails if the
See also: findO and uptoO conversion is not possible.

center(s1, i, s2) : s3 - position string at center delay(i) : n - delay execution


centerO produces a string of size i in which s1 is centered, with s2 used delayO delays program execution i milliseconds. This procedure is not
for padding at left and right as necessary. supported on all platforms; if it is not, there is no delay and delayO fails.
Defaults: i 1
s2 II II (blank) delete(A, a) : A - delete element
See also: leftO and rightO If A is a set, deleteO deletes a from A. If A is a table, deleteO deletes the
element for key a from A. deleteO produces A.
char(i) : s - produce character See also: insertO and memberO
char(} produces a one-character string whose internal representation is
i. The value of i must be between 0 and 255 inclusive.
412 Procedures Appendix E Appendix E Procedures 413

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

iand(i1, i2) : i3 - compute bit-wise and


exit(i) - exit program
exitO terminates program execution with exit status i.
iandO produces an integer consisting of the bit-wise AND of i1 and i2.

Default: normal exit (machine dependent) See also: icomO, iorO, ishiftO, and ixorO

See also: stopO


icom(i1): i2 - compute bit-wise complement

exp(r1) : r2 - compute exponential icomO produces the bit-wise complement of i1.

expO produces the mathematical constant e (2.71828...) raised to the


See also: iandO, iorO, ishiftO, and ixorO
power r1.
image(a) : 5 - produce string image
See also: logO and N1 I\. N2
imageO produces a string image of a.
find(51, 52, i1, i2) : i3, i4, ..., in - find string
in5ert(A, a1, a2) : A - insert element
findO generates the sequence of integer positions in s2 at which s1 occurs
as a substring in s2[i1 :i2], but fails if there is no such position. If A is a table, insertO inserts key a 1 with value a2 into A. If A is a set,
insertO inserts a1 into A. insertO produces A.
414 Procedures Appendix E Appendix E Procedures 415

Default: a2 &null 1i5t(i, a) : L - create list


See also: deleteO and memberO listO produces a list of size i in which each value is a.
Defaults: i 0
integer(a) : i-convert to integer a &null
integerO produces the integer resulting from converting a, but it fails if See also: [a1, a2, ... , an]
the conversion is not possible.
See also: numericO and realO log(r1, r2) : r3 - compute logarithm
logO produces the logarithm of r1 to the base r2.
ior(i1, i2) : i3 - compute bit-wise inclusive or
Default: r2 &e (2.71828 ... )
iorO produces the bit-wise inclusive OR of i1 and i2.
See also: expO
See also: iandO, icomO, ishiftO, and ixorO
many(c, 5, i1, i2) : i3 -locate many characters
i5hift(i1, i2) : i3 - shift bits
manyO succeeds and produces the position in s after the longest initial
ishiftO produces the result of shifting the bits in i1 by i2 positions. sequence of characters in c within s[i 1:i2]. It fails if the initial character is
Positive values of i2 shift to the left with zero fill; negative values of i2 not in c.
shift to the right with sign extension.
Defaults: s &subject
See also: iandO, icomO, iorO, and ixorO i1 &pos if s is defaulted, otherwise 1
i2 0
ixor(i1, i2) : i3 - compute bit-wise exclusive or See also: anyO and matchO
ixorO produces the bit-wise exclusive OR of i1 and i2.
See also: iandO, icomO, iorO, and ishiftO map(51, 52, 53) : 54 - map characters
mapO produces a string of size *s1 obtained by mapping characters
key(T) : a1, a2, ..., an - generate keys from table of s1 that occur in s2 into corresponding characters in s3.
keyO generates the keys of table T. Defaults: s2 string(&ucase)
s3 string(&lcase)
See also: !a
match(51, 52, i1, i2) : i3 - match initial string
left(51, i, 52) : 53 - position string at left
matchO produces the position beyond the initial substring of s2[i1 :i2], if
leftO produces a string of size i in which s1 is positioned at the left, with any, that is equal to s1; otherwise it fails.
s2 used for padding at the right as necessary.
Defaults: s2 &subject
Defaults: i 1 i1 &pos if s2 is defaulted, otherwise 1
s2 II II (blank) i2 0
See also: centerO and rightO See also: =s, anyO, and manyO
416 Procedures Appendix E Appendix E Procedures 417

member(A, a) : a- test for membership Default: s2 "rt"


If A is a set, memberO succeeds if a is a member of A but fails otherwise. See also: close()
If Ais a table, memberO succeeds if a is a key of an element in A, butit fails
otherwise. memberO produces a if it succeeds. ord(s) : i-produce ordinal
See also: deleteO and insertO
ord() produces an integer (ordinal) between 0 and 255 that is the internal
representation of the one-character string s.
move(i) : 5 - move scanning position
See also: char()
moveO produces &subjeet[&pos:&pos + i] and assigns &pos + i to &pos/
but fails if i is out of range. moveO reverses the assignment to &pos if it pop(L) : a - pop from list
is resumed.
pop() produces the left-most element of Land removes it from L, but fails
See also: tabO
if L is empty. pop is a synonym for get.

numeric(a) - convert to numeric


See also: get(), pull(), push(), and put()

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

read(f) : 5 - read line reverse(s1) : s2 - reverse string


readO produces the next line from f, but it fails on an end of file. rever5eO produces a string consisting of the reversal of 51.
Default: f &input
right(s1, i, s2) : s3 - position string at right
See also: readsO
rightO produces a string of size i in which s1 is positioned at the right,
read5(f, i) : 5 - read string with s2 used for padding at the left as necessary.

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

real(a) : r - convert to real


runerr(i, a) - terminate execution with run-time error
realO produces a real number resulting from converting a, but fails if the
conversion is not possible. runerrO terminates program execution with error i and offending value
a.
See also: integerO and numericO
Default: no offending value

remove(5): n - remove file


seek(f, i) : f - seek to position in file
removeO removes (deletes) the file named 5, but fails if 5 cannot be
removed. seekO seeks to position i in f but fails if the seek cannot be performed.
The first byte in the file is at position 1. seek(f, 0) seeks to the end of file
See also: renameO f. If i is negative, the position is relative to the end of the file.
See also: whereO
rename(51, 52) : n - rename file
renameO renames the file named 51 to be 52, but fails if the renaming seq(i1, i2) : i3, i4, ... - generate sequence of integers
cannot be accomplished.
5eqO generates an endless sequence of integers starting at i1 with
See also: removeO increments of i2.
Defaults: i1 1
repl(51, i) : 52 - replicate string i2 1
replO produces a string consisting of i concatenations of 51. See also: i1 to i2 by i3
420 Procedures Appendix E Appendix E Procedures 421

set(L) : S - create set stop(a1, a2, ..., an) - stop execution


setO produces a set whose members are the distinct values in the list L. stopO terminates program execution with an error exit status after
Default: L [] writing strings a1, a2, ... , an. If ai is a file, subsequent output is to ai.
Initial output is to standard error output.
sin(r1) : r2 - compute sine Default: ai 1111 (empty string)

sinO produces the sine of r1 given in radians. See also: exitO and writeO

See also: asinO


string(a) : s - convert to string

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

sortf(A, i) : L - sort structure by fields table(a) : T - create table


sortfO produces a sorted list of the values of A. Sorting is primarily by tableO produces a table with a default value a.
type and in most respects is the same as with sortO. However, among
lists and among records, two structures are ordered by comparing their Default: a &null
ith fields. i can be negative but not zero. Two structures having equal ith
fields are ordered as they would be in regular sorting, but structures tan(r1) : r2 - compute tangent
lacking an ith field appear before structures having them.
tanO produces the tangent of r1 given in radians.
Default: 1
See also: atanO
See also: sortO
trim(s1, c) : s2 - trim string
sqrt(r1) : r2 - compute square root
trimO produces a string consisting of the characters of s1 up to the
sqrtO produces the square root of r1. trailing characters contained in c.
See also: N1 1\ N2 Default: c I I (blank)
422 Procedures Appendix E

type(a) : s - produce type name


typeO produces a string corresponding to the type of a. Appendix F
upto(c, s, i1, i2) : i3, i4, ... in - locate characters
uptoO generates the sequence of integer positions in s preceding a
character of c in s[i1 :i2]. It fails if there is no such position. Keywords
Defaults: s &subject
i1 &pos if s is defaulted, otherwise 1
i2 0
See also: balO and findO

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.

&cset : c - all characters


The value of &cset is a cset consisting of all 256 characters.
423
424 Keywords Appendix F Appendix F Keywords 425

&date : 5 - date &interval : i-elapsed time between window events


The value of &date is the current date in the form ywy/mm/dd, as in The value of &interval is the time, in milliseconds, between the last
"1997/10/31 ". received window event and the previous event in that window. &interval
is zero if this information is not available.
&dateline : 5 - date and time of day
The value of &dateline is the current date and time of day, as in "Friday, &Icase : c - lowercase letters
October 31, 1997 7:21 pm". The value of &Icase is a cset consisting of the 26 lowercase letters.

&digit5 : c - digits &Idrag : i-left-button drag event


The value of &digits is a cset containing the ten digits. The value of &Idrag is the integer that represents the event of dragging
the mouse with the left button depressed.
&dump : i-termination dump
If the value of &dump is nonzero at the time of program termination, a &Ietters : c - letters
dump in the style of displayO is provided. &dump is zero initially. The value of &Ietters is a cset consisting of the 52 upper- and lowercase
letters.
&e : r - base of natural logarithms
The value of &e is the base of the natural logarithms, 2.71828.... &Ipress: i -left-button press event
The value of &Ipress is the integer that represents the event of pressing
&errout : f - standard error output the left mouse button.
The value of &errout is the standard error output file.
&Irelease : i-left-button release event
&fail - failure The value of &Irelease is the integer that represents the event of releasing
the left mouse button.
The keyword &fail produces no result.

&mdrag : i-middle-button drag event


&feature5 : 51, 52, ..., 5n - implementation features
The value of &mdrag is the integer that represents the event of dragging
The value of &features generates strings identifying the features of the the mouse with the middle button depressed.
executing version of Icon.

&meta : n - state of meta key during window event


&h05t : 5 - host system
The value of &meta is the null value if the meta key was depressed at the
The value of &host is a string that identifies the host system on which time of the last received window event; otherwise, a reference to &meta
Icon is running. fails.

&input : f - standard input


The value of &input is the standard input file.
426 Keywords Appendix F Appendix F Keywords 427

&mpress : i-middle-button press event &rdrag : i - right-button drag event


The value of &mpress is the integer that represents the event of pressing The value of &rdrag is the integer that represents the event of dragging
the middle mouse button. the mouse with the right button depressed.

&mrelease : i-middle-button release event &resize : i-window resize event


The value of &mrelease is the integer that represents the event of The value of &resize is the integer that represents a window resizing
releasing the middle mouse button. event.

&null : n - null value &row : i-mouse row location


The value of &null is the null value. The value of &row is normally the column location of the mouse at the
time of the last received window event. If a window is open, &row also
&output : f - standard output can be changed by assignment, which affects &y, or as a side effect of
assignment to &y.
The value of &output is the standard output file.
&rpress : i - right-button press event
&phi : r - golden ratio
The value of &rpress is the integer that represents the event of pressing
The value of &phi is the golden ratio, 1.61803.... the right mouse button.

&pi : r - ratio of circumference to diameter of a circle &rrelease : i - right-button release event


The value of &pi is the ratio of the circumference of a circle to its diameter, The value of &rrelease is the integer that represents the event of releas-
3.14159.... ing the right mouse button.

&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.

&progname : s - program name &subject : 5 - subject of scanning


The value of &progname is the file name of the executing program. A The value of &subject is the string being scanned. The subject of scanning
string can be assigned to &progname to replace its initial value. may be changed by assignment to &subject.

&random : i-random seed &time : i-elapsed time


The value of &random is the seed for the pseudo-random sequence. The The value of &time is the number of milliseconds of CPU time since
seed may be changed by assignment to &random. &random is zero beginning of program execution.
initially.
428 Keywords Appendix F

&trace : i-procedure tracing


Procedure tracing is enabled by assigning a nonzero integer to &trace. A AppendixG
trace message is produced when a procedure is called, returns, sus-
pends, or is resumed. &trace is decremented for each message produced.
&trace is zero initially.

&ucase : c - uppercase letters


Window Attributes
The value of &ucase is a cset consisting of the 26 uppercase letters.

&version : s - Icon version


The value of &version is a string identifying the version of Icon.
Window attributes describe and control various characteristics of a window.
Some attributes are fixed and can only be read; others can be set only when the
&window : W - subject window window is opened. Most can be changed at any time.
The value of &window is the subject window, the default window for There are two classes of attributes: canvas attributes and graphics context
most graphics procedures. It may be changed by assignment. If there is attributes. In general, canvas attributes relate to aspects of the window itself,
no subject window, &window is null. while graphics context attributes affect drawing operations. Alternate graphics
contexts, each with its own set of graphics context attributes, are created by
&x : i-mouse x-coordinate CloneO. Canvas attributes, however, are shared by all clones of a window.

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

Canvas Attributes Graphics Context Attributes


The following attributes are associated with a canvas and shared by all The following attributes are associated with graphics contexts.
windows that reference that canvas.
Usage Graphics Attribute Interpretation
Usage Canvas Attribute Interpretation
R, W fg foreground color
R, W label window label (title) R, W bg background color
R, W pos, posx, posy window position on screen R, W reverse color reversal flag
R, W resize user resizing flag R, W drawop drawing operation
R, W size, height, width window size, in pixels R, W gamma color correction factor
R, W rows, columns window size, in characters
W image initial canvas contents R, W font text font
R fheight, fwidth maximum character size
R, W canvas window visibility state R ascent, descent dimensions from baseline
W iconpos icon position R, W leading vertical advancement
R, W iconlabel icon label
R, W iconimage icon image R, W linewidth line width
R, W linestyle line style
R, W echo character echoing flag R, W fillstyle fill style
R, W cursor text cursor visibility flag R, W pattern fill pattern
R, W x, y cursor location, in pixels
R, W row, col cursor location, in characters R, W clipx, clipy clipping rectangle position
R, W c1ipw, c1iph clipping rectangle extent
R, W pointer pointer (mouse) shape R, W dx,dy output translation
R, W pointerx, pointery pointer location, in pixels
R, W pointerrow, pointercol pointer location, in characters

R, W display device on which the window appears


R depth display depth, in bits
R displayheight display height, in pixels
R displaywidth display width, in pixels
432 Window Attributes Appendix G Appendix G Window Attributes 433

Attribute Descriptions Initial value: &null (clipping disabled)


See also: cliph, c1ipw, c1ipy, and CliPO
ascent - text font ascent
clipy - y-coordinate of clipping region
The read-only graphics context attribute ascent gives the distance, in
pixels, that the current text font extends above the baseline. The graphics context attribute clipy specifies the top edge of the clipping
region.
See also: descent and fheight
Initial value: &null (clipping disabled)
bg - background color See also: c1iph, c1ipw, c1ipx, and CliPO
The graphics context attribute bg specifies current background color.
col - text cursor column
Initial value: "white"
The canvas attribute col specifies the horizontal position of the text
See also: fg, drawop, gamma, reverse, and 890
cursor, measured in characters.

canvas - window visibility See also: cursor, row, x, and y

The canvas attribute canvas specifies the window visibility.


columns - window width in characters
Values: "hidden", "iconic", "normal", "maximal"
The canvas attribute columns specifies the number of text columns
Initial value: "normal" available using the current font.
See also: LowerO and RaiseO Initial value: 80
See also: rows and width
cliph - height of clipping region
The canvas attribute cliph specifies the height of the clipping region. cursor - text cursor visibility flag
Initial value: &null (clipping disabled) The canvas attribute cursor specifies whether the text cursor is actually
See also: clipw, c1ipx, c1ipy, and CliPO visible on the screen. The text cursor appears only when the program is
blocked waiting for input.
clipw - width of clipping region Values: "on", "off"

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

display - name of display screen


echo - character echoing flag
The canvas attribute display specifies the particular monitor on which
The canvas attribute echo specifies whether keyboard characters read by
the window appears. It cannot be changed after the window is opened.
WReadO and WReadsO are echoed in the window. When echoing is
enabled, the characters are echoed at the text cursor position.
displayhelght - height of display screen
Values: "on", "off"
The read-only canvas attribute displayheight gives the height in pixels of Initial value: "on"
the display screen on which the window is placed.
See also: cursor, WReadO, and WReadsO
See also: displaywidth

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

drawop - drawing mode fheight - text font height


The graphics context attribute drawop specifies the way in which newly The read-only graphics context attribute fheight gives the overall height
drawn pixels are combined with the pixels that are already in a window. of the current text font.
Values: "copy", "reverse" See also: ascent, descent, fwidth, and leading
Initial value: "copy"
fillstyle - area filling style
See also: bg, fg, and reverse
The graphics context attribute fillstyle specifies whether a pattern is to be
dx - horizontal translation used when drawing. The fill style affects lines and text as well as solid
figures. The pattern itself is set by the pattern attribute.
The graphics context attribute dx specifies a horizontal offset that is
Values: "solid", "textured", "masked"
added to the x value of every coordinate pair before interpretation.
Initial value: ·solid"
Initial value: 0
See also: Iinestyle and pattern
See also: dy

font - text font name


dy - vertical translation
The graphics context attribute font specifies the current text font.
The graphics context attribute dy specifies a vertical offset that is added
436 Window Attributes Appendix G Appendix G Window Attributes 437

Initial value: "fixed" iconpos - window position when iconified


See also: FontO The write-only canvas attribute iconpos specifies the location of the
iconified window as a string containing comma-separated x- and y-
coordinates.
fwidth - text font width
See also: iconimage and iconlabel
The read-only graphics context attribute fwidth gives the width of the
widest character of the current text font.
image - source of window contents
See also: fheight
The write-only canvas attribute image names a file containing an image
to be used as the initial contents of a window when it is opened.
gamma - color correction factor
See also: iconimage
The graphics context attribute gamma specifies the amount of color
correction applied when converting between Icon color specifications
and those of the underlying graphics system. A value of 1.0 results in no label - window label
color correction. Larger values produce lighter, less saturated colors. The canvas attribute label specifies a title used to identify the window.
Values: real values greater than zero Initial value: ""
Initial value: system dependent See also: iconlabel
See also: fg and bg
leading - text line advancement
height - window height in pixels
The graphics context attribute leading specifies the vertical spacing of
The canvas attribute height specifies the height of the window. successive lines of text written in a window.
Initial value: enough for 12 lines of text Initial value: font height
See also: rows, size, and width See also: fheight

iconimage - window image when iconified Iinestyle -line style


The canvas attribute iconimage names a file containing an image to be The graphics context attribute linestyle specifies the form of drawn lines.
used as the representation of the window when iconified. Values: "solid", "dashed", "striped"
Initial value: 1111
Initial value: "solid"
See also: iconlabel, iconpos, and image See also: fillstyle and Iinewidth

iconlabel - window label when iconified Iinewidth -line width


The canvas attribute iconlabel specifies a label to be used as the represen- The graphics context attribute linewidth specifies the width of drawn
tation of the window when iconified. lines.
Initial value: initial value of label attribute Initial value: 1
See also: iconimage, iconpos, and label See also: linestyle
438 Window Attributes Appendix G Appendix G Window Attributes 439

pattern - filling pattern specification pointery - mouse location y-coordinate

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

pointer - shape ofmouse indicator posx - x-coordinate of window position


The canvas attribute pointer specifies the shape of the figure that repre- The canvas attribute posx specifies the horizontal window position.
sents the mouse position. Attempts to read or write the position fail if the canvas is hidden.
Values: system dependent See also: pos and posy
Initial value: system dependent
See also: pointercol, pointerrow, pointerx, and pointery posy - y-coordinate of window position
The canvas attribute posy specifies the vertical window position. At-
pointercol - mouse location column tempts to read or write the position fail if the canvas is hidden.
The canvas attribute pointercol gives the horizontal position of the See also: pos and posx
mouse in terms of text columns.
See also: pointer, pointerrow, pointerx, and pointery resize - user resizing flag
The canvas attribute resize specifies whether the user is allowed to resize
pointerrow - mouse location row the window by interaction with the graphics system.
The canvas attribute pointerrow gives the vertical position of the mouse Values: "on", "off"
in terms of text lines. Initial value: "off"
See also: pointer, pointercol, pointerx, and pointery
reverse - color reversal flag
pointerx - mouse location x-coordinate The graphics context attribute reverse interchanges the foreground and
The canvas attribute pointerx specifies the horizontal position of the background colors when it is changed from "off" to "on" or from ·on" to
mouse in pixels. "offll

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

yellow-green r R E e , e5: \000\001 yz{ I }-\d\200\201 ... \214


green s S F f - e6: \000\001 \327 \330\331 ... \360
+
cyan-green t T G 9 *, / For example, the regular portion of the e3 palette is interpreted this way:
cyan u U H h I

blue-eyan v V I i < > char. r g b char. r g b char. r g b


blue w W J j ( ) @ 0 0 0 I 1 0 0 R 2 0 0
purple x X K k [ ] A 0 0 1 J 1 0 1 S 2 0 1
magenta y Y L I { } B 0 0 2 K 1 0 2 T 2 0 2
magenta-red z Z M m 1\
= C 0 1 0 L 1 1 0 U 2 1 0
pink 7 0 0 1 1 M 1 1 1 V 2 1 1
violet 8 E 0 1 2 N 1 1 2 W 2 1 2
F 0 2 0 0 1 2 0 X 2 2 0
Note that in the Icon color-naming system, "dark brown" and "light G 0 2 1 P 1 2 1 Y 2 2 1
brown" are the same as two shades of red-yellow. H 0 2 2 Q 1 2 2 Z 2 2 2
444 Palettes Appendix H

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)

Polygons and Curves


Figure 1.4
Lines or curves produced by DrawPolygonO and DrawCurveO pass
through and include each of the specified points. For wide lines, the center of the
path passes through these points. FiIICircle() then DrawCircle()
Fg("pale gray")

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

defined symbol key AppendixK


Key_PrSc print screen
Key_ScrollLock scroll lock
Key_Pause pause
Event Queues
Key_Insert insert
Key_Home home
Key_PgUp page up
Key_End end
Key_PgDn page down

Key_Left arrow left


Each window has an event queue, which is an ordinary Icon list.
Key_Up arrow up
Pending(W) produces the event queue of the window W. An event is repre-
Key_Right arrow right sented by three consecutive values on the list. The first value is the event code:
Key_Down arrow down a string for a keypress event or an integer for any other event. The next two
values are Icon integers whose lower-order 31 bits are interpreted as fields
Key_F1 function key Fl having this format:
Key_F2 function key F2
000 0000 0 000 0 S MC XXXX XXXX XXXX XXXX (second value)
Key_F3 function key F3
EEE MMMM MMMM MMMM YYYY YYYY YYYY YYYY (third value)
Key_F4 function key F4
Key_F5 function key FS The fields have these meanings:
Key_F6 function key F6 X X &x: 16-bit signed x-coordinate value
Key_F7 function key F7 Y Y &y: 16-bit signed y-eoordinate value
Key_F8 function key F8 SMC &shift, &meta, and &control flags
Key_F9 function key F9
E. .. M &interval, interpreted as Mx16 E milliseconds
Key_F10 function key FlO
Key_F11 function key Fll o unused; should be zero
Key_F12 function key F12 Coordinate values do not reflect any translation specified by dx and dy
attributes; the translation is applied by EventO when an event is read.
A malformed event queue error is reported if an error is detected when
Some keyboards have other keys that are not listed here. trying to read the event queue. Possible causes for the error include an event
queue containing fewer than three values, or second or third entries that are not
integer values or that are out of range. Only artificially constructed events can
produce such errors.

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

menu items pulled down, with the potentially selected


one highlighted AppendixM
radio buttons selected button highlighted
text list selected lines highlighted
text-entry text highlighted
slider thumb position
VIS
scrollbar thumb position

There is no visual indication when the callback occurs for a slider or


scrollbar vidget. The user cannot tell if the vidget is filtered or if a callback only
occurs when the mouse button is released. There is no visual indication when a
callback occurs when the user finishes with a text-entry vidget. To produce a This is a reference manual for VIB, an Icon program that can be used to create
callback for a text-entryvidget, the user must type return while the I-beam cursor interfaces and custom dialogs for Icon programs.
is in the text-entry field. If the user forgets this, no callback occurs. Since this is See Appendix L for detailed information about the vidgets that can be
easy to forget, the user may think there has been a callback to accept the contents used in VIB.
of the text-entry field when there has not been one. For this reason, text-entry
vidgets are best used in dialogs and not directly on interfaces.
The VIB Window
The window for VIB is shown in Figure M.l.

457
458 VIS Appendix M Appendix M VIS 459

The Edit Menu


The copy item copies a vidget, while delete deletes one. The
undelete item can be used to restore the last deleted vidget.
The final two items allow vidgets to be visually aligned.

FigureM.3

The Select menu allows one of the vidgets to be selected for manipula-
tion, as shown in Figure MA.

The Select Menu

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

Radio Buttons Scrollbars


fl'llqlBlCy llltfCIll Radio buttons allow the user to select Scrollbars perform the same function
...HbIdB l.ttClll one of a number of choices. as sliders but have buttons on the ends
phese llltfClll as well as a thumb for adjusting the
position.

Figure M.6 FigureM.ll

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

To enable the 1nterfac;e1 start


Text Lists Labels
by dleck1 ng thet the ..... 15.te
is disabled IN the 81.1'11 panel Textlists allow the user to scroll though Labels provide text but do not accept
is in stlNby status.
lines of text and select one or more of tabuleclon of detll entries events.
Then estebHsfl CCIlltact with the t
them.
I color o\nom:OHI sin(x)/x
di.log
drag
1nterac:t
FigureM.8 FigureM.13

Text-Entry Fields Lines


Text-entry fields provide the user with Lines can be used to decorate the inter-
directory:
an area to enter and edit text. face. Lines do not accept events.
group:

FigureM.9 Figure M.14

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

Application Canvas Dialog Figure M.ts


The dialog window button is checked when a custom dialog, instead of
an application interface, is being constructed. In this case, the name of
a procedure for invoking the dialog can be specified. Neither of these is
relevant for building an application interface.

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

Attribute Dialog for a Button r


I
... - . - -_. - --- ------. .. .. - Dialog for a Menu
I 1., t ,

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.

app.lcn Dialog for a Set of Radio Buttons Okay C8nCe1


,-
IItIMMiijjdi
Three buttons with default names Figure M.20
radi o...bUttOlLcbl are provided initially. These names
x: 221
can be changed by editing their text- The attribute dialog for a text list is shown in Figure M.21.
y: 49 entry fields.
Dialog for a Text List
There are three choices for user selec-
ID:
call beck:
tion: read only, which allows the user
to scroll through the lines but not select
Il: 153 width: 1110
y: 51 height: 1110
any; select one, which allows the user
FigureM.18 to select a single line; and select many,
which allows selection of any or all
Additional buttons can be added by clicking on one of the add buttons lines.
that appear at the left; the top and bottom add buttons insert a field above and FigureM.21
below the first and last fields, respectively. The other add buttons insert fields
between existing ones, as indicated by their positions. The del buttons at the Figure M.22 shows the attribute dialog for a text-entry field.
right delete the fields to their immediate left.
The attribute dialog for a menu is shown in Figure M.19.
466 VIS Appendix M Appendix M VIS 467

Dialog for a Text-Entry Field Dialog for a Scrollbar


The label field provides for The dialog for a scrollbar is the same as
text that appears at the left of the dialog for a slider, since the only
the field. An initial value for differences between the two types of
y: .xl _ V81118 1engtt1: -a- the text canbe entered and the vidgets are their visual appearance
---
1. .1: Text::....--====- ,
number of characters allowed and the functionality by which the
V8ll18: in the field can be specified. user can change the position of the
thumb.

Figure M.ll Okay C8ncel

The attribute dialog for a slider is shown in Figure M.23. FigureM.24

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

Figure M.26 shows the attribute dialog for a label.


The default values of the extreme positions of the thumb default to 0.0
and 1.0, allowing scaling in the application. If the filter toggle is on, a callback Dialog for a Label
occurs only when the user releases the thumb. Otherwise, a callback occurs Label vidgets are the simplest
whenever the user moves the thumb. 18l1el:
of all vidgets.
ID:
The attribute dialog for a scrollbar is shown in Figure M.24. x: 42lJ
y: 1

Okay C8ncel
,I

FigureM.26

Figure M.27 shows the attribute dialog for a line.


468 VIS Appendix M Appendix M VIS 469

-.... - - ...."..--- - . - _. - - .. --_. _.- - .-. 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

A vidget can be moved or modified only when it is selected. Only one


vidget can selected at a time. A vidget can be selected by clicking on it, which
highlights its comers to indicate it is selected. When a vidget is selected, any
previously selected vidget is deselected. Clicking on the canvas at any place
other than on a vidget deselects the currently selected vidget, leaving no vidget
selected.
A vidget also can be selected by using the Select menu, which displays
the IDs of all vidgets as shown earlier. This method of selection is useful if a
vidget is "lost" because it is behind another vidget or because it is too small to
be selected by clicking on it.

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.

Positioning Vidgets Vidgets to Be Aligned Figure M.28


In this example, the vidgets are obviously misaligned. Even if the
A vidget can be repositioned by selecting it and dragging with the left alignment is just slightly off, it's worth fixing it to make the interface
mouse button depressed. A selected vidget can be moved one pixel at a time by look tidy and professional.
using the arrow keys on the keyboard. They are useful for small movements and
precise positioning. A vidget also can be repositioned by using its dialog and The result of selecting align horiz and clicking on each of the three
changing the x-y coordinates of its upper-left comer. scrollbars below the anchor is shown in Figure M.29.
Vidgets can be aligned by selecting an "anchor" vidget and then choos-
ing align horiz or align vert from the Edit menu. Horizontal alignment aligns the
470 VIB Appendix M Appendix M VIS 471

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

A Default Button AppendixN


The default button is outlined as it is
for standard dialogs. Entering return
to dismiss a custom dialog is equiva-

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

unusable on those systems. • Reversible drawing does not work correctly.


Color correction is controlled by the gamma attribute. The default value
of the gamma attribute is 1.0 (the operating system handles gamma correction). The X Window System
Images Under X, an Icon program is a client that performs graphical I/O on a
server. The client and server can be the same machine, as when a program runs
In ReadlmageO, if an image file is not a valid GIF file, an attempt is made and displays locally on a workstation, or they can be on different machines. A
to read it as a Windows bitmap file. remote server can be specified by using the display attribute when opening a
In WritelmageO, if the file name ends in .bmp or .BMP, a Windows window.
bitmap file is written. In all other cases a GIF file is written. WritelmageO fails if There are many implementations of X, and different systems provide
GIF formatis selected butthe area beingwritten contains more than 256 different different features, so we can't describe precisely how things work in all situa-
colors. tions.
Keyboard Event Codes Font Specifications

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

unusual ones, such as orchid and papayawhip.


;If
arrow t donble arrow a mi ddl ebntton *' spider

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

cases a GIF image is written.

-
9F cross If- 1eft si de _sb left arrow CZ> watch

cross reve rse I- left tee sb ri ght arrow I xterm


Keyboard Event Codes
+ crosshai r QI 1eftbntton
t
sb np arrow
Icon uses X keysym codes as event codes. The actual code returned for a Llto.
'Or di amond cross L 11 angle t sb v donble arrow
particular key depends on the configuration of the X server; this can be altered
dynamically by the xmodmap utility. For example, the Sun keypad has one key • dot ...J 1r angle
.:1)
shnttle

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

Running an Icon Program

The implementation of Icon is based on the concept of a virtual machine - an


imaginary computer that executes instructions for Icon programs. The Icon
compiler translates Icon programs into assembly language for the virtual
machine and then converts the assembly language into virtual machine code.
This virtual machine code is then run on a real computer by an interpreter. This
implementation method allows Icon to run on many different computer plat-
forms.
Compiling and running Icon programs is easy. It is not necessary to
understand Icon's virtual machine, but knowing the nature of the implementa-
tion may help answer questions about what is going on in some situations.
How Icon programs are run necessarily varies from platform to plat-
form. On some platforms, Icon is run from the command line. On others, it is run
interactively through a visual interface. This chapter describes how Icon is run
in a command-line environment, such as under UNIX, and under Microsoft
Windows. Even for these environments, the details depend on the platform. In
any event, the user manual for a specific platform is the best guide to running
Icon.

Running Icon from the Command Line


The name of a file that contains an Icon source program must end with the suffix
.icn, as in hello.icn. The .icn suffix is used by the Icon compiler to distinguish Icon
source programs from other kinds of files.
The Icon compiler usually is named icont. To compile hello.icn, all that
is needed is
icont hello.icn

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.

Input and Output Redirection Environment Variables


In a command-line environment, most input and output is done using standard Environment variables can be used to configure Icon and specify the location of
input, standard output, and standard error output. Standard input typically is files. For example, the environment variable IPATH can be used to specify the
read from the keyboard, while standard output and standard error output are location of library modules. If graphics is in
written to the console.
/usr/icon/ipl/gprogs
Standard input and standard output can be redirected so that files can be
used in place of the terminal. For example, and IPATH has that value, then
hello < hello.dat > hello.out link graphics
executes hello with hello.dat as standard input and hello.out as standard output. will find it.
(The directions in which the angular brackets point, relative to the program Here are other environment variables that may be useful. Their default
name, are suggestive of the information flow.) values are given in parentheses.
482 Running Icon Appendix 0 Appendix 0 Running Icon 483

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)

r Open lIS [8ed-only

Running Icon under Microsoft Windows


The Microsoft Windows implementation of Icon runs under Windows Opening an Icon File Figure 0.2
95, Windows NT, and Windows 3.1. The compiler and interpreter can be
You can easily select an existing Icon source file or name a new one. If
invoked either using command-line invocation or through a visual develop- you click Open without choosing a name, the default name of noname.icn
ment environment, except on Windows 3.1, which only supports the visual is used.
development environment. This section briefly describes running Icon under
Microsoft Windows. For details on hardware and software requirements, see Editing occurs within the main Wi window, as shown in Figure 0.3.
Jeffery (1997).
When Windows Icon is installed, it produces the files shown in Figure
0.1.
Icon Files in Microsoft Windows
The files shown as question marks pro-
vide on-line help.
Windows Intro 10 Icon Lcnguage
Icon Help Reference

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

opened C:\WINICON\BIN\nonarne.icn, l1ines, 29 characters 'cont -8 -0 C:\WINICON\BIN\noname C:\WINICON\BIN\noname.icn


execution complete
ranslating:
C:\WINICON\BIN\noname.icn:
File C:\WINICON\BIN\noname.icn; Line 2 # ")": invalid argument
1 error

Editing a File Figure 0.3 A Syntactic Error Figure 0.4


The top area shows program source code, while the bottom portion Run-time errors also result in a message for which the source line is
shows messages such as compiler errors. You can change the font and highlighted. When the error messages become long, you can either
the number of lines used to show messages from the Edit menu. increase the number oflines for the message window (as was done here)
When you are done editing your program, you can save it, compile it, or scroll through the message window's entire text using the scrollbar.
make an executable, and run your program using menu options. The on-line
help includes a more detailed explanation of these operations. User Manuals
Error Handling The best source of information for running Icon on a particular platform
is the Icon user manual for that platform. User manuals are included with
A compilation error results in a message in which the editor highlights distributions of Icon. They also are available on-line. See Appendix P.
the line at which the error was detected. Figure 0.4 shows an example.
Appendix P

Icon Resources

Many resources are available to Icon programmers. These include implementa-


tions for many platforms, a program library, source code, books, technical
reports, newsletters, and a newsgroup.
Most Icon material, except for books, is available free of charge.

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

IPATH 45, 481, 482 M origin 60, 88, 180 as values 38


ishiftO 414 output See writing; drawing calling 37-38
iteration 15-16 mailing list, Icon 488 invocation 37
ixorO 414 main procedure See procedures, main p libraries 45
manyO 34, 415 linking 45
K mapO 415 PaletteCharsO 157, 163, 396
list invocation 37
matchO 415 PaletteColorO 156, 163, 396
keyO 28, 414 main 7, 60, 256, 481
mathematical procedures 22 PaletteGraysO 157, 163, 396, 444
Key_symbols 450 mathematical 22
matrices 302 PaletteKeyO 156, 397
keyboard parameters 35
memberO 27, 416 palettes 155-157, 441-444
events 66, 183, 188, 474, 476 returns 38-39
members, set 26 parameters 35
symbols 449 standard 387-422
menu bar 223 parentheses 49, 51
keys, table 27-28 suspension 39
menus 210-211, 233, 460, 465 parsing, string 33
keysyms.icn 449, 476 traceback 56
callbacks 216 Pascal 8-9
keywords 19, 423-428 tracing 56
meta key 186, 196, 207 PatternO 158, 397
ProcessEventO 255, 258, 397
Microsoft Windows 44, 68, 473, 482 pattern 158, 438
L program
mixed-mode arithmetic 22 patterns 148, 157-160
command line arguments 481
monitors See displays built-in 158
L-systems 117-125 structure 7, 59, 254, 268-274
Motif Window Manager 68 sizes 160
label 67, 69, 437 termination 42, 62
mouse PendingO 66, 188, 189, 397, 451
labels 215, 244, 461, 467 program library See Icon program library
button 66, 183 PixelO 174, 397
leading 133 prototyping 247, 472
click 183 plants 117-125
leading 133, 437 pullO 26, 417
drag 183 pointer
lettO 31, 414 pushO 25, 417
events 66, 183 mouse 66, 192, 197, 474, 477
LettStringO 395 putO 25, 417
pointer 66, 192, 474, 477 text See cursor
lexical comparison 32
pointer semantics 52-55, 323
libraries 45 See also Icon program library position 197
pointer 192, 198, 438, 474, 477
Q
light, intensity of 144 moveO 33, 416
MSTKSIZE 102, 482 pointercol 192, 438 qei 57
lightness 139-141, 143
mutable colors 146-148, 151-153, 474 pointerrow 192, 438 queues 25-26
limitation 15, 377
pointerx 192, 197, 438 quotation marks 30
Lindenmayer systems 117-125
line attributes 85-87 N pointery 192, 197, 439
line segments 75
points 71-72 R
names, predefined 43, 373 polling 189
line terminators 39, 41, 50, 491 NewColorO 146, 395 radio buttons 209, 242, 289, 460, 464
polygons 73, 79-81, 102, 446
lines 63, 73, 106, 445-446 newline character 128 callbacks 216
polymorphism 51
attributes 85 newsgroup, Icon 488 RaiseO 167, 398
popO 26, 417
on interfaces 215, 227-229, 461, 467 newsletters, Icon 488 random colors 163
portability
linestyle 86, 437, 445, 474 next 12, 376 random numbers 23, 102
colors 145
linewidth 63, 85-86, 437, 445-446, 446, 474 not 12, 376 random rectangles 64-66, 92-94, 150
fonts 138
link 45 NoticeO 193, 287, 297, 395 random walk 107
keyboard events 476
link graphics 59, 388, 481 null character 29 RandomColorO 163
monochrome 148-149
listO 24, 415 null value 21, 35-36, 36, 39, 41, 99 readO 10, 40, 418
pos 167, 439
lists 24-26 See also structures numericO 416 ReadlmageO 162, 398, 474, 476
posO 417
as procedure arguments 37, 75, 101 posx 167, 439 reading
empty 24
of attributes 69
o posy 167, 439 from files 10, 39-41
precedence 48 from window 127, 188
local 36 openO 40, 42, 416 images 162, 474, 476
predefined names 43, 373
local variables 36-37 OpenDialogO 193, 287, 396 readsO 418
prefix operators 48-51
logO 23, 102, 415 operators 379-386 real numbers 21, 97-98
preprocessing 42-44, 51, 371-374
Logo 106 infix 48-51 literals 21
printing, color images 149
LowerO 167, 395 prefix 48-51 realO 418
procedures 7, 35, 35-39
LPATH 46, 371, 482 ordO 417 record constructor 23
arguments 37
502 Index Index 503

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.

THE ICON Contents


The Language
PROGRAMMING
1 Getting Started
LANGUAGE 2 Expressions
3 String Scanning
,;;,
4 Characters, Csets, and Strings
5 Numerical Computation and Bit
Operations
6 Structures
7 Expression Evaluation
8 Procedures
9 Co-Expressions
10 Data types
11 Input and Output
12 An Overview of Graphics
13 Other Features
14 Running an Icon Program
15 Libraries
Ralph E. Griswold· Madge T. Griswold 16 Errors and Diagnostic Facilities
Programming Techniques
ISBN 1-57398-001-3 17 Programming with Generators
18 String Scanning and Pattern MatchiI
Peer-to-Peer Communications, paperback, 19 Using Structures
1996,386 pages 20 Mapping and Labelings

Appendixes, References, Index

For more information, contact:


Peer-to-Peer Communications, Inc. 800-420-2677 • fax: 804-975-0790
p.o. Box 640218 info@peer-to-peer.com • http://www.peer-to-peer.con
San Jose, California 95164-0218, U.S.A.
2:/(9(//$1*8$*(6/,.($1'86($''21*5$3+,&6/,%5$5 ,6(*(176@52
,(6 7+$7 '(0$1' &203/(; 92/80,1286 &2'( 72 $''5(66 0$1< )(66252)20387(5&,(1&(0(5,
7(',286'(7$,/6 +(+,*+/(9(/*5$3+,&6)($785(6,17+(32:(5)8/ 786 $7 +( !1,9(56,7< 2) 5,=21$
&21352*5$00,1*/$1*8$*($5(,17(*5$7(':,7+7+(5(672)7+( ( 67$57(' +,6 &$5((5 $7 (// $%6
/$1*8$*(*5$3+,&6&2'(,66+257$1'($6<72:5,7(#28'21@7 '(6,*1,1*+,*+/(9(/675,1*352&(66
1(('<($562)(;3(5,(1&(:,7+$5&$1(7(&+1,48(6?<28&$1*(7 ,1*/$1*8$*(6$1'%(&$0(+($'2)
,035(66,9(5(68/76:,7+-867$)(:/,1(62)&21&2'( 7+( 52*5$00,1* $1*8$*( (
!6,1*&21<28&$1'5$:86(&2/256$1')2176&5($7(,0$*(6'2 6($5&+ (3$570(17 7+(5( ( +$6
6,03/($1,0$7,216$1'%8,/'32:(5)8/$33/,&$7,216:,7+9,68$/ 29(5
 <($56 2) (;3(5,(1&( ,1 7+(
,17(5)$&(6!622.,16,'()25$+,172):+$7@63266,%/( '(6,*1$1',03/(0(17$7,212)+,*+
/(9(/ 352*5$00,1* /$1*8$*(6 $1'
+,6%22.,66(/)&217$,1('7+$6$//<281(('7281'(567$1'$1'
,6$87+25&2$87+252) %22.621
86(*5$3+,&6,1<285352*5$00,1*,1&/8',1*+2:72352*5$0,1
7+(68%-(&7(%(*$17+('(9(/23
&21 $1< &$5()8//< (;3/$,1(' 352*5$0 (;$03/(6 *8,'( <28
0(172)&21,1  $1'+$6*8,'('
7+528*+7+((;&,7,1*:25/'2)*5$3+,&6()25(/21*<28&$1%(
&5($7,1*<2852:1&20387(5*5$3+,&6 ,772,7635(6(170$785(67$7(
 ,666,67$1752
)(6625,17+(,9,6,212)20387(5
&,(1&($7 +(!1,9(56,7<2) (;$6$7
+(08/7,3/$7)250&217$,16 $11721,2(,1752'8&('&21@6
> (;(&87$%/(&21%,1$5,(6)25 
 *5$3+,&6 )$&,/,7,(6 $1' +$6 &217,1
$1'02673238/$5
 8('7+(,5'(9(/230(17
3/$7)2506   ,67$))&,(1

>352*5$0&2'($1',0$*(6)5207+(%22. 7,67,17+((3$570(172)20387(5
>$+8*(/,%5$5<2)$'',7,21$/&21352*5$0 &,(1&( $7 +( !1,9(56,7< 2) 5,
0$7(5,$/ =21$:+(5(+(/('7+(&216758&7,21
! >7+(&203/(7(38%/,&'20$,16285&(&2'( 2)$1' 2%$(+$6%((1$0$-25
)25&21 &2175,%872572&21)250$1<<($56
>7+(&203/(7(&2152-(&7"(%6,7()520 $1'+$6%((1'((3/<,192/9(',17+(
+(!1,9(56,7<2)5,=21$ '(6,*1 $1' ,03/(0(17$7,21 2) ,76
*5$3+,&6)$&,/,7,(6
+(,6'(6,*1('72%(86(':,7+$"(%%52:6(568&+
$6 (76&$3( $9,*$725 25 17(51(7 ;3/25(5 3(1 7+( 723
/(9(/"(%3$*(:,7+<285%52:6(5$1'<28@5(5($'<72(;3/25( ISBN 1-57398-009-9
+81'5('62)0(*$%<7(62)25,*,1$/)$6&,1$7,1*0$7(5,$/ 90000>

PP
SM


   
PEER-TO-PEER
COMMUNICATIONS 9 781573 980098

You might also like