ANIMAL LANGUAGE REFERENCE






Applets are small programs, in the way that piglets are small pigs and liberallets are small liberals. The latter is deceptive – small pigs eat less than big pigs, but all liberals spend everything they can.

Screen Saver Construction Set's applets are written in a dedicated programming language called ANIMAL – Alchemy Natural Interpreted Minimal Application Language. It took weeks to think up that acronym.

The ANIMAL language was written to be easy to understand, even for non-programmers. You can master it in an afternoon, even if the only computer programming you've done to date has involved an eight-pound hammer and a chainsaw.

ANIMAL was also written to be easy for a computer to understand, such that the part of each screen saver that runs programs written in ANIMAL can be small and fast.

It's worth noting that ANIMAL is vastly easier to use than Java – it's arguably even easier than BASIC. Furthermore, because ANIMAL lacks commands which can access the system resources, memory or hard drives of your computer, it's impossible to write an ANIMAL applet that can hurt your system. The worst possible outcome of a badly-written applet will be an error message.

Applets written in ANIMAL can do things like play sounds, display pictures, draw lines, rectangles, polygons and other shapes, display text, manage simple animation and do the usual string and mathematical functions that programs are usually called upon to deal with. ANIMAL lacks pretty much all the complicated aspects of more disturbing programming languages – ANIMAL will not require that you have even a cursory understanding of memory allocation, handles, pointers, hooks, callbacks, thunks, segments, sectors, structs, headers, resources or floating point numbers.

Applets written in ANIMAL are developed using what more serious programmers call an Integrated Development Environment – Screen Saver Construction Set includes an editor in which you can create and edit your applets, and from which they can be run to see if they work properly.

Applets can be exceedingly simple or fairly extensive. You can combine the elements drawn by applets with other graphics in a single screen saver.


NOTE: One of the most important aspects of writing programs – even simple ones, such as the sorts of things ANIMAL lends itself to – is figuring out how to express your ideas in the language in question. While we welcome the opportunity to address any bugs which turn up in the ANIMAL language itself, we are unable to assist you with your programming projects. Specifically, we can't tell you how to make ANIMAL do what you have in mind, proof-read your code or fix problems with your completed applets. These are things you need to work out for yourself.





Single-Cell ANIMALs


An ANIMAL program consists of calls to a library of "functions" supported by the ANIMAL language. A function is a command which is provided with "arguments" – some sort of information – upon which the function can perform something. Having done all the something it was intended to perform, a function will return a value.

In some cases, functions under ANIMAL have no need for arguments, and in others, they don't return a useful value.

Here's a fairly elementary ANIMAL function:

Add(6,3)

The Add function, as you might imagine, adds two numbers. The arguments to Add in this case are 6 and 3. Arguments are always separated by commas. The value returned by Add will beà did everyone get 9?

The arguments to Add are numbers. Under ANIMAL, numbers are referred to as "integers." If you were conscious during highschool mathematics, you'll probably recall that integers are also referred to as "whole" numbers, in that they do not have a component to the right of the decimal. Integers under ANIMAL can range from -2,147,483,647 to +2,147,483,647, which is to say, they can hold some pretty large numbers.

As an aside, numbers which do allow for decimal points and things to the right thereof are called "floating point" numbers, and ANIMAL doesn't support them. As such, 3 is a valid number under ANIMAL, while 3.1415926 is not.

In addition to storing single numbers, ANIMAL allows for "arrays" of integers. Think of an array as being a stack of integers. You can store numbers in each element of an array, and retrieve them as you need them. More will be said of this later.

The other sort of data used in ANIMAL applets are strings. A string is where text is stored. Strings can be any length you need them to be, and as you work with strings, they'll automatically change size to store whatever you put in them. ANIMAL includes functions which can manipulate strings, as well as the obvious ones to display the contents of strings on your screen.

Here's an important bit of ANIMAL lore. Functions under ANIMAL know what sorts of arguments they're supposed to be fed, and they'll complain if you try to feed them something inappropriate. For example, this is not a good idea:

Add(6,"Cat in a blender")

There's no obvious way to add a number and a string of text, and this function would fail.


NOTE: When ANIMAL encounters a function call it can't complete, it will terminate the execution of your program. In the applet editor in Screen Saver Construction Set, you'll see a dialog box with a description of the problem as soon as you right-click to close the applet preview window. Your editing cursor will be placed at the beginning of the offending line in the applet editor. Fix the problem and run the applet again.


Some animal functions can accept arguments of several types, and figure out what to do with them depending on what they are. Note also that some ANIMAL functions can accept variable numbers of arguments. We'll deal with this in greater detail later.

Applets written in ANIMAL can store integers and strings in "variables." A variable is a claim check for the number or text that has been stored in it. Under ANIMAL, integer variable names always start with the @ character, and string variable names always start with the $ character. Forgetting to included these characters is a common source of errors in writing ANIMAL applets.

These are integer variables under ANIMAL:

@x @variable @x1

These are string variables under ANIMAL:

$text $MoreText $CatInABlender

Only the first four characters of a variable name, not including the @ or $ characters, are significant under ANIMAL. You can use longer variable names to make your programs easier to follow, but ANIMAL will ignore the extra characters. This means, for example, that the variables @apple and @applet would be identical.

Variable names are case-sensitive under ANIMAL. As such, @APPLE, @Apple and @apple would constitute three distinct variable names.

ANIMAL will not let you use two variables having identical names but different types. For example, you could not use @wombat and $wombat to store an integer and a string.

Before a variable can be used, it must be "declared." Variable declaration is handled through function calls. To declare an integer variable, you would do this:

DeclareInt(@pigs)

This function call has created an integer variable called @pigs. In has implicitly set the value of @pigs to zero. The DeclareInt function is an example of a function call which can accept variable numbers of arguments – in this case, one or two. Here's the other form of DeclareInt:

DeclareInt(@pigs,92637)

This function call has created a variable called @pigs and set its value to 92637 – which is an awful lot of pigs.

Here's how to declare a string variable:

DeclareString($wombats)

This function call has created a string variable called $wombats. This string has no text in it – it's referred to as a "null" or "empty" string.

As with DeclareInt, you can also specify a second argument for DeclareString:

DeclareString($wombats,"Wombats on Mars are dancing, my dearà")

This call has created a string variable called $wombats and stored the text "Wombats on Mars are dancing, my dearà" in it.

Once they have been declared, variables remain in existence for the life of your applet. You can change what's stored in them, of course.

Functions which expect integer arguments can in fact be passed actual numbers, such as 3, 12 and 1942, or integer variables which contain numbers, such as @apple, @pigs and @CatInABlender. For example, this is a legal use of the Add function which turned up earlier:

DeclareInt(@six,6) DeclareInt(@three,3) Add(@six,@three)

The same is true of functions which expect string arguments – you can pass explicit strings in quotation marks, or the names of string variables.

It was noted earlier that functions always return a value. Under ANIMAL, the return value for all functions is an integer. In some cases, the numbers returned by functions aren't of enormous use. However, it's important to keep in mind that the return value of a function can be passed to another function in the place of an integer variable. For example, here's how to add three numbers together:

Add(6,Add(3,9))

In this example, the second call to Add will add the numbers 3 and 9, and return an integer value of 12. This integer will be passed as the second argument to the first call to Add, which will add it to 6 and return the final result. Typically, you would store the value returned by the first Add function somewhere, like this:

DeclareInt(@result) @result=Add(6,Add(3,9))

You can nest function calls like this to pretty well any reasonable depth. Doing so will typically result in ANIMAL applets that run slightly faster, at the expense of being a bit harder to write and track down problems in.

Here's a useful ANIMAL function. You can display text on your screen using the Print function. The text parameters – the display font and color, and the location of the text on your screen, can be set using other ANIMAL functions, to be described in detail later. The unusual aspect of Print is that it can be passed any number of arguments in any combination of types. HereÆs a legal call to Print:

Print(1," cat plus ",Sub(6,5)," blender ","\061 a world of dinner time possibilities.")

This function would display:

1 cat plus 1 blender = a world of dinner time possibilities.

Clearly dinner time is more adventurous in some homes than in others.

There are two as yet undiscussed elements in the foregoing call to Print. The first is the Sub function, which will probably be inferred to perform subtraction. Specifically, it subtracts its second argument from its first argument and returns the result. The string "\061 a world of dinnertime possibilities." looks a bit mysterious. Under ANIMAL, the \ character is special when it appears in a string – it's called an "escape." If the next three characters are digits, the escape and its three digits will be replaced with a single character having the ASCII value specified by the three digits. Use the Character Map application included with Windows to sort these out.

In addition, the escape "\n" will become a carriage return. Carriage returns are ignored by Print, but they're useful in other function calls. Should you actually want the "\" character to appear in a string, use "\\".


IMPORTANT: Never use the escapes \000 or \001 in a string under ANIMAL. These characters are used internally by ANIMAL's string manager.


Under ANIMAL, anything to the right of a semicolon is ignored. You can use this feature to add comments to your program text, or to temporarily disable lines in your programs.

As a final note in this introduction to function calls under ANIMAL, unlike all the other bits of the language, function names are not case-sensitive. This means that you can call add, Add or ADD to add numbers, and ANIMAL won't protest.




Herding your ANIMALs


In some cases, integers are used to hold values which represent the state of an earlier function call. This is what programmers call a "boolean" value. You'll probably want to forget that last remark. The state of things is defined as being either FALSE or TRUE. We represent FALSE as the number zero. While the value of TRUE is said to be one, any non-zero value is by definition TRUE.

This probably sounds like the sort of thing ancient Greek philosophers used to leap out of their baths over. It will turn up in less academic circles in a moment.

ANIMAL includes a number of ways to decide which parts of your applets get executed. This is properly called "program control." Here's what it involves:

  • GoTo – jumps to somewhere else in your program.
  • GoSub / Return – calls a subroutine in your program.
  • If – jumps to somewhere else in your program depending on the state of its argument.
  • Do / While – loops through a portion of your program.
  • End – terminates your program.

It's possible that the aforementioned ancient Greek philosophers were just unusually good at getting out of the way of trouble before it happened, and decided to forego bathing in expectation of this sort of thing. Here's what's really happening.

You can mark specific places in an ANIMAL program by inserting a "label" into your program. Labels always start with a colon. Here's an example:

