============================================================================== DRAW CIRCLE LIBRARY User's Guide version 1.0 Last Updated on August 8th, 2023 ------------------------------------------------------------------------------ Created by James Pujals, a.k.a. "DZ-Jay" - ============================================================================== ============================================================================== Table of Contents ============================================================================== Section 1. Introduction 1.1 A Brief History 1.2 Acknowledgements 1.3 The Draw Circle Library 1.4 Demo Program Overview Section 2. General Library Concepts 2.1 Circle Drawing Basics 2.2 Block Type 2.3 Drawing Region 2.4 Block Data Source 2.5 Drawing Styles & Options 2.6 Known Limitations Section 3. Introducing "DrawCircle" 3.1 Procedure Overview 3.2 Drawing Algorithm 3.3 Variables & Other Requirements 3.4 Circle Parameters 3.5 Constants & Functions 3.6 Examples Section 4. Introducing The Demo Program 4.1 Operating Modes 4.2 Test Mode 4.3 Visual Effects Mode 4.4 The Effects Bestiary ============================================================================== Section 1: Introduction ============================================================================== ----------------------- 1.1: A Brief History ----------------------- During the development of my first game, "Christmas Carol vs. The Ghost Of Christmas Presents," I wanted to add a visual effect that simulated the way cameras zoom in on a subject for dramatic effect. The idea consisted of a "closing circle" effect, in which concentric circles were drawn smaller and smaller, painting the screen with a solid color and obscuring anything in their path, until the only thing visible was a small sprite at the center of the screen. I thought that such a visual effect, along with a melodramatic musical chord for extra punch, would give the impression of a "dramatic close up," similar to the ones utilized in some movies. There was just one problem, I had no idea how to draw a circle programmatically, and I suck at maths and geometry. After a brief online search, I found a simple implementation of a circle drawing algorithm, written in C, that looked to be small, efficient, and effective for my purposes. I proceeded to translate it into CP-1610 Assembly Language in order to integrate it into my game program. I thought the resulting routine was useful and versatile, so I posted it to the AtariAge "Intellivision Programming" forum for others to use. It eventually made its way to a few games, in which it was utilized to draw animated explosions and other effects. Almost ten years later, when working on a new game, I had a similar need for a circle drawing utility to add animated transitions and other visual effects. Because my new game is written entirely in IntyBASIC, I decided that, rather than port my previous Assembly code, I should go back to the original C source and translate it to IntyBASIC directly. In doing so, I expanded upon the original routine, adding support for all display modes, and a host of new configurable features. After adding full documentation and a demonstration program that doubles as a testing platform, the result is the new, full-featured "Draw Circle Library" for IntyBASIC games. It is my hope that this library proves as useful to others as it is to me. ------------------------ 1.2: Acknowledgements ------------------------ First and foremost, I would like to extend my gratitude and appreciation to Herr Alois Zingl for his circle rasterization algorithm; and for so graciously acceding to my request to use, re-purpose, and re-publish his work. Second, many thanks go to Dave Akers, for coming up with the idea of putting together my circle drawing routine and Joe Zbiciak's Colored Squares plotting library. It was Mr. Akers' implementation of that combination for the game "Paddle Party" that eventually lead to this library. Of course, I also wish to thank Joe Zbiciak for his guidance, for his great help during the years, and for his prolific documentation on the technical capabilities of the Mattel Intellivision® -- from which most of my knowledge and experience stems. His Colored Squares plotting library also informed and inspired my own IntyBASIC implementation of the same, used in the "Draw Circle" library. Finally, I would be remiss if I failed to acknowledge Óscar Toledo and his wonderful effort to keep our programming community alive -- the IntyBASIC compiler has enabled a new Golden Age of Intellivision® games that may even surpass the original canon from the 1980s. ------------------------------- 1.3: The Draw Circle Library ------------------------------- "Draw Circle" is an IntyBASIC software library that applies a variant of Bresenham's Line Algorithm, itself derived from another called the "Mid- Point Circle Algorithm." It includes the core "DrawCircle" procedure as well as various support subroutines, macros, and constants. The library supports all Intellivision® display modes and offers a number of configuration parameters and options that allow for a wide variety of drawing styles and effects. Library Contents ---------------- The entire "Draw Circle" library is contained within the "lib" folder, and includes the following files: lib Library folder |-- draw-circle.bas - Contains main drawing routine. |-- plot-const.bas - Defines common drawing constants. |-- plot-macro.bas - Defines common utility functions. `-- plot.bas - Contains support routines for plotting. The "plot-macro.bas" module may be of interest, for it includes a number of utility functions that may be useful in any program, such as some for determining if a value is within a given range, formatting card data, converting coordinates to background addresses in all screen modes, etc. ----------------------------- 1.4: Demo Program Overview ----------------------------- The demonstration program is intended to illustrate how to use the "Draw Circle" library, and to showcase its capabilities. It does this by implementing a number of visual effects of varying complexity, each one showing off a particular aspect of the library. The program also offers a manual testing mode that allows the user to draw circles of various styles by changing drawing parameters. Note that the demo program was cobbled together quickly, and although it follows sound engineering practices and is well structured and commented, it was done crudely, mainly as a means to an end. A lot of work was put into the visual effects themselves, but the infrastructure that handles the menu, input handling, and other engine functionality remains very rudimentary. Demo Program Contents --------------------- The demonstration program encompasses the main project source file, as well as all modules within the "demo" folder. The program's files are listed below: project Project folder |-- circle.bas - Main demo module, includes all others. |-- title.bas - SDK module to draw the title screen. `-- demo - Demo folder |-- const.bas - Defines common demo constants. |-- data.bas - Contains all data used by the demo. |-- macro.bas - Defines common utility functions. |-- util.bas - Contains common support subroutines. |-- vfx-1.bas - Contains subroutine for effect #1. |-- vfx-2.bas - Contains subroutine for effect #2. |-- vfx-3.bas - Contains subroutine for effect #3. |-- vfx-4.bas - Contains subroutine for effect #4. |-- vfx-5.bas - Contains subroutine for effect #5. `-- vfx-6.bas - Contains subroutine for effect #6. The "util.bas" module may be of particular interest, for it includes a number of utility subroutines that may be useful in any program, such as for handling user input, data entry, delay loops, etc. Feel free to persue the file -- all subroutines are extensively commented with full descriptions of their operation and functionality. ============================================================================== Section 2: General Library Concepts ============================================================================== ----------------------------- 2.1: Circle Drawing Basics ----------------------------- Circles are drawn on the screen by plotting background cards at positions corresponding to the circumference of a circle. Target positions are computed using a variation of Bresenham's line drawing algorithm applied to circles, and are based on various parameters that indicate the origin and radius of the circle to draw. Additionally, the drawing routine supports various options that allow for a number of interesting drawing styles. This section describes some important concepts put forward by the "Draw Circle" library. The terms and concepts introduced here are used in the rest of this guide, and will help in understanding the various drawing configuration options available. ------------------ 2.2: Block Type ------------------ In the context of the "Draw Circle" library, a "block" is the drawing element, or "brush," used to paint the circle on the screen. The library supports two types of blocks, "Card" and "Bloxel." Each block type corresponds to a specific drawing resolution, therefore all circle dimensions, coordinates, and related parameters are measured in units of the block type in use. Card ---- The "Card" block type uses a normal background card of 8 x 8 pixels to draw circles. Although such circles will appear very "blocky," "Card" block type is the more versatile of the two, for it supports both Intellivision® display modes, "Color Stack" and "Background/Foreground." "Card" block type also supports drawing using pictures from GROM or GRAM, a feature which can be exploited for additional artistic or stylistic effect. Bloxel ------ The "Bloxel" block type utilizes a special feature of Intellivision®'s Color Stack display mode called "Colored Squares," to draw circles of a slightly higher resolution than with the "Card" block type. Colored Squares mode treats each background card as four blocks of 4 x 4 pixels, called "bloxels" (for "blocky pixels"), each with its own color. Using "Bloxel" block type allows for smoother looking circles, but its mandatory use of Color Stack mode may not be suitable for all programs. It also limits the color of bloxels to the first seven of the primary set, plus the background color, further reducing its utility. Nevertheless, Colored Squares bloxels can be intermixed with regular GROM or GRAM background cards (at card boundaries, of course), which can allow for some interesting circle effects on top of a graphical scene. See the description of the "Slide Show" visual effect in section 4.4 "The Effects Bestiary," for an example of this technique in practice. ---------------------- 2.3: Drawing Region ---------------------- Circles can be drawn at any size, up to a radius of 255 blocks -- though in practice, the effective size is constrained to a pre-defined drawing region. Any blocks that fall outside the drawing region are automatically clipped. The library supports two region configurations, "Full Screen" and "Box." Full Screen ----------- In "Full Screen" the drawing region is the entire screen -- that is, either a grid of 20 x 12 background cards, or 40 x 24 color bloxels, depending on the block type used. When using "Full Screen" region type, all coordinates are relative to the origin of the screen (column zero, row zero -- its top-left corner). Box --- The "Box" region type constrains the drawing area to a user-defined rectangular window within the screen boundaries -- the so-called "box." When using "Box" region type, all coordinates are relative to the origin of the region box (coordinates X = 0, Y = 0 -- the top-left corner of the region rectangle). ------------------------- 2.4: Block Data Source ------------------------- When drawing a circle, each block plotted on the screen is a background card, whose actual data can be obtained from two possible sources: either a statically defined color format word, or a screen buffer element. Format ------ With "Format" data source, the background card is a fully composed 16-bit data word comprising picture and color information, according to the "Block" type in use. The same data word will be use for every block plotted on the screen. Note that the format data word must conform to the active display mode. In the case of Colored Squares, the data word is a value from zero to seven representing the primary and background colors. Buffer ------ With "Buffer" data source, each background card is drawn by copying corresponding elements from a data buffer to the screen. The buffer represents a full or partial background scene, depending on the "Region" type in use, and can include any screen data appropriate to the selected "Block" type. The buffer itself can be defined dynamically in an array variable, or stored as ROM data. Note that the drawing routine will correlate positions on the screen to corresponding buffer offsets, so the dimensions and layout of the screen buffer must match those of the selected drawing region. -------------------------------- 2.5: Drawing Styles & Options -------------------------------- The "Drawing Style" determines how circles are drawn on the screen, and is a combination of the "Block" type, the "Region" type, and the "Data Source." Additionally, drawing can be controlled further by the use of optional modifiers that influence the style. These are described below. Color Stack Option ------------------ When using Intellivision®'s "Color Stack" display mode, it may not be desirable to alter stack assigments (i.e., the usage of the "Color Stack Advance" bit) present in the current background scene. Doing so may distort or corrupt the colors of the scene. To accommodate for this issue, the "Draw Circle" library supports a special feature that preserves the current Color Stack assigments. When enabled, the current state of "Color Stack Advance" bits on the BACKTAB will be left intact as background cards are plotted. Note that this option applies only for drawing styles with a "Card" block type and "Format" data source. It will be ignored for any other style. Double Fill Option ------------------ By default, circles are drawn by plotting one single block for each corresponding point along the circumference computed by the algorithm. This method is perfectly acceptable for most cases, but may not be suitable under some special circumstances, namely when filling a screen area by drawing circles. The problem is caused by a limitation inherent in Bresenham's algorithm and manifests itself when drawing two concentric circles where the radius of one is one block smaller or larger than the other. In such cases, the computed plot points may not be perfectly contiguous, leaving gaps in between the two circles. Depending on the desired effect, these gaps may present a problem, as illustrated in the examples below. Example #1: Drawing circles with default fill option. Legend: . Screen card boundaries * Plotted block (circle #1) + Plotted block (circle #2) % Gap X Origin Circle #1 with radius = 4: Circle #2 with radius = 5: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .+.+.+. . . . . . . . . . . .*.*.*. . . . . . . . . .+.+. . . .+.+. . . . . . . . .*. . . .*. . . . . . . .+. . . . . . . .+. . . . . . .*. . . . . .*. . . . . . .+. . . . . . . .#. . . . . .*. . . . . . . .*. . . . .+. . . . . . . . . .+. . . . .*. . . .X. . . .*. . . . .+. . . . .X. . . . .+. . . . .*. . . . . . . .*. . . . .+. . . . . . . . . .+. . . . . .*. . . . . .*. . . . . . .+. . . . . . . .+. . . . . . . .*. . . .*. . . . . . . .+. . . . . . . .+. . . . . . . . .*.*.*. . . . . . . . . .+.+. . . .+.+. . . . . . . . . . . . . . . . . . . . . . . .+.+.+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Both circles drawn concentrically: . . . . . . . . . . . . . . . . . . . .+.+.+. . . . . . . . . .+.+.*.*.*.+.+. . . . . . .+.%.*. . . .*.%.+. . . . . .+.*. . . . . .*.+. . . . .+.*. . . . . . . .*.+. . . .+.*. . . .X. . . .*.+. . . .+.*. . . . . . . .*.+. . . . .+.*. . . . . .*.+. . . . . .+.%.*. . . .*.%.+. . . . . . .+.+.*.*.*.+.+. . . . . . . . . .+.+.+. . . . . . . . . . . . . . . . . . . . Notice that there are four gaps where the two contiguous circles fail to touch. This is not an error, but a side-effect of the drawing algorithm itself. To mitigate this potential problem, the "Draw Circle" library supports an option to "Double Fill" each plotted point -- that is, drawing two adjecent blocks instead of one for each point. This method is guaranteed to prevent such gaps at the cost of making the resulting circles slightly wider and their circumference thicker. In most cases, this distortion is tolerable and provides a simple and effective solution to the inherent gap problem. Example #2: Drawing circles with "Double Fill" option. Legend: . Screen card boundaries * Plotted block (circle #1) + Plotted block (circle #2) X Origin Circle #1 with radius = 4: Circle #2 with radius = 5: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .+.+.+.+. . . . . . . . . . .*.*.*.*. . . . . . . . .+.+.+. . .+.+.+. . . . . . . .*.*. . .*.*. . . . . . .+.+. . . . . . .+.+. . . . . .*.*. . . . .*.*. . . . . .+.+. . . . . . .+.+. . . . .*.*. . . . . . .*.*. . . .+.+. . . . . . . . .+.+. . . .*.*. . .X. . . .*.*. . . .+.+. . . .X. . . . .+.+. . . .*.*. . . . . . .*.*. . . .+.+. . . . . . . . .+.+. . . . .*.*. . . . .*.*. . . . . .+.+. . . . . . .+.+. . . . . . .*.*. . .*.*. . . . . . .+.+. . . . . . .+.+. . . . . . . .*.*.*.*. . . . . . . . .+.+.+. . .+.+.+. . . . . . . . . . . . . . . . . . . . . . .+.+.+.+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Both circles drawn concentrically: . . . . . . . . . . . . . . . . . . . .+.+.+.+. . . . . . . . .+.+.+.*.*.+.+.+. . . . . .+.+.*.*. . .*.*.+.+. . . . .+.+.*. . . . .*.+.+. . . .+.+.*. . . . . . .*.+.+. . .+.+.*. . .X. . . .*.+.+. . .+.+.*. . . . . . .*.+.+. . . .+.+.*. . . . .*.+.+. . . . .+.+.*.*. . .*.*.+.+. . . . . .+.+.+.*.*.+.+.+. . . . . . . . .+.+.+.+. . . . . . . . . . . . . . . . . . . Notice that the gaps from the previous example are completely filled, and that the circles overlap slightly. The accompanying demo program offers a number of examples that illustrate how to exploit the "Double Fill" option to create some interesting visual effects. ------------------------- 2.6: Known Limitations ------------------------- "Draw Circle" is a very versatile library and its many features allow for a variety of uses in many types of games. Nonetheless, its functionality is limited by its implementation, and ultimately, by the platform itself. Restrictions ------------ The most obvious of these limitations are the color, memory, and picture restrictions imposed by the Intellivision® graphics processor and its related hardware. Certainly, the display mode used by the host program will necessarily limit the capabilities of the circle drawing routine. For instance, selecting "Foreground/Background" mode precludes the usage of the "Bloxel" block type. Conversely, a mode of "Color Stack" will inevitably constrain the available color choices used for drawing. Moreover, drawing styles apply to a full circle -- mixing block types, formats, sources, etc.; within the same circle, is not supported. Performance ----------- Nevertheless, the biggest limitation is perhaps that of performance. The library is written entirely in IntyBASIC and therefore, its execution speed is determined by the IntyBASIC runtime engine, the efficacy of the compiler, and the performance of the resulting machine code. It is that last one which is of most significance. Because there is no way to access CPU registers directly from IntyBASIC, computations tend to require the use of temporary variables for transient values, which by their nature impose a penalty incurred by direct memory access. This problem is compounded when such complex computations need to be performed repeatedly, as is the case for the circle plotting routines. Worse still, larger circles require more blocks to be plotted, which may result in a perceivable slowdown, especially when drawn repeatedly at different radii. All that notwithstanding, there is a bright side to all this: It turns out that for most cases the performance of the circle drawing routine is quite suitable -- perfectly adequate, in fact. A great number of cool and visually striking effects can be created in spite of it. In case of any doubt, I expect the included demo program to illustrate this very point with great aplomb. :) ============================================================================== Section 3: Introducing "DrawCircle" ============================================================================== ------------------------- 3.1 Procedure Overview ------------------------- At the heart of the "Draw Circle" library is the "DrawCircle" procedure. As the name suggests, the routine draws a circle on the screen by painting background cards around its circumference according to a given drawing style and other parameters. These parameters, described in detail in the subsequent sections, give the user full control over the drawing behaviour. ------------------------ 3.2 Drawing Algorithm ------------------------ The algorithm used by "DrawCircle" is a variant of Bresenham's Line Algorithm, itself derived from the "Mid-Point Circle Algorithm." This version was adapted to IntyBASIC from a compact implementation in C found at the following location: http://members.chello.at/easyfilter/bresenham.html Prior Art --------- The page and code are attributed to, and wholly owned by Alois Zingl from Vienna, Austria. A detailed description of the algorithm and additional code examples are available in the paper entitled, "A Rasterizing Algorithm For Drawing Curves," by Alois Zingl, published on 8 November, 2012. A PDF copy of the paper can be download from Herr Zingl's web site at the following location: http://members.chello.at/%7Eeasyfilter/Bresenham.pdf Original C Implementation ------------------------- Alois Zingl's original implementation of his circle algorithm in the C programming language, is listed below: void plotCircle(int xm, int ym, int r) { int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ do { setPixel(xm-x, ym+y); /* I. Quadrant */ setPixel(xm-y, ym-x); /* II. Quadrant */ setPixel(xm+x, ym-y); /* III. Quadrant */ setPixel(xm+y, ym+x); /* IV. Quadrant */ r = err; if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */ if (r > x || err > y) /* e_xy+e_x > 0 or no 2nd y-step */ err += ++x*2+1; } while (x < 0); Both the Mid-Point and Bresenham's circle algorithms exploit the symmetry of circles by drawing all eight octants of a circle simultaneously, and improve upon other methods by employing only integer arithmetic, making them perfectly suited for computer graphics applications. Additional Optimisations ------------------------ Herr Zingl's implementation extends the algorithm further to address an inherent limitation on both the Mid-Point and Bresenham's algorithms that result in over-plotting spurious pixels at some angles. It also simplifies the function to plot four quandrants simultaneously, rather than the eight octants. Because circles are perfectly symmetrical on a Euclidian plane (that is, each quadrant is a reflection of the others), the algorithm needs only to compute one of them, then plot the rest merely by rotating the resulting points around the origin of the circle. These latter calculations can be very fast, requiring only addition and subtraction to offset the points from the mid-point of each axis. Figure #1: Calculated points for Quadrant #2. Legend: . Screen card boundaries % Points in Quadrant #1 * Rotated points X Origin Quadrant #2 Quandrant #3 (mid - x, mid - y) (mid + x, mid - y) -4 -3 -2 -1 0 +1 +2 +3 +4 +4 . . . . . . . . +4 . . . . . .%%%.***.***. . . . . . +3 . . .%%%.***.***. . . +3 . . . .%%%.%%%.***.***.***. . . . +2 . .%%%. . . .***. . +2 . .%%%.%%%. . . . . . .***.***. . +1 .%%%. . . . . .***. +1 . .%%%. . . . . . . . . . .***. . 0 .%%%. . . X . . .***. 0 . .%%%. . . . . . . . . . .***. . -1 .***. . . . . .***. -1 . .***.***. . . . . . .***.***. . -2 . .***. . . .***. . -2 . . . .***.***.***.***.***. . . . -3 . . .***.***.***. . . -3 . . . . . .***.***.***. . . . . . -4 . . . . . . . . -4 -4 -3 -2 -1 0 +1 +2 +3 +4 Quandrant #1 Quandrant #4 (mid - x, mid + y) (mid + x, mid + y) Figure #2: Simultaneous rotation of points. Legend: . Screen card boundaries % Points in Quadrant #1 X Origin Step #1: Step #2: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .%. . . . . . . . . .%.%. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .%. . . . . . . . . .%. . .X. . .%. . . .%. . .X. . .%. . . . . . . . . . . . . . . . . . . .%. . . . . . . . . . . . . . . . . . . . . . . . . . .%. . . . . . . . .%.%. . . . . . . . . . . . . . . . . . . . . . . . . Step #3: Step #4: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .%.%. . . . . . . .%.%.%. . . . . . .%. . . .%. . . . . .%. . . .%. . . . .%. . . . . . . . . .%. . . . . .%. . . .%. . .X. . .%. . . .%. . .X. . .%. . . . . . . . . .%. . . .%. . . . . .%. . . . .%. . . .%. . . . . .%. . . .%. . . . . . .%.%. . . . . . . . .%.%.%. . . . . . . . . . . . . . . . . . . . . . . . IntyBASIC Version ----------------- The IntyBASIC version is a direct translation from the C implementation. The only original art introduced is this edition is the implementation of the "setPixel()" function, which in the "Draw Circle" library is carried out by eight special-purpose routines, each dedicated to supporting a specific drawing style. The plotting routines are listed below with their corresponding drawing styles: Procedure Block Region Source --------------------------------------------------------- DoPlotScreenCard Card Full Format DoPlotScreenCardBox Card Box Format DoPlotCsqBloxel Bloxel Full Format DoPlotCsqBloxelBox Bloxel Box Format DoPlotBufferScreenCard Card Full Buffer DoPlotBufferScreenCardBox Card Box Buffer DoPlotBufferCsqBloxel Bloxel Full Buffer DoPlotBufferCsqBloxelBox Bloxel Box Buffer All procedure code is fully documented with extensive comments that describe what they do and how they work. For more information, see the "plot.bas" library module. ------------------------------------- 3.3 Variables & Other Requirements ------------------------------------- The "DrawCircle" procedure and its support routines depend on a number of variables that must be defined by the host program. These are utilized as input parameters or as internal temporary storage when performing calculations. All variables required by the library are listed below. Parameter Variables ------------------- Variable Storage Type Description ----------------------------------------------------------------- OrigX 8-Bit X position of circle origin OrigY 8-Bit Y position of circle origin PlotX 8-Bit X position for plotting PlotY 8-Bit Y position for plotting Radius 8-Bit Circle radius DrawStyle 8-Bit Circle drawing style DrawOpts 8-Bit Additional drawing options BoxOrigX 8-Bit X position of region origin BoxOrigY 8-Bit Y position of region origin BoxWidth 8-Bit Width of region box BoxHeight 8-Bit Height of region box #PlotFormat 16-Bit, unsigned Card format word for drawing #PlotBuffer 16-Bit, unsigned Pointer to source buffer Temporary Variables ------------------- Variable Storage Type Description ----------------------------------------------------------------- #TempRad 16-Bit, signed Rotation radius #TempErr 16-Bit, signed Error accumulator #TempPosX 16-Bit, signed X coordinate to plot #TempPosY 16-Bit, signed Y coordinate to plot #TempMask 16-Bit, unsigned Colored Squares color mask #TempCard 16-Bit, unsigned Card format word for drawing #TempAddrs 16-Bit, unsigned Target address to plot #TempBuffer 16-Bit, unsigned Pointer to buffer element Library Modules --------------- In addition to variable requirements, "DrawCircle" depends on a number of modules included in the library. These can be divided into two classes: definition and subroutine. Definition modules include all constant and function definitions needed by "DrawCircle." These should be included at the top of your program primary source file, before including any of the subroutine modules, and prior to any calls to "DrawCircle." Subroutine modules include all support routines on which the "DrawCircle" procedure depends. These can be included anywhere in your program's primary source file, but should typically go at the end, along with any other code or data modules, and outside the main program flow. Example: A sample main program source file illustrating module use. ' --------------------------------------------- ' FILE: projects/myproject/myproject.bas ' --------------------------------------------- OPTION EXPLICIT ' DEFINITIONS ' --------------- ' Include any definition modules at the ' top of the primary source file. Include "lib/plot-const.bas" Include "lib/plot-macro.bas" ' ... ' --------------- ' ... ' Main program code goes here ... ' ... ' --------------- ' CODE & DATA ' --------------- ' Include any code and data modules at the ' bottom of the primary source file. Include "lib/draw-circle.bas" Include "lib/plot.bas" ' ... ------------------------ 3.4 Circle Parameters ------------------------ "DrawCircle" expects a set of parameter variables that control the drawing behaviour. These parameters can be grouped into two sets: the core set of parameters that describe the drawing style and the basic characteristics of the circle; and any additional parameters that may be required by the drawing style chosen. Core Parameters --------------- The following parameters apply to any circle, and are always required by the "DrawCircle" procedure: DrawStyle The drawing style. This parameters is a combination of the Block, Region, and Data Source flags. It can be configured directly by OR'ing applicable style constants, or by calling the "GetDrawingStyle()" utility function. (See the following section, 3.5 "Constants & Functions.") DrawOpts Additional drawing options. This parameter is a combination of drawing option flags, such as "Double Fill," etc. It is configured directly by OR'ing the applicable constants (See the following section, 3.5 "Constants & Functions."). Radius The circle's radius, or distance from the center to its circumference. This value is an 8-bit positive integer that corresponds to units of the block type. OrigX The X coordinate of the circle's origin, or center point. This value is an 8-bit positive integer that corresponds to units of the block type. OrigY The Y coordinate of the circle's origin, or center point. This value is an 8-bit positive integer that corresponds to units of the block type. Additional Parameters --------------------- Depending on the drawing style, the following parameters may apply: BoxOrigX The X coordinate of the drawing region's origin, or its top-left corner. This value is an 8-bit positive integer that corresponds to units of the block type. Applies only when the drawing region is set to "Box." BoxOrigY The Y coordinate of the drawing region's origin, or its top-left corner. This value is an 8-bit positive integer that corresponds to units of the block type. Applies only when the drawing region is set to "Box." BoxWidth The width of the drawing region. This value is an 8-bit positive integer that corresponds to units of the block type. Applies only when the drawing region is set to "Box." BoxHeight The height of the drawing region. This value is an 8-bit positive integer that corresponds to units of the block type. Applies only when the drawing region is set to "Box." #PlotFormat The background card format data word to use for drawing the circle. It can be any valid BACKTAB data word for the active display mode. Applies only when the block data source is set to "Format." #PlotBuffer A pointer to the screen data buffer. It can point to either an array variable or a data table in ROM. The buffer data must be of the same size as the drawing region, and laid out accordingly. Applies only when the block data source is set to "Buffer." ---------------------------- 3.5 Constants & Functions ---------------------------- The "Draw Circle" library defines a number of constants to represent the various drawing styles and options supported. The most important ones are listed below. Constant Description ------------------------------------------------------------------ DRAW_BLOCK_CARD Sets block type to "Card." DRAW_BLOCK_BLOXEL Sets block type to "Bloxel." DRAW_REGION_FULL Sets region type to "Full Screen." DRAW_REGION_BOX Sets region type to "Box." DRAW_SOURCE_FORMAT Sets block data source to "Format." DRAW_SOURCE_BUFFER Sets block data source to "Buffer." DRAW_OPT_NONE No additional drawing options. DRAW_OPT_DOUBLE_FILL Enables "Double Fill" drawing option. DRAW_OPT_COLOR_STACK Enables "Color Stack" persistence option. Example: // Set the drawing style to "Card," "Full Screen," "Format." DrawStyle = (DRAW_BLOCK_CARD \ Or DRAW_REGION_FULL \ Or DRAW_SOURCE_FORMAT) // Enable "double-fill" and Color Stack persistence options. DrawOpts = (DRAW_OPT_DOUBLE_FILL Or DRAW_OPT_COLOR_STACK) For convenience, the following constants are also defined to represent all valid combinations of drawing styles: Constant Block Region Source ------------------------------------------------------------------ DRAW_SCREEN_CARD Card Full Format DRAW_SCREEN_CARD_BOX Card Box Format DRAW_CSQ_BLOXEL Bloxel Full Format DRAW_CSQ_BLOXEL_BOX Bloxel Box Format DRAW_BUFFER_SCREEN_CARD Card Full Buffer DRAW_BUFFER_SCREEN_CARD_BOX Card Box Buffer DRAW_BUFFER_CSQ_BLOXEL Bloxel Full Buffer DRAW_BUFFER_CSQ_BLOXEL_BOX Bloxel Box Buffer Example: // Set the drawing style to "Card," "Full Screen," "Format." DrawStyle = DRAW_SCREEN_CARD Additionally, the library includes various utility functions that encapsulate common computations and other functionality. Most of these are used internally by the plotting routines, but at least one, the "GetDrawingStyle()" function, is intended for general use by the host program. Function: GetDrawingStyle(block, region, source) Description: Returns a drawing style value based on a given set of arguments. The return value can be assigned to the "DrawStyle" parameter variable. Arguments: block Type block type to use, one of the following: DRAW_BLOCK_CARD 8x8 screen cards DRAW_BLOCK_BLOXEL 4x4 bloxels region The type of region to use, one of the following: DRAW_REGION_FULL Full screen DRAW_REGION_BOX Box region source The block data source to use, one of the following: DRAW_SOURCE_FORMAT Formatted card data DRAW_SOURCE_BUFFER Screen buffer Example: // Set the drawing style to "card," "full," "format." DrawStyle = GetDrawingStyle(DRAW_BLOCK_CARD, \ DRAW_REGION_FULL, \ DRAW_SOURCE_FORMAT) --------------- 3.6 Examples --------------- Below are a few examples that illustrate how to use the "DrawCircle" routine for various effects. Example #1 ---------- The following code draws a blue circle centered on the screen, using the GROM solid color card ($5F), in Color Stack mode. The circle has a radius of five background cards. ' We are using Color Stack mode, but FG/BG mode would work ' the same, although the #PlotFormat data word will have ' to be adjusted accordingly. Mode SCREEN_COLOR_STACK Wait ' Set the drawing style: 8x8 Card, Full Screen, Format DrawStyle = GetDrawingStyle(DRAW_BLOCK_CARD, \ DRAW_REGION_FULL, \ DRAW_SOURCE_FORMAT) ' Configure the circle parameters Radius = 5 OrigX = 10 OrigY = 6 #PlotFormat = (($5F * 8) Or CS_BLUE) ' Call the drawing routine Gosub DrawCircle Example #2 ---------- This example illustrates how to animate a growing disc by repeatedly drawing concentric circles. It uses the "Double Fill" drawing option to ensure the concentric circles completely fill the disc. ' We are using Color Stack mode, but FG/BG mode would work ' the same, although the #PlotFormat data word will have ' to be adjusted accordingly. Mode SCREEN_COLOR_STACK Wait ' Set the drawing style: 8x8 Card, Full Screen, Format DrawStyle = GetDrawingStyle(DRAW_BLOCK_CARD, \ DRAW_REGION_FULL, \ DRAW_SOURCE_FORMAT) ' Configure the circle parameters, choosing a ' random screen location for the origin. OrigX = Random(20) OrigY = Random(12) DrawOpts = DRAW_OPT_DOUBLE_FILL #PlotFormat = (($5F * 8) Or CS_BLUE) ' Grow the disc by increasing the radius For Radius = 0 To 5 ' Draw circles repeatedly Gosub DrawCircle Next Example #3 ---------- The following example shows a simple screen "reveal" effect. Concentric circles are drawn, using a screen buffer containing the new scene as the data source. As the radius increases, a larger portion of the scene in the buffer is "revealed." The code below is a simplified version of Visual Effect #5 "Screen Reveal", from the demo program. See section 4.4 "The Effects Bestiary" for a technical description of how this effect works. ' We are using Color Stack mode, but FG/BG mode would work ' the same, provided the buffer data supports it. Mode SCREEN_COLOR_STACK Wait ' Set the drawing style: 8x8 Card, Full Screen, Buffer DrawStyle = GetDrawingStyle(DRAW_BLOCK_CARD, \ DRAW_REGION_FULL, \ DRAW_SOURCE_BUFFER) ' Use "double-fill" to ensure full coverage DrawOpts = DRAW_OPT_DOUBLE_FILL ' The reveal starts from the top-left corner ' of the screen. OrigX = 0 OrigY = 0 ' The source buffer is stored as ROM data. ' Alternatively, it could be an array variable. #PlotBuffer = VarPtr SceneRevealData(0) ' Reveal more of the target scene by repeatedly ' increasing the radius. For Radius = 0 To 24 ' Draw circles repeatedly Gosub DrawCircle Next Example #4 ---------- This example illustrates a simple "expanding ring" effect. It is similar to Example #2, but it draws a second consecutive circle to erase the inner area as the ring expands. It also uses a block type of "Bloxel" to draw using Colored Squares mode. Notice that it adjusts the circle parameters to call "DrawCircle" a second time. The second circle overlaps the one drawn in the previous iteration, effectively "erasing" it by painting it over with the background color. Afterwards, the circle parameters are restored, and the cycle repeats for the next iteration. ' Select Color Stack mode so that Colored Squares ' are available. Mode SCREEN_COLOR_STACK Wait ' Set the drawing style: 4x4 Bloxel, Full Screen, Format DrawStyle = GetDrawingStyle(DRAW_BLOCK_BLOXEL, \ DRAW_REGION_FULL, \ DRAW_SOURCE_FORMAT) ' Configure the circle parameters, choosing a ' random screen location for the origin. OrigX = Random(40) OrigY = Random(24) ' Paint the circle red #PlotFormat = CS_PIX0_RED ' Draw expanding rings ... For Radius = 0 To 24 Gosub DrawCircle ' Draw a second circle inside to erase ' the previous ring. If (Radius > 0) Then ' Reduce the radius, and set the color to the ' background, while saving the current format. Radius = (Radius - 1) #TempFormat = #PlotFormat #PlotFormat = CS_PIX0_BACKGROUND Gosub DrawCircle ' Restore the format and radius #PlotFormat = #TempFormat Radius = (Radius + 1) End If Next For additional usage examples, see the various visual effects routines included in the demo program. They can be found in the "demo" folder included with the library. ============================================================================== Section 4: Introducing The Demo Program ============================================================================== ----------------------- 4.1: Operating Modes ----------------------- The Demo Program offers two main modes of operation, "Test" and "Visual Effects." When engaged, each mode offers a menu of the options it supports. In the case of "Visual Effects" mode, the options correspond to a number of special effects that showcase the "DrawCircle" routine. While in "Test" mode, the options correspond to various circle drawing parameters available to the user. Switching Between Modes ----------------------- Switching between modes is done simply by pressing the zero ("0") key on the hand-controller keypad. When pressed, the screen will be refreshed to show the new mode on the top row, followed by the applicable menu. Main Screen Layout ------------------ The main screen is divided into two areas: - The mode selector - The menu area The mode selector indicates the currently active mode, and is always visible on the top row of the screen; while the applicable options are listed in the menu area below it. In all cases, the key to select each option is presented on the first column, to the left of the option's name. All menu selections are entered via the Intellivision® hand-controller keypad. Figure #3: "Visual Effects" mode menu screen. Option key | V +---------------------------------------+ |0 M O D E : E f f e c t s | <--- Mode Selector +---------------------------------------+ |1 C o l o r B u b b l e s | <-, |2 R i n g B u r s t | | |3 S o n a r P u l s e | | |4 C o l o r T r a n s i t i o n | | |5 S c e n e R e v e a l | > Menu area |6 S l i d e S h o w | | | | | | | | | | <-' +---------------------------------------+ | | +---------------------------------------+ Figure #4: "Test" mode menu screen. Option key | V +---------------------------------------+ |0 M O D E : T e s t | <--- Mode Selector +---------------------------------------+ |1 B l o c k : C a r d | <-, |2 R e g i o n : F u l l | | |3 R a d i u s : 4 | | |4 O r i g i n X : 1 0 | | |5 O r i g i n Y : 6 | > Menu area |6 R e g i o n X : 3 | | |7 R e g i o n Y : 2 | | |8 W i d t h : 1 3 | | |9 H e i g h t : 8 | <-' +---------------------------------------+ | | +---------------------------------------+ ------------------------ 4.2: Test Mode ------------------------ The demo program's "Test" mode allows the user to manually adjust various circle parameters and then draw the resulting circle on the screen. This mode is useful for testing the various drawing styles and other aspects of the "DrawCircle" procedure, in order to get acquainted with it. "Test" mode can also be used to test and validate the functionality of the drawing routine itself, ensuring its proper behaviour after adding any custom modifications. The Parameter Menu ------------------ In "Test" mode, the menu options represent circle parameters that allow the user to control the style, position, and other details of a circle. Each row lists the option key, the parameter name, and its current value, as illustrated below: Figure #5: "Test" mode parameter options. Key Name Current Value | ,_____|_____, ,_____|_____, / \ / \ / \ +---------------------------------------+ |1 B l o c k : C a r d | |2 R e g i o n : F u l l | |3 R a d i u s : 4 | : : . . The options correspond to input parameter variables of the "DrawCircle" procedure. In order to simplify the user interface and operation of the demo program, some input parameters have been omitted. The available parameters are: Key Option Description Variable -------------------------------------------------------------- 1 Block The block type. DrawStyle 2 Region The drawing region type. DrawStyle 3 Radius The circle's radius. Radius 4 Origin X The origin's X coordinate. OrigX 5 Origin Y The origin's Y coordinate. OrigY 6 Region X The region's X coordinate. BoxOrigX 7 Region Y The region's Y coordinate. BoxOrigY 8 Width The region's width. BoxWidth 9 Height The region's height BoxHeight See section 3.4 "Circle Parameters" for more information about these and other available parameters. Entering Data Values -------------------- Changing a parameter value is done by pressing its corresponding key. Some options, namely the ones related to the drawing style, offer a choice between various supported values. Pressing their option key will iterate through the available values, updating the display with the most current configuration. The rest of the parameters accept a numeric integer representing some magnitude, always measured in units of the block type chosen. Pressing the key to one of these numeric options will activate "data entry" mode. Data entry mode is indicated by a blinking cursor, which prompts the user for input, and advances as digits are entered using the keypad. It ends when the user presses either the "Enter" key to commit the entry, or the "Clear" key to cancel it. Up to a maximum of three digits are accepted for any parameter. Also, upon pressing "Enter," the value will be checked against the acceptable range for the given parameter, and rejected if it falls outside it. If the input is either cancelled or rejected, the previous value will be restored automatically. Drawing A Circle ---------------- Once parameters have been adjusted as desired, pressing the "Enter" key at any moment will draw the resulting circle on the screen. Pressing any key, button, or disc direction on the hand-controller will return to the main menu. --------------------------- 4.3: Visual Effects Mode --------------------------- The demo program's "Visual Effects" mode allows the user to execute a number of interesting and visually striking effects produced by creative use of the "DrawCircle" routine. The various effects serve to showcase the capabilities and versatility of the "Draw Circle" library. The Effects Menu ---------------- In "Visual Effects" mode, the menu options represent the various effects available to the user. Each row lists the option key and its corresponding effect name, as illustrated below: Figure #6: "Visual Effects" mode options. Key Effect Name | ,_______________|______________, / \ / \ +---------------------------------------+ |1 C o l o r B u b b l e s | |2 R i n g B u r s t | |3 S o n a r P u l s e | : : . . There are currently six effects available in total. These listed below. For a full description of each effect, see section 4.4 "The Effects Bestiary." Key Option Description --------------------------------------------------------------------- 1 Color Bubbles Draws color bubbles around the screen. 2 Ring Bursts Draws expanding rings around the screen. 3 Sonar Pulse Simulates a sonar or radar pulse display. 4 Color Transition Simulates a "closing circle" transition. 5 Scene Reveal Reveals a hidden scene. 6 Slide Show Transitions between colorful game screens. Executing Effects ----------------- Visual effects are executed by pressing the corresponding option key from the effects menu. Some effects, such as the "Color Bubbles" and "Ring Bursts," will run continuously until interrupted by any hand-controller input. Others, such as the "Color Transition," will play once and wait for the user to press any input on the hand-controller to return to the menu. In any case, any key, button, or disc pressed while the effect is running, will abort it immediately and return to the menu screen. ---------------------------- 4.4: The Effects Bestiary ---------------------------- Below is a detailed description of each visual effect. For a more technical description of effects' implementation, see the code module for each individual effect, which include a great deal of commentary. The modules can be found in the "demo" folder. Color Bubbles ------------- Fills the screen with colorful bubbles that grow to random sizes. How It Works ------------ On each iteration, the routine will randomly choose a position on the screen, a maximum circle radius, and a drawing color. Using the chosen random parameters, the routine will then draw increasingly larger concentric circles at the selected location, starting from a radius of zero, until the maximum is reached. After a brief pause, the process repeats with a new set of random parameters. The end result gives the effect of expanding color bubbles filling the screen. Ring Burst ---------- Fills the screen with colorful rings that grow to random sizes and consume each other. How It Works ------------ On each iteration, the routine will randomly choose a position on the screen, a maximum circle radius, and a drawing color. Using the chosen parameters, the routine will then draw increasingly larger concentric circles at the chosen location, starting from a radius of zero, until the maximum is reached. After a brief pause, the process repeats with a new set of random parameters. As each larger circle is drawn, the previous smaller one is "erased" by re-painting it with the background color. The end result gives the impression of expanding color rings that erase anything underneath as they grow. Sonar Pulse ----------- Simulates a sonar or radar pulse that appears to "ping" on top of the menu screen. How It Works ------------ On each iteration, the routine will randomly choose one of the primary colors, avoiding the background color. It will then draw increasingly larger concentric circles originating at the center of the screen, starting from a radius of zero, until the circle expands beyond the screen boundaries. As each larger circle is drawn, the previous smaller one is "erased" by replacing it with its corresponding background card from a screen buffer that replicates the original menu screen. The end result is intended to simulate the way that "sonar pings" or "radar pulses" are depicted in movies -- that is, as a recurring expanding ring that leaves its contents untouched as it grows. Color Transition ---------------- Simulates a "closing circle" color transition that covers a scene completely. How It Works ------------ First, an initial "scene" is displayed on the screen. After a brief delay, the routine will draw concentric circles of a random color, centered at the screen. The radius of the circle is decreased on each iteration, closing in on the scene until it is entirely ocluded. The end result simulates a "closing circle" color transition, similar to the ones used in old movies. Scene Reveal ------------ Simulates an "opening circle" transition that reveals one scene on top of another. How It Works ------------ The routine clears the screen and initializes the Color Stack with the background colors used by the new scene to be revealed. It then paints the screen black using solid pixel blocks to hide the actual background color. After a short pause, the routine will randomly choose one of eight available positions at the perimeter of the screen, and start drawing increasingly larger concentric circles that originate at the chosen location. The circles themselves are drawn using data from a screen buffer that contains represents the new scene. Each circle block drawn is taken from its corresponding card in the buffer. The end result is a visual effect that reveals a new background scene as the circle expands from the edge of the screen, simulating similar transitions used in old movies. Slide Show ---------- By far the most complex of all effects, it simulates a slide show that cycles through various scenes taken from classic games, using a variety of circle transition effects and techniques. How It Works ------------ The routine first draws a decorative picture frame around the canvas wherein the slides will be presented. This frame serves three main purposes: first, it adds a bit of graphical flair to the effect; second, it hides away Color Stack change boundaries; and third, it demonstrates the "Box" drawing region feature of "DrawCircle," which bounds the circle to a rectangular area, clipping the edges as it draws. Figure #7: The slide show canvas. Xx--==--==--==--==--==--==--==--==--==xX | ^ | || | || | | | || <--- Canvas Frame || | | || || | | || Slide Number || | | | || V || Xx--==--==--==--==|1 |--==--==--==--==xX The frame includes a placeholder for the current slide number. The number is rendered using a sprite in order to overlay it above the frame, centered on the screen. The slides include a variety of screens that utilize different colors, screen modes, and text and graphical elements. At least one slide manipulates the Color Stack considerably. All scenes were taken from classic games, albeit slightly modified to be compatible with the effect. The ultimate effect is a nice and periodic sequence of slides depicting classic game screens, smoothly transitioning from one to the other. The slide show sequence operates as follows: 1. The canvas is first primed with any Color Stack background color transitions required by the new slide. The color information is copied to the screen from the slide data record. Any such changes are hidden using solid pixel blocks from GROM painted with the canvas color. 2. The Color Stack is then updated with the background colors of the new slide, read from the slide data record. Only the 2nd and 4th colors in the Stack -- which correspond to the colors used by the canvas -- are updated. The new background colors remain hidden behind the solid pixel blocks painted in the previous step. 3. The new slide scene is "revealed" using an "opening circle" transition, in which circles of increasingly larger radius are drawn using data read from a screen buffer. 4. Finally, after a brief pause, the canvas is cleared using a "closing circle" transition that paints it with the canvas background color. The slide counter is then advanced and, following another brief pause, the cycle starts again with the next slide. The slide show cycles through these steps for all slides, and repeats the full sequence until the user interrupts it. ==============================================================================