:Wombats

Labels are used by functions in ANIMAL that want to change the order in which the lines of your program are executed. The most elementary of these is the GoTo function. It works like this:

:Wombats Beep() GoTo(:Wombats)

This example has also introduced another new function, Beep. The Beep function makes noise. It can actually make a variety of noises – it appears here in its most unplugged manifestation.

The GoTo function accepts a single argument, this being the name of a label to which it should jump. In this case, it will jump to the label :Wombats. The next line is a call to Beep, and the next is the GoTo call again. This bit of programming is what's called an "endless loop" – it will keep beeping until the program is terminated.

Aside from illustrating how the GoTo function works, this example also illustrates why you should avoid using GoTo as much as possible. GoTo lends itself to creating spaghetti programs – applets which embody poor or non-existent logic, and get themselves into all sorts of trouble.

The GoSub function is used to call "subroutines." A subroutine is a bit of ANIMAL code that does something and then "returns," that is, resumes execution of the main program immediately after the call to GoSub. Subroutines are a useful structure for breaking up the workings of a complex applet into small functional bits.

Here's an example of a subroutine, albeit a fairly simple one

Print("This is a noise. ") GoSub(:MakeNoise) Print("That was a noise"); End() :MakeNoise Beep(32) Return()

A subroutine always starts with a label, and ends with a call to the Return function. It's important that subroutines are only called by the GoSub function, and never blundered into accidentally by your main program. For this reason, it's a good idea to put them after the End function call – we'll get to the deeper meaning of End presently.

In this example, the GoSub function will jump to the label :MakeNoise. The :MakeNoise subroutine will make a call to Beep – you can ignore the argument which Beep appears to have sprouted for the moment. When the subroutine encounters its Return function, the main program will resume executing immediately after the GoSub function which called it – in this case, at the second Print call.

You can nest GoSub calls, that is, you can call GoSubs from within other GoSubs. The truly daring may wish to explore a breathtaking programming technique called "recursion," whereby GoSubs call themselves. There's an example of this in the Sierpinski's Curve program, to be presented later in this document.


IMPORTANT: Never use GoTo to jump into or out of a subroutine. Doing so may bring about the end of the universe as we know it. No foolin'.


The If function works like GoTo, save that it will only jump if its first argument is TRUE. Here's a simple example:

DeclareInt(@silent,1) If(@silent,:NoNoise) Beep() :NoNoise End()

In this example, the value of @silent is 1, that is, a TRUE value. The If function will jump to the label passed as its second argument if its first argument is TRUE, so in this example it jumps past the call to Beep, and no sound is generated.

In this example, @silent is 0, and If will not jump. The call to Beep will be executed.

DeclareInt(@silent,0) If(@silent,:NoNoise) Beep() :NoNoise End()

Bet you're wondering why anyone would want to do this.

ANIMAL includes a number of functions which test the state of things and return TRUE or FALSE values – 1 or 0 respectively – largely with an eye to running the If function. While the first argument to If can be any integer, you'll probably want to use these testing functions much of the time. Here are a few of them:

  • IsEqual(a,b) – returns TRUE if a equals b.
  • IsNotEqual(a,b) – returns TRUE if a does not equal b.
  • IsGreaterThan(a,b) – returns TRUE if a is greater than b.
  • IsLessThan(a,b) – returns TRUE if a is less than b.

There are also functions to see whether two strings contain the same text, whether a sound is currently playing and so on – all of which might be used to provide the first argument for If.

Here's an example of If in action:

DeclareInt(@count,0) DeclareInt(@limit,10) :loop Beep() @count=Add(@count,1) If(IsLessThan(@count,@limit),:loop) End()

This is a very simple loop – loops are hot stuff in writing programs. In this example, the value of @count is initially zero. The value is increased by one each time through the loop. If the value of @count is less than the value of @limit – which is set to 10 – the If function will jump back to the start of the loop. When @limit reaches 10, the If function will not jump, and the loop will end.

This bit of program will generate ten beeps.

In some cases, you might want If to make up its mind based on more than one condition. For example, you might want If to jump when the value of @x is greater than 10 and the value of @y is greater than 20. There are functions to combine logical states and test the results. Here's an example of how this would work:

If(And(IsGreaterThan(@x,10),IsGreatherThan(@y,20)),:GoForIt)

The And function will return TRUE if both its arguments are TRUE, or FALSE otherwise. There's also an Or function – it will return TRUE if one or both of its arguments are TRUE. This next If statement will jump if the value of @x is greater than 10 or the value of @y is greater than 20

If(Or(IsGreaterThan(@x,10),IsGreatherThan(@y,20)),:GoForIt)

You can test more than two conditions by nesting And and Or function calls.

ANIMAL also supports a Not function, which can be useful in constructing complex decision structures. Not returns the opposite state of its argument – FALSE if its argument is TRUE and TRUE if it's argument is FALSE.

Creating loops with If statements is unnecessarily cumbersome. ANIMAL includes dedicated functions for managing loops, Do and While. The Do function begins a loop and the While function tests a condition and loops back through its corresponding Do as long as the condition is TRUE. Here's one at work:

DeclareInt(@counter,0) DeclareInt(@limit,10) Do() Beep() Inc(@count) While(IsLessThan(@count,@limit))

This is another example of a bit of ANIMAL programming to beep your speaker ten times. As an aside, it introduces another new function, Inc. The Inc function will increment the variable passed to it. If you use it as it's illustrated here, it will increment @count by one each time it's called. You could also do this:

Inc(@count,2)

This would cause @count to be incremented by the value of the second argument, 2 in this case.

ANIMAL includes a corresponding function, Dec, to decrement variables.

By convention, the lines inside a Do / While loop are indented, as shown here. This is by no means mandatory – ANIMAL will ignore the indents – but indenting can help make complex loop structures easier to understand.

You can nest Do / While loops. This is often useful in creating things that happen over the two dimensions of your screen. Here's an example:

DeclareInt(@x) DeclareInt(@y) DeclareInt(@width,GetScreenWidth()) DeclareInt(@depth,GetScreenDepth()) DeclareInt(@space,10) DeclareInt(@color,MakeColor(255,255,255)) @y=0 Do() @x=0 Do() SetPixel(@x,@y,@color) Inc(@x,@space) While(IsLessThan(@x,@Width)) Inc(@y,@space) While(IsLessThan(@y,@depth))

This example will draw a matrix of dots on your screen. It uses a number of functions which have not been discussed yet – they'll have to wait for a while for a complete revelation. The structure of this bit of programming, however, should be apparent.


IMPORTANT: Never use GoTo to jump into or out of a Do / While loop. Doing so may bring about the end of the universe as we know it just as catastrophically as using GoTo to jump out of a subroutine would.


Finally, the End function terminates the execution of an applet. In a completed screen saver, this will move the screen saver's display along to the next block in its list. You can have multiple calls to End in an applet, should you have cause to terminate it under multiple circumstances.




Pictures of ANIMALs


The ANIMAL language is rich with ways to draw things on your screen – considering that screen savers are a largely visual medium, you'd probably expect this to be so. ANIMAL's graphic facilities fall into two groups:

  • Graphic primitives: Things that draw lines and other objects to your screen.
  • Image primitives: Things that deal with pictures.

Graphic primitives are easier to understand, and we'll investigate them first.

An ANIMAL applet maintains two graphic elements which are involved in drawing things – the current brush and the current pen. The brush is used to fill solid objects and the pen is used to draw lines.

The brush is defined as having a specific color. The pen is defined as having a color and a thickness in pixels.

The colors used by ANIMAL are stored in integers, but they're a bit special. They actually consist of three values, one each for the red, green and blue component of the color in question. The numbers which represent these color values are called indices. Colour indices can range between 0 and 255.

By varying the three color indices, any color can be created. Here are a number of examples. The numbers in parentheses are the red, green and blue indicies respectively.

Red (255,0,0) Green (0,255,0)
Blue (0,0,255) Cyan (0,255,255)
Magenta (255,0,255) Yellow (255,255,0)
Maroon (128,0,0) Forest Green (0,128,0)
Midnight Blue (0,0,128) Teal (0,128,128)
Grape (128,0,128) Olive (128,128,0)

While you could figure out color values by yourself, it would be tedious and technical. As such, ANIMAL provides a function call to do this, called MakeColor. It works like this:

MakeColor(255,255,0)

The first argument to MakeColor is the amount of red light in the color to be create. The second is the amount of green light and the third is the amount of blue light. The foregoing call to MakeColor will create yellow.

The Applet editor's Quick Color Selector feature can be used to generate RGB values – open this dialog, select a colour and the three numbers will be pasted into your applet at the current cursor location.

When an ANIMAL applet first runs, the current brush is white. The current pen is also white, and one pixel wide. Here's how you'd change the brush color:

SetBrushColor(MakeColor(0,0,255))

This has make the current brush blue.

This is how you'd set the pen color:

SetPenColor(MakeColor(255,255,0),4)

The SetPenColor function also sets the pen width. This has created a yellow pen that's four pixels wide.

You can change the brush and pen characteristics as frequently you wish.

The simplest drawing function under ANIMAL is the Line function. Line draws a straight line using the current pen. Because a line is not a closed shape, the current brush doesn't get involved. The Line function expects the following arguments:

Line(left, top, right, bottom)

The left and top arguments to Line define the starting point for the Line to be drawn. The right and bottom arguments define the ending point. You would make the top and bottom values identical to draw a horizontal line, and the left and right values identical to draw a vertical line.

ANIMAL also supports a function to draw curved lines, called Bézier curves – it's mind-numbingly complex, and we'll leave it alone for a while.

ANIMAL provides functions to draw rectangles, round-cornered rectangles, ellipses, pie-shaped slices and polygons. Each of these shapes can be drawn either frames or filled. Framed shapes consist of the outline of the shape in question drawn with the current pen. Filled shapes are the interior of the shape filled with the current brush.

The simplest of the solid shape functions are FrameRect and FillRect. They work like this:

FrameRect(left, top, right, bottom) FillRect(left, top, right, bottom)

The four arguments to FrameRect and FillRect specify the upper left and lower right corners of the rectangles they're to frame and fill respectively. Note that, as with all the solid shape functions, if you wanted to draw a rectangle which was both filled and drawn with an outline, you would have to call both functions, passing them the same arguments.

The functions to draw ellipses work the same way as the functions to draw rectangles – the ellipses to be drawn are defined as residing within a defined rectangle:

FrameEllipse(left, top, right, bottom) FillEllipse(left, top, right, bottom)

The FrameRoundRect and FillRoundRect functions are a fusion of the rectangle and ellipse functions. They draw round-corner rectangles, and they look like this:

FrameRoundRect(left, top, right, bottom, xround, yround) FillRoundRect(left, top, right, bottom, xround, yround)

The xround and yround arguments define the horizontal and vertical radii respectively of the round corners.

Round-corner rectangles are considered to be cheesy and horribly out of date in most civilized design circles.

The FramePolygon and FillPolygon functions will allow you to draw polygons having an unlimited number of sides. The polygon functions are passed an array as their first argument and an integer defining the number of elements in the array as their second argument. The array defines the points which form the corners of the polygon. Each point uses two consecutive array entries, wherein the first entry is the horizontal value of the coordinate, and the second entry is the vertical coordinate. If the first and last points in the array are not identical, the polygon will draw an additional line to close itself.

You can create polygons which are as complex as you like, limited only by the maximum size of an array – 32,768 elements. Arrays will be discussed in detail later in this document.

The FramePie and FillPie functions behave a bit like the FrameEllipse and FillEllipse functions, save that they only draw a portion of an ellipse. They're called like this:

FramePie(left, top, right, bottom, startangle, endangle) FillPie(left, top, right, bottom, startangle, endangle)

The pie functions draw pie-slice wedges – they're primarily useful in creating graphs and charts, or representations of desert. The startangle argument defines the location of the starting line of the pie, and the endangle argument defines the location of the ending line of the pie. The angles are expressed in degrees, where the three o'clock position is zero degrees, and increasing angle values move counterclockwise. The angle values can run from zero through 359 degrees.

The ANIMAL language also includes a function called Arc, which is called the same way FramePie is. Arc draws the round bit of a pie slice, but not the lines which connect it to the center of the circle.

While they don't actually draw anything, the SaveGraphicState and RestoreGraphicState functions are useful in working with the other ANIMAL functions that make things happen on your screen. If you call SaveGraphicState, all the drawing and text parameters you've set will be stored in memory. A subsequent call to RestoreGraphicState will fetch said parameters from memory and make them active again. You can, as such, change things like the pen and brush colors, the current font – to be discussed in a moment – and a variety of other things – and not have to worry about explicitly putting them back the way you find them. This is particularly useful for subroutines which draw things.

The only catch in using SaveGraphicState and RestoreGraphicState is that every call to SaveGraphicState must be balanced by a subsequent call to RestoreGraphicState – or unspeakably vile things will happen. See the discussion of stacks later in this document for a more involved discussion of the true meaning of "unspeakably vile."

If you want to create elements in your screen saver which look like they belong in Windows, you'll probably want to use the DrawEdge function. It's what Windows itself calls to draw the raised, depressed and etched three-dimensional edges for dialog boxes, windows and buttons. There's a complete discussion of its functions in the reference section of this document.

One of the important things to keep in mind about DrawEdge is that it does not use the current brush or pen colors. Rather, it uses colors defined by the color scheme selected by the owner of the computer on which your screen saver will run. These are called "system" colors. ANIMAL includes a function, GetSysColor, which will retrieve the current system colors. The number returned by GetSysColor is a color, just like MakeColor would return. Once again, you'll want to have a look at the reference section of this document for the details of how to call GetSysColor.

Here's a simple use of DrawEdge and GetSysColor. This is an applet to draw Windows-style buttons. Most of the work is done by the DrawButton subroutine. Note that this version of DrawButton bases the width of the buttons it draws on the width of the text that will reside in the buttons. You might want to change this to a fixed width for all the buttons, as is more commonly the case in Windows' dialogs.

DeclareInt(@bleft) DeclareInt(@btop) DeclareString($btext) DeclareInt(@bwidth) DeclareInt(@bdepth) DeclareInt(@bhpad,24) DeclareInt(@bvpad,4) @bleft=Sub(GetScreenWidth(),220) @btop=Sub(GetScreenDepth(),48) StrCpy($btext,"Cancel") Gosub(:DrawButton) @bleft=Sub(GetScreenWidth(),120) @btop=Sub(GetScreenDepth(),48) StrCpy($btext,"OK") Gosub(:DrawButton) End() :DrawButton SaveGraphicState() @bwidth=Add(GetTextLength($btext),Mul(@bhpad,2)) @bdepth=Add(GetTextDepth($btext),Mul(@bvpad,2)) SetBrushColor(GetSysColor(15)) FillRect(@bleft,@btop,Add(@bleft,@bwidth),Add(@btop,@bdepth)) DrawEdge(@bleft,@btop,Add(@bleft,@bwidth),Add(@btop,@bdepth),BitOr(1,4),15) SetPosition(Add(@bleft,@bhpad),Add(@btop,@bvpad)) SetTextTransparent(1) SetTextForeColor(GetSysColor(18)) Print($btext) RestoreGraphicState() Return()

The DrawEdge function can be used to draw more complex Windows objects as well, if you're suitably patient. Note that the buttons it creates are wholly cosmetic, as clicking on one would immediately terminate the screen saver it was part of.

Here's a bit more drawing lore. Not all Windows screen drivers have the same pixel dimensions. In designing a screen saver, you should allow for this. You can ascertain the screen dimensions by calling the GetScreenWidth and GetScreenDepth functions, respectively. If you want to draw something relative to the center of the screen, for example, you should center it based on the dimensions returned by these functions, not by positioning it in the center of your screen.

In addition to MakeColor and GetSysColor to generate color values, ANIMAL also offers GetFixedColor. The GetFixedColor function returns colors identical to those used in the color selectors found in the Header, Image and Text block editors in Screen Saver Construction Set. There are 256 fixed colors – the argument to GetFixedColor can range from zero through 255. Color number zero is black, and color number 255 is white.

ANIMAL maintains a drawing "offset." This is a pair of values which is added to the location of everything ANIMAL draws. The drawing offset is usually zero, and as such has no effect.

You can change the drawing offset by calling the SetPosition function. If you were to call:

SetPosition(100, 200)

all the subsequent drawing would take place beginning at 100 pixels to the right and 200 pixels down the upper left corner of your screen. This feature can be used to position complex drawings, among other things.

The drawing offset is saved and restored by SaveGraphicState and RestoreGraphicState.

ANIMAL supports a number of functions to work with pictures – raster images. The picture functions can be used to include photorealistic images in your screen savers, or graphics from other applications, such as Windows Paintbrush and Adobe Photoshop. See the discussion of Image blocks in the Reference document for more about importing pictures from image files.

Because the screen savers created by Screen Saver Construction Set are self-contained entities, any pictures you wish to use in an applet must be imported into your screen saver SCR file. There's a discussion of how this works in the Applet section of the Reference document. The process of importing pictures assigns a unique number to each picture available to your applet, beginning with picture number one. Here's how these numbers are displayed in the Picture selection dialog of Screen Saver Construction Set.

When you wish to display or otherwise work with a stored picture under ANIMAL, you'll need to reference it though its picture number.

The simplest application of pictures is displaying them on your screen. This is handled by the DrawPicture function. Here's what it looks like at work:

DrawPicture(picturenumber, left, top)

The first argument to DrawPicture is the number of the picture you want to draw, as found in the Applet Pictures dialog. The second and third arguments are the location of the upper left corner of the picture as it will appear on your screen.

In fact, DrawPicture is one of the ANIMAL functions which knows how to deal with a variable number of arguments. Here's DrawPicture turned up to ten:

DrawPicture(picturenumber, left, top, drawn, transparentcolor)

The fourth and fifth arguments are optional – and perhaps just the slightest bit complicated.

The fourth argument will tell DrawPicture how to handle your picture should it find itself displaying it on a computer that can manage no more than 256 colors. This issue is dealt with in detail in the Reference document's discussion of palettes. If you set this value to 0, DrawPicture will assume that your picture is photorealistic. If you set it to 1, DrawPicture will assume that your picture is drawn. DrawPicture assumes that all pictures are photorealistic if the drawn argument is not present.

The final argument to DrawPicture specifies a transparent color. If this argument is present, whatever is behind your picture will show through the image in areas where the pixels are the same color as the color passed as the fifth argument. You would typically use MakeColor to create the value for this argument.

In order for a pixel to be transparent, its color must match that of the transparentcolor argument exactly. This usually means that you'll have to prepare your graphics with a paint application to make sure that all the transparent areas are the right color.

Managing transparency slows DrawPicture down a bit.

You'll probably find that it's useful to know the pixel dimensions of the images you're working with. While you can manage this by checking out the pixel dimensions of the source image files from which your pictures are imported – the Get Info function of our Graphic Workshop Professional software is handy for this – having your applets take care of this for you is both more elegant and less susceptible to errors. Here's the function to do this:

DeclareInt(@width) DeclareInt(@depth) DeclareInt(@bits) GetPictureDimensions(1, @width, @depth, @bits)

The GetPictureDimensions function will find the pixel dimensions and color depth of the picture referenced by its first argument and store these values in the three variables passed to it. Note that you must provide it with three variables store the width, depth and bits in, even if you don't need all three numbers.

You might want to see the Introduction to Graphic Files document installed with this software if you're a bit fuzzy on things like pixel dimensions.


NOTE: Applets can only reference pictures which have been imported into the applet in question. You can't use a picture from another applet, or a picture from an Image block elsewhere in your screen saver.


ANIMAL provides several functions to deal with images which have previously been painted to your screen. The simplest of these is CopyScreenArea. The CopyScreenArea function will copy a rectangular area of your screen to another location. Here's what it looks like:

CopyScreenArea(destleft, desttop, left, top, right, bottom)

CopyScreenArea will take a picture of the rectangular area defined by the latter four arguments and paint it back to your screen at the location defined by the first two arguments.

The SaveScreenArea and RestoreScreenArea functions work a bit like CopyScreenArea, but they allow you to draw something to your screen and subsequently restore the drawn area to its original appearance. SaveScreenArea takes a picture of a rectangular area of your screen, and RestoreScreenArea paints the picture back to your screen at a later time.

SaveScreenArea(left, top, right, bottom) RestoreScreenArea(left, top)

Note that RestoreScreenArea can in fact replace the saved screen area at a location other than the one from which it was copied by SaveScreenArea if you like.

The SaveScreenArea and RestoreScreenArea functions work by saving the screen areas they copy on the ANIMAL stack – a bit of internal magic which you need not understand to write ANIMAL applets. It's essential that every call to SaveScreenArea be balanced by a corresponding call to RestoreScreenArea, or dire things will take place.




Written ANIMALs


The ANIMAL language includes functions to display text on your screen, and to manage its typographic parameters. Text under ANIMAL can include single-line and paragraph text sections, with your choice fonts and colors.

There's an important catch in using the text functions of ANIMAL. While ANIMAL will use any font you tell it to, the font in question will have to be installed on whichever machines your screen savers run on as well. If you design a screen saver to use a font called Nuclear Accident Sanserif, and Nuclear Accident Sanserif doesn't happen to be available on the machines that ultimately run your screen saver, all your text will appear in another font – most likely Courier.

It's a really good idea to restrict your use of fonts to the ones which area installed with Windows – Times New Roman, Arial, WingDings and so on. If you need text in unusual fonts, consider setting it as a graphic using Windows Paint or GIF Construction Set Professional, and then displaying it with DrawPicture. If you use GIF Construction Set Professional's Banner function to handle display text, you'll have access to its various special effects, anti-aliasing and so on.

Text is drawn under ANIMAL using the current font. When an ANIMAL applet first runs, this is always sixteen-point Arial medium. You can replace this with a font of your choosing by calling the SetFont function:

SetFont(fontname, size, bold, italic, underline)

The SetFont function expects a font name as its first argument. The second argument, size, is the size you'd like the font displayed in, in points. For practical purposes, one point is more or less equal to one pixel on your monitor.

The bold, italic and underline arguments define the effects to be applied to the new current font. Pass 1 for each of these to enable the effect in question, or 0 to disable it.

Here are a few examples of SetFont at work:

SetFont("Nuclear Accident Sanserif", 24, 0, 0, 0) SetFont("Times NewRoman", 56, 1, 0, 0) SetFont("Arial", 64, 1, 1, 0)

These would create the following current fonts, respectively:

  • 24-point Nuclear Accident Sanserif medium
  • 56-point Times New Roman bold
  • 64-point Arial bold italic

You can change the current font as often as you like.

Text is drawn using the current text foreground color for the text itself, and the current text background color for the area behind your text. These are initially white and black respectively.

The SetTextForeColor function will set the text foreground color to anything you like. Its argument is a color, as returned by MakeColor or one of the other color functions described earlier in this document. The SetTextBackColor works the same way, for the current text background color.

As an aside, you can find out what the current screen saver background color is by calling GetSaverColor – the screen saver background color is set in the Header block of a screen saver. As such, you could set the current text background color to match the screen saver background like this:

SetTextBackColor(GetSaverColor())

In addition to being set to whatever color you like the look of, the current text background can be transparent. To enable text background transparency, do this:

SetTextTransparent(1)

To disable transparency, do this:

SetTextTransparent(0)

The simplest of the two functions to draw text is Print, which has been touched on earlier. The Print function only draws single lines of text, but it can be used to print as many objects as you like. Text is always drawn by Print at the current drawing location. You can set this by calling SetPosition. You can pass combinations of integer and string arguments to Print. The Print function ignores carriage returns and other control characters. It updates the current drawing position to point to the end of your displayed text, such that subsequent calls to Print will continue printing at the end of your displayed text, unless you explicitly call SetPosition to change the current drawing position.

The dimensions in pixels of the area occupied by a line of text can be determined by calling GetTextLength and GetTextDepth. Note that these functions base their calculations on the characteristics of the current font. They only work for single lines of text.

You can print multiple lines of text – and, in fact, multiple paragraphs – with the ParagraphText function. Here's what it looks like:

ParagraphText(text, left, top, width, justification)

The ParagraphText function will display the text in the string passed as its first argument. The left and top arguments define the upper left corner of the rectangle in which the text will be drawn. The width argument defines the width of the text rectangle. The depth of the rectangle will be whatever is required to contain the text to be displayed. The rectangle depth in pixels will be returned by the ParagraphText function when it's finished drawing.

The justification argument to ParagraphText selects the text justification – pass 0 for left justification, 1 for center justification and 2 for right justification.




The Sounds of ANIMALs


The ANIMAL language includes several function to deal with making noise. The simplest of these is Beep, which was touched on earlier in this document. The Beep function actually calls the part of Windows that makes noises for dialog boxes and other Windows events.

If you call Beep with no argument, the default Windows sound will be played. This is also true if you call it with an argument of 0. If you call it with an argument of -1, the internal squeaker-speaker in your computer will click.

There are four other noises Beep knows how to make – these are the arguments which will generate them.

  • 16 – The sound associated with the hand icon.
  • 32 – The sound associated with the question mark icon.
  • 48 – The sound associated with the exclamation point icon.
  • 64 – The sound associated with the asterisk icon.

The actual sounds that Beep generates are set through the Sounds applet in the Windows Control Panel, and can be assumed to vary from system to system.

You can play sounds and music of your own choosing through the PlaySound function. It looks like this:

PlaySound(soundnumber)

As with the DrawPicture function discussed earlier, PlaySound expects to be passed reference number for a sound loaded through the Applet editor of Screen Saver Construction Set. Sounds can be imported into a screen saver from WAV and MID files. WAV files are sampled sounds, and MID files are sequenced music. There's a more complete discussion of sounds in the Reference document's Sound section.

The PlaySound function will only play one sound at a time. If you call it while a sound is playing, it will terminate the existing sound before it begins playing a new sound.

You can determine whether a sound is playing by calling the IsSoundPlaying function. IsSoundPlaying returns a TRUE value if a sound is playing, or a FALSE value otherwise.

If you leave a sound to play until it runs out, it will terminate itself. Should you wish to terminate a sound early, you can call KillSound.

Note that sounds have a potential catch in screen savers. Many newer systems come with pretty powerful speakers, and loud sounds can generate enough vibration to move your mouse and shut down your screen saver. Softer, less percussive sounds with a minimum of deep, rumbling bass notes are somewhat safer.


NOTE: Applets can only reference sounds which have been imported into the applet in question. You can't use a sound from another applet, or a sound from a Sound block elsewhere in your screen saver.





Strange Little ANIMALs


No one has ever managed to write a document like this one without a section to deal with all the fiddley details that didn't fit in the other sections. This includes us.

One of the aspects of ANIMAL which has been touched on but not really explained is that of arrays. Array variables are tables of integers. Once declared you can store integers in specific elements of an array, and subsequently retrieve them.

Array variables are always identified with the & character. Here's how to create an array:

DeclareArray(&table,256)

This has created an array called &table, having 256 entries. An array can have up to 32,768 entries. Once an array has been declared, it cannot be resized.

You can regard the array &table as being 256 integers, stacked one atop the other. Imagine one of those neo-trendy compact disc racks from Sears. To store something in one of the integers, or "elements" of the array, you would do this:

SetArrayValue(&table,6,22)

This has stored the value 22 in the sixth element of the array. Array elements are numbered starting with 0.

You can retrieve values from an array like this:

DeclareInt(@result) @result=GetArrayValue(&table,6)

In this case, the value of @result would be 22.

You can see a fairly complex application of arrays in the Sierpinski's Curve program near the end of this document.


TIP: Tables are a useful way of dealing with multiple complex calculations which must be performed over and over again, but which yield a finite number of results. Rather than perform such calculations repeatedly, do it once and store the results in a table.


The ANIMAL language includes functions to deal with time and dates. These can be used to measure time, kill time while something happens or just tell users of your screen saver that it's time to go home.

The simplest of these functions is Sleep. The Sleep function accepts an argument specifying the number of milliseconds, or thousandths of a second, that it's required to so nothing. When you call sleep, it goes into a holding pattern for a while, and then allows your application to resume.

Sleep is useful for things like slowing down animations and other drawing activity on your screen. It's important to keep in mind that while Sleep will sleep for the same duration on all machines, the speed of other functions of ANIMAL can be affected by the speed of the processor in the computer on which they find themselves running. Don't count on Sleep for absolute timing.


NOTE: While it's asleep, Sleep checks the status of your screen saver every 100 milliseconds. If your screen saver terminates, Sleep will immediately wake up, and your applet will shut down as it's supposed to.


The GetSystemTime function returns the current status of the system clock on your computer. The clock counts the number of seconds between January 1, 1970 and the present. This value is largely useless in an absolute sense, but if you call GetSystemTime now, wait a while and call GetSystemTime again, you can figure out how long "a while" was.

Finally, the StrGetTime function will get the system time and date, format it into something presentable and store it in a previously declared string variable. Here's how it works:

DeclareString($thetime) StrGetTime($thetime)

The string in $thetime will be 24 characters long, and time will be formatted as follows:

Thu Jul 15 01:10:30 1999

You can use the string manipulation functions available under ANIMAL to extract portions of this string.

As an aside, ANIMAL executes its applets one line at a time, and there's a small but measurable delay while it fetches each one. In some cases, you'll want to have it do multiple things as quickly as possible. While ANIMAL doesn't allow for multiple function calls per line, you can often trick it into letting you get away with this.

You might, for example, wish to call StrGetTime and Print as quickly as possible. Note that both these functions return values, even though the values aren't really of any use. You can combine them both on the same line, then, by doing something with the return values:

And(StrGetTime($thetime),Print($thetime))

This peculiar bit of programming will call StrGetTime, which will store the current system time and date in the variable $thetime. It will then call Print to display it. Finally, it will AND the two return values together. In fact, the result of this final function is irrelevant – using these two function calls as arguments to And, however, eliminated the delay that would have existed between them had they resided on separate lines.

There's a resource in ANIMAL to let you personalize the screen savers you create to include the name of whoever uses them. In addition to whichever string variables you create, two "magic" strings exist for any running applet. The string variable $_OWN contains the name of the owner or user of the computer upon which the screen saver in question is running. The string $_ORG contains the name of the company that owns the computer. These names are supplied when Windows is installed.

For example, if you do this:

Print($_OWN)

The name of the owner or current user of the computer on which your screen saver is running will be displayed.




All the ANIMALs in the Zoo


This section is an alphabetic reference to all the functions supported by ANIMAL. The function calls are listed here with the number and types of their arguments. Arguments enclosed in square brackets are optional.

Function names are case-insensitive under ANIMAL.

Abs(integer a)

    The Abs function returns the absolute value of its argument. If the argument is less than zero, a positive value will be returned.

Add(integer a1, integer a2)

    The Add function returns the sum of its two arguments

And(integer a1, integer a2)

    The And function returns the logical AND of its two arguments. If both arguments are TRUE, the return value will be TRUE – otherwise it will be FALSE.

Arc(integer left, integer top, integer right, integer bottom, integer startangle, integer endangle)

    The Arc function draws an arc of the ellipse bounded by the rectangle defined by its first four arguments. The arc begins at the angle defined by startangle and ends at the angle defined by endangle. The three o'clock position is represented by zero degrees, and increasing angles rotate counterclockwise. Angles are specified in degrees, ranging from zero through 359.

Beep([integer type])

    The Beep function will cause your system's speaker to beep. If you call Beep with no argument, your system's default sound will be generated. There are a number of other sounds available to Beep, which can be selected by the argument passed to it. These sounds correspond to the sounds generated when system messages appear under Windows.

    • 0 – The default sound.
    • 16 – The sound associated with the hand icon.
    • 32 – The sound associated with the question mark icon.
    • 48 – The sound associated with the exclamation point icon.
    • 64 – The sound associated with the asterisk icon.
    • -1 – A short click through the internal speaker, rather than your sound card.

    Other values passed to Beep may have unpredictable results.

    Note that the actual sounds associated with these arguments can be configured by the users of Windows machines through the Sounds applet of the Control Panel. As such, Beep may make different noises on different computers.

BezierCurve(array a1, integer count)

    The BezierCurve function draws complex Bézier curves. The array passed as its first argument defines the control points for the curve, and the value of the second argument specifies the number of elements in the array.

    The following illustrates the control points of a Bézier curve:

    A Bézier curve must be defined by a minimum of four points. Each point is stored as two consecutive elements in the array passed to BezierCurve. The first element is the horizontal coordinate and the second element is the vertical coordinate. The first point is the starting anchor; the second point is the first control; the third point is the second control and the fourth point is the ending anchor. A second curve can be added to the first by adding three additional points – six array entries – to the list of points. The ending anchor for the first curve will become the starting anchor for the second curve.

    You can generate as many contiguous Bézier curves as you like with a single call to BezierCurve. Note that the control points of a Bézier curve may reside outside the boundaries of your screen.

BitAnd(integer a, integer mask)

    The BitAnd function performs a bitwise AND between its two arguments and returns the result.

BitOr(integer a, integer mask)

    The BitOr function performs a bitwise OR between its two arguments and returns the result.

BitXOr(integer a, integer mask)

    The BitXOr function performs a bitwise Exclusive OR between its two arguments and returns the result.

ClearScreen([integer color])

    The ClearScreen function clears the entire screen. If you don't pass an argument to ClearScreen, the screen will be cleared to the background color defined in the Header block of your screen saver. Alternately, you can pass it a color to clear the screen to. See the MakeColor function for more about generating colors.

CopyScreenArea(integer destleft, integer desttop, integer left, integer top, integer right, integer bottom)

    The CopyScreenArea function copies the rectangular area defined by the left, top, right and bottom arguments to the location specified by the destleft and desttop arguments. If the source rectangle for CopyScreenArea extends beyond the edges of your screen, the actual area copied will be adjusted to only include pixels which reside within your screen area. The CopyScreenArea function creates a copy of the area to be duplicated before it paints new pixels to your screen, so applying it to overlapping areas will work correctly.

Dec(integer variable a, [integer value])

    The Dec function will decrement the value of the integer variable passed as its first argument. By default, it will subtract 1 from the integer variable. If you pass a second argument to Dec, it will subtract the value of this argument.

DeclareArray(array name, integer count)

    The DeclareArray declares an array defined by the name argument and allocates count integer values. All the values are initially set to zero. Once an array has been defined, it cannot be destroyed or its size changed. Array variable names are identified as beginning with the & character. The value of count must be greater than zero and less than 32768.

DeclareInt(integer variable name, [integer init])

    The DeclareInt function declares an integer variable. By default, new integer variables are assigned the value zero. If the init argument is present, it will be stored in the newly created integer variable. Integer variable names are identified as beginning with the @ character.

DeclareString(string variable name, [string init])

    The DeclareString function declares a string variable. By default, new string variables contain zero-length strings. If the init argument is present, it will be stored in the newly created string variable. String variable names are identified as beginning with the $ character.

Div(integer a, integer b)

    The Div function returns a divided by b.

Do()

    The Do function indicates the beginning of a Do / While loop.

DrawEdge(integer left, integer top, integer right, integer bottom, integer edgeflags, integer borderflags)

    The DrawEdge function allows you to access Windows' internal logic for drawing the edges of three-dimensional objects, such as buttons and frames. It's useful for simulating bits of Windows in your screen savers. The left, top, right and bottom arguments to DrawEdge define the rectangular area for the edge to be drawn. The edgeflags argument specifies the edge characteristics and the borderflags argument specifies which borders the edges will apply to. The latter two values can be a bit tricky to work with.

    The edgeflags argument is formed by bitwise ANDing the following values together:

    • Raised outer edge – 1
    • Sunken outer edge – 2
    • Raised inner edge – 4
    • Sunken inner edge – 8

    Useful combinations of these flags include:

    • Raised edge – BitOr(1,4)
    • Sunken edge – BitOr(2,8)
    • Etched edge – BitOr(4,8)
    • Bump edge – BitOr(1,8)

    The borderflags argument is formed by bitwise ANDing the following values together:

    • Left border – 1
    • Top border – 2
    • Right border – 4
    • Bottom border – 8

    Useful combinations of these flags include:

    • Top left – BitOr(2,1)
    • Top right – BitOr(2,4)
    • Bottom left – BitOr(8,1)
    • Bottom right – BitOr(8,4)
    • Rectangle – BitOr(BitOr(1,2),BitOr(4,8))

    The most commonly used borderflags argument will be Rectangle – all four sides. You can save yourself some typing by keeping in mind that the result of all those calls to BitOr will, in fact, be 15. If borderflags is set to 16, a diagonal line will be drawn.

    The DrawEdge function draws using the colours defined in the colour scheme selected through the Windows control panel.

DrawGradient(integer startcolor, integer endcolor, integer left, integer top, integer right, integer bottom, integer vertical)

    The DrawGradient function will fill a rectangular area with a color gradient. The gradient will be a smooth transition between the startcolor value and the endcolor value. The color values will typically be generated by MakeColor.

    Set the vertical argument to 0 if you want to create a gradient which runs horizontally, or to 1 to create a gradient which runs vertically.

    Gradients are significantly less attractive in a 256-color environment.

DrawPicture(integer picturenumber, integer left, integer top,[integer drawn, integer transparentcolor])

    The DrawPicture function will draw a picture from the picture list of the current applet. The picture to be drawn is specified by the picturenumber argument. Picture numbers are assigned when pictures are imported into an applet. The upper left corner of the picture to be drawn will appear at the coordinates specified by the left and top arguments.

    On a system with a screen driver capable of displaying more than 256 colors, pictures will be drawn using the colors of their sources images. On systems with limited color screen drivers, pictures will be dithered or remapped to the common screen saver palette. Set the drawn value to 0 for photorealistic pictures, or to 1 for drawn images.

    If the transparentcolor argument is included in a call to DrawPictures, any pixels in your source images which have the color specified in transparentcolor will not be drawn, and whatever is behind them will show through. The colors must match exactly for transparency to work. Displaying picture with transparency is somewhat slower than displaying a solid image. In addition, note that transparency may not work as you expect on systems with screen drivers capable of displaying 256 or fewer colors.

    See this discussion of pictures elsewhere in this document for more about using pictures in ANIMAL applets.

End()

    The End function terminates a running applet.

FillRect(integer left, integer top, integer right, integer bottom)

    The FillRect function draws a solid rectangle in the area defined by its left, top, right and bottom arguments, using the current brush color.

FillRoundRect(integer left, integer top, integer right, integer bottom, integer xround, integer yround)

    The FillRoundRect function draws a solid rectangle in the area defined by its left, top, right and bottom arguments, using the current brush colour. The rectangle will have rounded corners. The corner will be drawn as arcs xround pixels wide by yround pixels deep.

FillEllipse(integer left, integer top, integer right, integer bottom)

    The FillEllipse function draws a solid ellipse in the area defined by its left, top, right and bottom arguments, using the current brush colour.

FillPie(integer left, integer top, integer right, integer bottom, integer startangle, integer endangle)

    The FillPie function draws a solid pie-shaped segment of the ellipse bounded by the rectangle defined by its first four arguments, using the current brush colour. The arc begins at the angle defined by startangle and ends at the angle defined by endangle. The three o'clock position is represented by zero degrees, and increasing angles rotate counterclockwise. Angles are specified in degrees, ranging from zero through 359.

FillPolygon(array points, integer count)

    The FillPolygon draws a solid filled polygon using the current brush colour. The points of the polygon are defined by the array passed as the first argument. Each point is stored as two consecutive elements in the array. The first element is the horizontal coordinate and the second element is the vertical coordinate. The count argument defines the number of elements in the array, that is, it's twice the number of points in the polygon. If the first and last points in the array are not identical, the polygon will draw an additional line to close itself.

    The value of count must be 4 or greater.

FrameEllipse(integer left, integer top, integer right, integer bottom)

    The FrameEllipse function draws a hollow ellipse in the area defined by its left, top, right and bottom arguments, using the current pen.

FramePie(integer left, integer top, integer right, integer bottom, integer startangle, integer endangle)

    The FramePie function draws as hollow pie-shaped segment of the ellipse bounded by the rectangle defined by its first four arguments, using the current pen. The arc begins at the angle defined by startangle and ends at the angle defined by endangle. The three o'clock position is represented by zero degrees, and increasing angles rotate counterclockwise. Angles are specified in degrees, ranging from zero through 359.

FramePolygon(array points,integer count)

    The FramePolygon draws a hollow polygon using the current pen. The points of the polygon are defined by the array passed as the first argument. Each point is stored as two consecutive elements in the array. The first element is the horizontal coordinate and the second element is the vertical coordinate. The count argument defines the number of elements in the array, that is, it's twice the number of points in the polygon. If the first and last points in the array are not identical, the polygon will draw an additional line to close itself.

FrameRect(integer left, integer top, integer right, integer bottom)

    The FrameRect function draws a hollow rectangle in the area defined by its left, top, right and bottom arguments, using the current pen.

FrameRoundRect(integer left, integer top, integer right, integer bottom, integer xround, integer yround)

    The FrameRoundRect function draws a hollow rectangle in the area defined by its left, top, right and bottom arguments, using the current pen. The rectangle will have rounded corners. The corner will be drawn as arcs xround pixels wide by yround pixels deep.

GetArrayValue(array name, integer index)

    The GetArrayValue function will return the value stored in the array element specified by index.

GetFixedColor(integer number)

    The GetFixedColor function will return the color of the entry of the standard screen saver palette specified by number. The value of number must be between 0 and 255. The colors returned by GetFixedColor will match the colors used in the Header, Image and Text blocks of Screen Saver Construction Set.

GetHorizontalPos()

    The GetHorizontalPos function returns the horizontal value of the current drawing position.

GetPictureDimensions(integer picturenumber, integer variable xvariable, integer variable yvariable, integer variable bitsvariable)

    The GetPictureDimensions function will store the dimensions in pixels and the bit depth of a picture in the variables passed to it. The picture to be measured is specified by the picturenumber argument. Picture numbers are assigned when pictures are imported into an applet. The value stored in bitsvariable will be 1, 4, 8 or 24.

GetPixel(integer left integer top)

    The GetPixel function returns the color of the pixel on your screen at the location defined by left and top.

GetSaverColor()

    The GetSaverColor returns the screen saver background color, as specified in the Header block of your screen saver.

GetScreenBits()

    The GetScreenBits function returns the color depth in bits of your screen driver.

GetScreenDepth()

    The GetScreenDepth function returns the depth in pixels of your screen.

GetScreenWidth()

    The GetScreenWidth function returns the width in pixels of your screen.

GetSysColor(integer index)

    The GetSysColor function returns the color of a Windows system element. System colours are defined through the display applet of the Windows Control Panel. The color returned is defined by the argument passed to GetSysColor, as follows:

    • Scroll bar
    •  0
    • Background
    •  1
    • Active caption
    •  2
    • Inactive caption
    •  3
    • Menu
    •  4
    • Window
    •  5
    • Window frame
    •  6
    • Menu text
    •  7
    • Window text
    •  8
    • Caption text
    •  9
    • Active border
    •  10
    • Inactive border
    •  11
    • Application work space
    •  12
    • Highlight
    •  13
    • Highlighted text
    •  14
    • Button face
    •  15
    • Button shadow
    •  16
    • Grey text
    •  17
    • Button text
    •  18
    • Inactive caption text
    •  19
    • Button highlight
    •  20
    • 3D dark shadow
    •  21
    • 3D light
    •  22
    • Information text
    •  23
    • Information background
    •  24

    Passing values to GetSysColor other than those specified here may produce unpredictable results.

GetSystemTime()

    The GetSystemTime function returns the number of seconds which have elapsed since January 1, 1970, based on the current setting of the system clock. This value is not affected by year-2000 issues, but the maximally-paranoid should note that it will wrap back to January 1, 1970 some time in July of the year 2116. If you write any screen savers which you feel will still be popular a century from now, be sure to add a warning to this effect to their documentation.

GetTextDepth(string text)

    The GetTextDepth function will return the depth in pixels of the text passed to it, based on the current font.

GetTextLength(string text)

    The GetTextLength function will return the width in pixels of the text passed to it, based on the current font.

GetVerticalPos()

    The GetVerticalPos function returns the vertical value of the current drawing position.

GoSub(label subroutine)

    The Gosub function will suspend execution of the current applet and call the subroutine specified by the argument passed to it. When the subroutine terminates by calling the Return function, execution of the applet will resume with the line immediately following the GoSub.

GoTo(label jump)

    The GoTo function will jump to the label passed as its argument and continue executing the current applet from there. GoTo's are considered to be the tools of unclean spirits, and will lead you into the hell of funky programmers, from whence no soul has ever returned – something worth keeping in mind.

If(integer state, label jump)

    The If function will jump to the label passed as its second argument if its first argument is TRUE, that is, non-zero.

Inc(integer variable a, [integer value])

    The Inc function will increment the value of the integer variable passed as its first argument. By default, it will add 1 to the integer variable. If you pass a second argument to Inc, it will add the value of this argument.

IsEqual(integer a, integer b)

    The IsEqual function will return TRUE if a equals b, or FALSE otherwise.

IsGreaterThan(integer a, integer b)

    The IsGreaterThan function will return TRUE if a is greater than b, or FALSE otherwise.

IsGreaterThanOrEqualTo(integer a, integer b)

    The IsGreaterThanOrEqualTo function will return TRUE if a is greater than or equal to b, or FALSE otherwise.

IsLessThan(integer a, integer b)

    The IsLessThan function will return TRUE if a is less than b, or FALSE otherwise.

IsLessThanOrEqualTo(integer a, integer b)

    The IsLessThanOrEqualTo function will return TRUE if a is less than or equal to b, or FALSE otherwise.

IsNotEqual(integer a, integer b)

    The IsNotEqual function will return TRUE if a does not equal b, or FALSE otherwise.

IsSoundPlaying()

    The IsSoundPlaying will return TRUE if a sound is currently playing, or FALSE otherwise.

KillSound()

    The KillSound function will stop the currently playing sound, if there is one. If no sound is playing, it does nothing.

Line(integer left, integer top, integer right, integer bottom)

    The Line function draws a line from the point defined by left, top to the point defined by right, bottom, using the current pen. The current drawing position is set to right, bottom.

MakeColor(integer red, integer green, integer blue)

    The MakeColor function returns a color value defined by the red, green and blue values passed to it. The arguments must be between 0 and 255. The value returned by MakeColor can be used as an argument for any ANIMAL function which expects a color.

Max(integer a, integer b)

    The Max function returns the greater of its two arguments.

Min(integer a, integer b)

    The Min function returns the lesser of its two arguments.

Mod(integer a, integer b)

    The Mod function returns the remainder of a divided by b. Rumors to the effect that Mod actually draws bell-bottom jeans on your screen and says "groovy" a lot are spurious.

Mul(integer a, integer b)

    The Mul function returns a multiplied by b.

Not(integer state)

    The Not function returns the opposite state of its argument. If state is TRUE, Not returns FALSE. If state is FALSE, Not returns TRUE.

Or(integer a1, integer a2)

    The Or function returns the logical OR of its two arguments. If either argument is TRUE, the return value will be TRUE – otherwise it will be FALSE.

ParagraphText(string text, integer left, integer top, integer width, integer justification, [integer margin])

    The ParagraphText function draws formatted paragraph text using the current font and text colour. The text passed to ParagraphText can include carriage returns. It will be drawn in a rectangle having its upper left corner in the location specified by the left and top arguments, and a width defined by the width argument. The depth of the rectangle will be whatever is required to draw all the text.

    ParagraphText can use one of the following justification types:

    • Left
    •  0
    • Center
    •  1
    • Right
    •  2

    The margin argument defines the additional margin area around the the text, in pixels.

    The ParagraphText function returns the depth of the rectangle which contains the text.

PlaySound(integer soundnumber)

    The PlaySound function will play a sound. The sound to be played is specified by the soundnumber argument. Sound numbers are assigned when sounds are imported into an applet. Only one sound can play at a time – if you call PlaySound while a sound is playing, the current sound will be terminated and the new sound played.

Pow(integer a1, integer a2)

    The Pow function returns a1 raised to the power of a2.

Print([variable arguments])

    The Print function displays text on your screen at the current drawing position. It updates the current drawing to the end of the displayed text position when it's done. It uses the current font and text color.

    You can pass any number of integer and string arguments to Print. The arguments will be printed in the order they're passed.

    The Print function only prints single lines of text, and it ignores carriage returns.

Random(integer limit)

    The Random function returns a random number between 0 and limit-1.

RestoreGraphicState()

    The RestoreGraphicState function restores the text and drawing parameters saved by a previous call to SaveGraphicState. It's essential that every call to SaveGraphicState have a corresponding call to RestoreGraphicState.

RestoreScreenArea(integer left, integer top)

    The RestoreScreenArea function paints a previously saved screen fragment to the screen at the location defined by left and top. RestoreScreenArea must be called after a corresponding call to SaveScreenArea. To balance a call to SaveScreenArea without actually painting anything to your screen, call RestoreScreenArea with arguments that will cause it to paint an area beyond the edge of your screen.

Return()

    The Return function returns from a subroutine called through GoSub.

SaveGraphicState()

    The SaveGraphicState function stores the current drawing position, text colour, back color, text background transparency mode, brush, pen and font. You can subsequently change some or all of these values. Upon calling RestoreGraphicState, all the save values will be restored. You can nest calls to SaveGraphicState, but it's essential that every call to SaveGraphicState be balanced by a corresponding call to RestoreGraphicState.

SaveScreenArea(integer left, integer top, integer right, integer bottom)

    The SaveScreenArea function copies the screen area defined by its arguments and stores it. The RestoreScreenArea function can be used to paint a saved area to the screen. Every call to SaveScreenArea must be balanced by a call to RestoreScreenArea.

SetArrayValue(array name, integer index, integer value)

    The SetArrayValue function will set the element of an array specified by index to value.

SetBrushColor(integer color)

    The SetBrushColor function will set the current brush to the color specified by its argument.

SetDrawingOffset(integer left, integer top)

    The SetDrawingOffset function will set the current drawing offset to left and top. The drawing offset values area added to the coordinates of everything ANIMAL draws to your screen. The default values area zero.

SetFont(string name, integer size, integer bold, integer italic, integer underline)

    The SetFont function sets the current text font. The current font will be used in subsequent calls to Print, GetTextDepth, GetTextLength, and ParagraphText.

    The name argument can be the name of any font installed in your system. However, keep in mind, if you plan to distribute your screen savers, that the fonts you use will also have to have been installed on any other machines that will run your screen savers. Restricting your selection of fonts to Times New Roman, Arial, WingDings, Courier New and other common Windows fonts is less likely to have your screen savers behave oddly when they display text on someone else's computer. If name specifies a font that doesn't exist, Windows will choose a default font, usually Arial or Courier.

    The size argument defines the size of the font in points. For practical purposes, one point is equivalent to one pixel on your monitor. Professional typographers will usually get all red in the face and pop a few minor blood vessels if you suggest this within their hearing, but it's nearly true. The value of size should not be less than 4, and probably several points larger still if you actually expect people to be able to read your text without a microscope.

    Set bold, italic and underline to 1 to enable these effects, or to 0 to disable them.

SetPenColor(integer color, integer thickness)

    The SetPenColor function will set the current drawing pen to the color and thickness specified in its arguments. The value of thickness should be one or greater.

SetPixel(integer left, integer top, integer color)

    The SetPixel function sets the pixel on your screen at the location specified by left and top to color.

SetPosition(integer left, integer top)

    The SetPosition function sets the current drawing position to left and top.

SetTextBackColor(integer color)

    The SetTextBackColor function sets the current text background color.

SetTextForeColor(integer color)

    The SetTextForeColor function sets the current text foreground color.

SetTextTransparent(integer transparent)

    The SetTextTransparent function sets the text transparency mode. Pass it an argument of 1 to have subsequent calls to Print and ParagraphText draw text with no background color, or an argument of 0 to have the background of text drawn in the current text background color.

ShiftLeft(integer a1, integer a2)

    The ShiftLeft function returns a1 bitwise shifted left by a2.

ShiftRight(integer a1, integer a2)

    The ShiftLeft function returns a1 bitwise shifted right by a2.

Sleep(integer milliseconds)

    The Sleep function will cause your applet to do nothing for the number of milliseconds passed to it. The Sleep function checks the state of your screen saver while it's sleeping – if the screen saver closes while it's sleeping, it won't stay asleep.

StrCat(string variable, string text)

    The StrCat function adds the text in its second argument to the text in the string variable passed as its first argument. Having nothing much to do with felines, this process is properly called "concatenation."

StrCmp(string s1, string s2)

    The StrCmp function returns TRUE if the two strings passed to it are identical, or FALSE otherwise.

StrCmpI(string s1, string s2)

    The StrCmpI function performs a string comparison like StrCmp, except that it ignores the case of the strings in question.

StrCpy(string variable, string text)

    The StrCpy function copies the text in its second argument to the string variable passed as its first argument. Any text previously stored in the string variable will be discarded.

StrFromChar(string variable, integer c)

    The StrFromChar function creates a one-character long string which holds a character having the ASCII value c, and stores the string in the string variable passed as its first argument.

StrFromInt(string variable text, integer c)

    The StrFromInt function creates a string which holds a text respresentation of the number c, and stores the string in the string variable passed as its first argument.

StrGetChar(string text, integer index)

    The StrGetChar function returns the ASCII value of the character at location index in the string passed as its first argument. If index is greater than the length of the string, it returns the ASCII value of the last character in the string.

StrGetTime(string variable text)

    The StrGetTime function will get the current system time and date and store it as text in the string variable passed to it. The string will always be 24 characters long, and time will be formatted as follows:

    • Thu Jul 15 01:10:30 1999

StrLen(string text)

    The StrLen function returns the number of characters in the string passed to it.

StrLwr(string variable text)

    The StrLwr function will set all the alphabetic characters in the string variable passed to it to lower case.

StrSub(string variable destination, string initialstring, integer start, integer end)

    The StrSub function will copy a section from the text passed to it as its initialstring argument beginning with the character position specified by start until the character position specified by end, and place the new string in destination.

StrUpr(string variable text)

    The StrUpr function will set all the alphabetic characters in the string variable passed to it to upper case.

Sub(integer a1, integer a2)

    The Sub function will return a1 minus a2.

While(integer state)

    The While function designates the end of a Do / While loop. As long as the value of the argument passed to While is TRUE – non-zero – the loop will repeat. Every call to Do must be balanced by a call to While.


NOTE: In addition to whichever string variables you create, two "magic" strings exist for any running applet. The string variable $_OWN contains the name of the owner or user of the computer upon which the screen saver in question is running. The string $_ORG contains the name of the company that owns the computer. These names are supplied when Windows is installed.





Stacks of ANIMALs


This next section is technical, and you can safely ignore it if you find such matters disturbing. It deals with a bit of the internal workings of the ANIMAL language, its "stack." You will never have to interact directly with the stack, and it wouldn't have come up at all save that knowing how the stack behaves can be useful in keeping your ANIMAL applets from blowing up unexpectedly.

A stack is a way for computers to keep track of things in the order in which they appear. It is, as its name implies, a list to which things can be added and from which things can be removed. The most recent thing added to a stack is always the first thing to be removed.

When something is placed on the stack, it is said to have been "pushed." When it gets removed from the stack, it has been "popped."

The processor which runs your computer maintains a stack. ANIMAL also maintains a stack – it's worth noting that these stacks are different, and don't get involved with each other.

An ANIMAL applet uses the stack for a number of purposes, including:

  • Storing GoSub return addresses: When an applet executes a GoSub, the line number of the GoSub is pushed onto the stack. At such time as the applet encounters the Return for said GoSub, Return pops the line number off the stack, and knows where to resume execution of the main program.

  • Saving and restoring graphic states: If you use SaveGraphicState in your applets, the graphic information is pushed onto the stack. When you call RestoreGraphicState, it's popped off the stack and restored.

  • Do / While loops: The Do function of a Do / While loop pushes its line number onto the stack. The While function pops it off the stack and as such knows where its corresponding Do is.

  • Saving the screen area: The SaveScreenArea function copies an area of your screen and pushes it onto the stack. The corresponding RestoreScreenArea pops it off the stack and paints it back to your screen.

In addition, the stack is used to manage the arguments for function calls.

None of this would be worth mentioning, save that writing ANIMAL applets which misuse the stack can get it into trouble. For example, imagine a program which called the Do function, but never got around to balancing those calls with While. Each time a Do was executed, the line number of the Do would be pushed onto the stack. With no While functions to pop these numbers off the stack, they'd accumulate. They might breed in there and form families.

The stack is a large but finite entity. In a properly-written applet, it never gets full, because everything pushed onto it is popped off it shortly thereafter. However, in the foregoing scenario – multiple calls to Do without balancing calls to While – things would be added to the stack that would never be removed. Eventually the stack would fill up. This is called "stack overflow," and when the stack overflows, your applet will terminate.

Stack overflow problems are a bit insidious, because it's not always apparent that your stack is gradually getting full. This is one of the reasons why it's important to write logical, well thought-out applets. This is also why the GoTo function is to be avoided, as it's one of the easiest ways to confuse the logic of a program.

As a final note about stacks, the default stack for an ANIMAL applet allows for 8192 entries, which is probably about eight thousand more than you'll ever need. You can create applets with larger stacks, should you find that you genuinely need to do so. The default stack size can be set through the Screen Saver Construction Set Setup dialog. Note that this value only pertains to new applets – it will not adjust the stack size for existing ones.

Should you genuinely need to increase the stack size for an applet, copy the existing applet text to the clipboard, create a new applet and paste the text into it.

Before you start thinking about increasing the stack size for an applet, however, you should make sure that your applet isn't simply pushing lots of things onto the stack and failing to pop them off.




Examples of ANIMALs in the Wild


The following are several small ANIMAL applets. One of the best ways to learn to write programs of any sort is to begin with existing programs and modify them to do what you had in mind. Registered users of Screen Saver Construction Set are, of course, welcome to abstract these bits of code for their own use.


Tiled Background

The textured backgrounds used on web pages are actually small graphics painted repeatedly to fill the entire screen area. You can do this with a screen saver as well. The following illustrates an applet which generates a tiled background.

In this example, the variable @tile contains the picture reference number for the tile bitmap. The GetPictureDimensions function is used to ascertain the size of the picture to be used as a tile. Two nested Do / While loops make repeated calls to DrawPicture to display the tile.

DeclareInt(@tile,1) DeclareInt(@swide,GetScreenWidth()) DeclareInt(@sdeep,GetScreenDepth()) DeclareInt(@twide) DeclareInt(@tdeep) DeclareInt(@tbits) DeclareInt(@x) DeclareInt(@y) GetPictureDimensions(@tile,@twide,@tdeep,@tbits) @y=0 Do() @x=0; Do() DrawPicture(@tile,@x,@y) Inc(@x,@twide) While(IsLessThan(@x,@swide)) Inc(@y,@tdeep) While(IsLessThan(@y,@sdeep)) End()


Bouncing Pong Graphic

Screen savers with objects drifting leisurely across your monitor, rebounding from the plastic bits at the edges, are all the rage in some quarters. They're relatively easy to implement. The example here uses a picture, but you can do much the same thing with text.

You can see this example at work in the demonstration screen saver installed with Screen Saver Construction Set.

The bouncing graphic is a simple example of animation. The image is repeatedly repainted, with each iteration slightly removed from the previous one. This example of animation cheats a bit – the source image is eight pixels larger on all sides than it has to be, with the extra area filled with black. Because the graphic is never allowed to move more than eight pixels per iteration, each new graphic overpaints the previous one.

The procedure for making the graphic move is based on two position variables, one for each dimension, and two delta variables, which determine how much it can move per iteration. If the position of the graphic exceeds and edge of the screen, the corresponding delta variable is multiplied by -1, that is, its sign is changed.

In fact, to make the trajectory of the graphic more interesting, the delta variables are given a small random component as well.

In this first example, the graphic, referenced by the value in @pic, is repeatedly painted. The Beep function is called whenever the graphic has changed directions, that is, when it hits a wall. The Sleep function is used to slow it down to a lazy crawl.

In this example, the Do / While loop that runs the graphic keeps looping as long as 1 is TRUE, which is to say, forever. It can only be terminated by the screen saver shutting down. In a more complex screen saver, with multiple blocks, you'd probably want to have it run for a predetermined time.

To make the loop run for a fixed time, you would do this:

DeclareInt(@stime,GetSystemTime()) DeclareInt(@period,120) Do() ;animation happens here While(IsLessThan(GetSystemTime(),Add(@stime,@period)))

This modified Do / While loop works by fetching the system time before the loop starts, storing it, and then repeatedly checking to see if the number of seconds stored in @period have elapsed.

DeclareInt(@pic,1) DeclareInt(@swide,GetScreenWidth()) DeclareInt(@sdeep,GetScreenDepth()) DeclareInt(@twide) DeclareInt(@tdeep) DeclareInt(@tbits) DeclareInt(@xpos) DeclareInt(@ypos) DeclareInt(@xdelta,2) DeclareInt(@ydelta,1) DeclareInt(@temp) GetPictureDimensions(@pic,@twide,@tdeep,@tbits) @xpos=Sub(Div(@swide,2),Div(@twide,2)) @ypos=Sub(Div(@sdeep,2),Div(@tdeep,2)) @swide=Sub(@swide,@twide) @sdeep=Sub(@sdeep,@tdeep) Do() DrawPicture(@pic,@xpos,@ypos,1) Sleep(40) @temp=Add(@xpos,@xdelta) If(And(IsLessThan(@temp,@swide),IsGreaterThan(@temp,0)),:xnext) @xdelta=Mul(@xdelta,-1) Inc(@xdelta,Sub(Random(2),2)) If(IsNotEqual(@xdelta,0),:xninc) Inc(@xdelta) :xninc Beep(0) :xnext Inc(@xpos,@xdelta) @temp=Add(@ypos,@ydelta) If(And(IsLessThan(@temp,@sdeep),IsGreaterThan(@temp,0)),:ynext) @ydelta=Mul(@ydelta,-1) Inc(@ydelta,Sub(Random(2),2)) If(IsNotEqual(@ydelta,0),:yninc) Inc(@ydelta) :yninc Beep(0) :ynext Inc(@ypos,@ydelta) While(1) End()

The foregoing animation loop works by repeatedly drawing the same graphic, using multiple calls to DrawPicture. This has two drawbacks – it's somewhat processor intensive, and as such a bit slow if you want to do multiple things in its animation loop, and it only works for single graphics. You can do the same thing by drawing the graphic once and then using SaveScreenArea and RestoreScreenArea or CopyScreenArea to move it around. This variation illustrates the former.

You can use this with slight variation to bounce a block of text or something drawn with calls to the ANIMAL line and shape functions.

;à initialization goes here DrawPicture(@pic,@xpos,@ypos,1) Do() SaveScreenArea(@xpos,@ypos,Add(@xpos,@twide),Add(@ypos,@tdeep)) Sleep(40) @temp=Add(@xpos,@xdelta) If(And(IsLessThan(@temp,@swide),IsGreaterThan(@temp,0)),:xnext) @xdelta=Mul(@xdelta,-1) Inc(@xdelta,Sub(Random(2),2)) If(IsNotEqual(@xdelta,0),:xninc) Inc(@xdelta) :xninc Beep(0) :xnext Inc(@xpos,@xdelta) @temp=Add(@ypos,@ydelta) If(And(IsLessThan(@temp,@sdeep),IsGreaterThan(@temp,0)),:ynext) @ydelta=Mul(@ydelta,-1) Inc(@ydelta,Sub(Random(2),2)) If(IsNotEqual(@ydelta,0),:yninc) Inc(@ydelta) :yninc Beep(0) :ynext Inc(@ypos,@ydelta) RestoreScreenArea(@xpos,@ypos) While(1) End()


Palette Color Grid

The palette grid applet probably won't turn up in any of your screen savers, but it's a useful illustration of the SetDrawingOffset function at work. It draws a chart which illustrates all the colors in the fixed screen saver palette. The SetDrawingOffset function is used to center the action on your screen.

DeclareInt(@x,0) DeclareInt(@y,0) DeclareInt(@size,32) DeclareInt(@colour,0) DeclareInt(@width) DeclareInt(@pad) DeclareString($Number) DeclareInt(@swidth,GetScreenWidth()) DeclareInt(@sdepth,GetScreenDepth()) SetTextTransparent(1) ClearScreen(MakeColor(0,0,0)) SetDrawingOffset(div(sub(@swidth,mul(@size,16)),2),div(sub(@sdepth,mul(@size,16)),2)) Do() @x=0 Do() @colour=add(mul(@y,16),@x) SetBrushColor(GetFixedColor(@colour)) SetPenColor(GetFixedColor(sub(255,@colour)),1) FillRect(mul(@x,@size),mul(@y,@size),add(mul(@x,@size),@size),add(mul(@y,@size),@size)) FrameRect(mul(@x,@size),mul(@y,@size),add(mul(@x,@size),@size),add(mul(@y,@size),@size)) StrFromInt($Number,@colour) @width=GetTextLength($Number) @pad=div(sub(@size,@width),2) SetPosition(add(mul(@x,@size),@pad),add(mul(@y,@size),8)) SetTextForeColor(GetFixedColor(sub(255,@colour))) Print($Number) Inc(@x) While(IsLessThan(@x,16)) Inc(@y) While(IsLessThan(@y,16)) End()


Sierpinski's Curve

The Sierpinski's Curve program, perhaps not surprisingly, generates Sierpinski's curve. While this figure has characteristics which probably interest mathematicians to the point of heavy breathing, it's just engaging to look at for the rest of us. What's perhaps more useful about the Sierpinski's Curve program is that it illustrates "recursion." If you look carefully, you'll find that it contains GoSubs which call themselves. Here's what the curve should look like – it becomes more complex the longer the program is allowed to run.

The depth variable sets the maximum level of complexity for the curve – you might want to experiment with changing this.

DeclareInt(@depth,8) DeclareInt(@level,1) DeclareArray(&n,256) DeclareInt(@n1,0) DeclareInt(@prm0,48) DeclareInt(@prm1,48) DeclareInt(@posit,div(GetScreenDepth(),10)) DeclareInt(@pensize,sub(@depth,1)) SetArrayValue(&n,0,1) SetBrushColor(GetSaverColor()) Do() Dec(@pensize) SetPenColor(MakeColor(255,255,241),max(1,@pensize)) ClearScreen() SetDrawingOffset(div(@posit,2),div(@posit,2)) Gosub(:drawcurve) SetDrawingOffset(0,0) Inc(@level) @posit=div(@posit,2) Sleep(500); While(IsLessThan(@level,@depth)) End() :drawcurve SetArrayValue(&n,@level,@level) Gosub(:left) Line(@prm0,@prm1,add(@prm0,@posit),add(@prm1,@posit)) Inc(@prm0,@posit) Inc(@prm1,@posit) SetArrayValue(&n,@level,@level) Gosub(:top) Line(@prm0,@prm1,add(@prm0,@posit),sub(@prm1,@posit)) Inc(@prm0,@posit) Dec(@prm1,@posit) SetArrayValue(&n,@level,@level) Gosub(:right) Line(@prm0,@prm1,sub(@prm0,@posit),sub(@prm1,@posit)) Dec(@prm0,@posit) Dec(@prm1,@posit) SetArrayValue(&n,@level,@level) Gosub(:bottom) Line(@prm0,@prm1,sub(@prm0,@posit),add(@prm1,@posit)) Dec(@prm0,@posit) Inc(@prm1,@posit) Return() :left @n1=GetArrayValue(&n,@level) If(IsLessThan(@n1,1),:noleft) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:left) Dec(@level) Line(@prm0,@prm1,add(@prm0,@posit),add(@prm1,@posit)) Inc(@prm0,@posit) Inc(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:top) Dec(@level) Line(@prm0,@prm1,@prm0,add(@prm1,mul(@posit,2))) Inc(@prm1,mul(@posit,2)) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:bottom) Dec(@level) Line(@prm0,@prm1,sub(@prm0,@posit),add(@prm1,@posit)) Dec(@prm0,@posit) Inc(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:left) Dec(@level) :noleft Return() :top @n1=GetArrayValue(&n,@level) If(IsLessThan(@n1,1),:notop) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:top) Dec(@level) Line(@prm0,@prm1,add(@prm0,@posit),sub(@prm1,@posit)) Inc(@prm0,@posit) Dec(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:right) Dec(@level) Line(@prm0,@prm1,add(@prm0,mul(@posit,2)),@prm1) Inc(@prm0,mul(@posit,2)) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:left) Dec(@level) Line(@prm0,@prm1,add(@prm0,@posit),add(@prm1,@posit)) Inc(@prm0,@posit) Inc(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:top) Dec(@level) :notop Return() :right @n1=GetArrayValue(&n,@level) If(IsLessThan(@n1,1),:noright) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:right) Dec(@level) Line(@prm0,@prm1,sub(@prm0,@posit),sub(@prm1,@posit)) Dec(@prm0,@posit) Dec(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:bottom) Dec(@level) Line(@prm0,@prm1,@prm0,sub(@prm1,mul(@posit,2))) Dec(@prm1,mul(@posit,2)) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:top) Dec(@level) Line(@prm0,@prm1,add(@prm0,@posit),sub(@prm1,@posit)) Inc(@prm0,@posit) Dec(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:right) Dec(@level) :noright Return() :bottom @n1=GetArrayValue(&n,@level) If(IsLessThan(@n1,1),:nobottom) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:bottom) Dec(@level) Line(@prm0,@prm1,sub(@prm0,@posit),add(@prm1,@posit)) Dec(@prm0,@posit) Inc(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:left) Dec(@level) Line(@prm0,@prm1,sub(@prm0,mul(@posit,2)),@prm1) Dec(@prm0,mul(@posit,2)) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:right) Dec(@level) Line(@prm0,@prm1,sub(@prm0,@posit),sub(@prm1,@posit)) Dec(@prm0,@posit) Dec(@prm1,@posit) @n1=GetArrayValue(&n,@level) Dec(@n1) Inc(@level) SetArrayValue(&n,@level,@n1) Gosub(:bottom) Dec(@level) :nobottom Return() End()




This document and all the other documentation included with Screen Saver Construction Set is copyright © 1995, 1996, 1997, 1998, 1999 Alchemy Mindworks Inc. It may not be reproduced in whole or in part or transmitted in any form save as a component part of the Screen Saver Construction Set software without the explicit written permission of the copyright holder. Unauthorized use of this document or any portion thereof may result in severe criminal and civil penalties. Alchemy Mindworks Inc. accepts no responsibility for any loss, damage or expense caused by your use of the information in this document, however it occurs.