RBONally

Creating "Rubber Band" Graphic Objects

 * //It's Not Just For Lines Anymore!//**

By Tom Nally

This article first appeared in the Liberty BASIC Newsletter Issue #110, July, 2003. It is reprinted here with the author's permission. In this article, all references to Liberty BASIC (LB) can be applied to Just BASIC (JB) as well. The reference and code to changing the cursor from the normal to a crosshair cursor was removed to preserve Just BASIC compatibility.

What is a "Rubber Band Graphic Object"?
"Rubber band objects" can best be described if we refer to the common experiences that we've had using familiar graphics programs.

Take Microsoft Paint, for instance. Open MS Paint, which automatically starts a new paint document. Click on the line tool. Using the line tool, left-click anywhere within the drawing area. While holding the left mouse button down, move the pointer around the drawing area.



Notice how the line that you are creating acts like a rubber band. It's anchored at the point where the left-button-down event occured. As we move the pointer around the drawing area, the line becomes a rubber band, stretching and shrinking between the anchor point and the current position of the mouse pointer. When we release the mouse left button, the line becomes fixed in place.

Even though that example will get everybody acquainted to the rubber band concept, let me add the following wordy definition for "rubber band objects".


 * //A "rubber band object" is any object created within a graphics program which can be defined in relation to two or more points on a plane. Typically, all  of the defining points except one are anchored (umoveable).  The last point will continually move with the motion of the pointer or cursor.  The rubber band object will continually scale itself between the anchored points, and the moveable point, giving it a rubber band character.//**

Most of the time, rubber band objects are defined by only two points. The first one is anchored in the graphics area, while the second point is continually moved by the user until the user completes her drawing operation.

What Kinds of Objects Can Behave Like Rubber Band Objects?
If we only look at MS Paint, we can see three objects that can be made to behave like rubber bands: lines, rectangles and circles. If you look at good CAD programs, however, you may fine 50 to 100 objects that, when drawn, will stretch and scale themselves between the anchor point and the moveable point. In addition to that, CAD programs will often provide entire libraries of pre-defined shapes (an architectural library, for example), with each object being stretchable and scalable.

The reality is this: all the primitive objects (lines, circles, rectangles, arcs) can be given a rubber band character during drawing. Additionally, any complex object made from these primitive objects can also! So, both the number and type of objects that can be rendered in this manner is infinite.

Programming Rubber Band Objects in LB: The Two Challenges
I'm sure there are several different ways to create rubber band objects in Liberty BASIC. Regardless of the number of methods, though, there seems to be at least two programming challenges that all the various methods must meet.

The first challenge is the precise handling of mouse events, such as left button clicks, right button clicks, and mouse movement. The second challenge is the undrawing of the object at the old pointer location followed by the rapid redrawing of the object at the new mouse location. If the programmer can handle both of these challenges, then she can create rubber band objects.

Challenge I: Handling Mouse Events
Why is the handling of mouse events a challenge? Because the same mouse event--a left-button click for instance--may trigger different operations at different times. Here's an example. Say that you are developing a program that draws lines, and you intend for your lines to behave as rubber band objects. A left-mouse click may do any of the three things listed below: So, the program must be coded to understand the context in which a left-mouse click is generated.
 * A left mouse click may do nothing at first, if the line drawing activity has not yet been enabled.
 * If line drawing has been enabled, then the left mouse click might establish the anchor point for the line. Once the anchor point is established, any subsequent mouse movement in the graphic box will cause a rubber band line to be drawn between the anchor point and the moving pointer.
 * If line drawing is enabled, and if the anchor point has been established by the first left-mouse click, then a second left-mouse click may "fix" the line, make it permanent in the graphics area, and stop the rubber band behavior until the user is ready to draw the next line.

Below is shown part of the code from the FoxCircle demo which handles the left-mouse click event. Note the use of two "flags", **FoxIsEnabled** and **FoxFirstClickEstablished**, to track the left-mouse clicks. code format="vb" [Left.Button.Clicked] if (FoxIsEnabled = 1) then if (FoxFirstClickEstablished = 0) then FoxFirstClickEstablished = 1 FoxCornerX = MouseX FoxCornerY = MouseY OldMouseX = MouseX + 0.4 OldMouseY = MouseY + 0.4 else FoxFirstClickEstablished = 0 end if end if code The first thing to notice about the handler is that no values are set unless the Fox drawing routine is enabled. Until fox-drawing is enabled, a left-mouse click event will create no fox images in the graphic box.

Second, **//if//** fox-drawing is enabled, the handler determines if a left-mouse click has already occured by evaluating the **FoxFirstClickEstablished** flag. If prior to entry of the handler that flag is currently zero, then the user intends for the left-mouse click to be the anchor point for the fox. So, the **FoxFirstClickEstablished** flag will be set to one. Unless this flag holds a value of 1, no rubber banding will be authorized within the **//mousemove//** handler, as you will see later.

After the **FoxFirstClickEstablished** flag is set to one, the handler will capture the coordinates of the **//anchor point//** of the fox to be drawn. This step is critical, because every single undraw and redraw of the fox is connected to this anchor point. The two coordinates of the anchor point have been given the variable names **FoxCornerX** and **FoxCornerY**.

On the other hand, if the **FoxFirstClickEstablished** flag is already set to 1 upon entry of the handler, that means that fox-drawing is already active, and a left-mouse click indicates the user's intent for the drawing operation to finish. So, the **FoxFirstClickEstablished** flag will be set to zero thereby stopping the rubber banding operations until the **//next//** left-mouse click begins the process again.

Now that left-mouse clicks are properly handled, here is what is happening over at the **//mousemove//** handler: code format="vb" [MouseChange1] If ((FoxIsEnabled = 1) and (FoxFirstClickEstablished = 1)) then QQQ = DrawTheFox(FoxCornerX, FoxCornerY, OldMouseX, OldMouseY) QQQ = DrawTheFox(FoxCornerX, FoxCornerY, MouseX, MouseY) OldMouseX = MouseX OldMouseY = MouseY end if code Notice that no drawing takes place at all unless and until both the **FoxIsEnabled** flag and the **FoxFirstClickEstablished** flag are both set to one. If both flags are set to 1, then drawing the fox occurs **//twice//**. In the **//first//** draw, the fox is actually //"undrawn"// between the anchor point and the //"old"// mouse location. In the **//second//** draw, the fox is drawn anew between the anchor point and the current mouse location.

Ahhh, the perfect seguey to the second challenge...

Challenge II: Undrawing and Redrawing the Rubber Band Object

 * //The XOR Rule Enables "Undrawing" of Graphics**//: Undrawing the object at the old mouse location, followed by redrawing at the new, is the lesser of the two challenges in my opinion. The reason for the relative ease is the XOR rule: **//if "rule XOR" is in effect during drawing, any object redrawn exactly on top of itself will disappear.//**  This capability makes //"undrawing"// the object at the //"old"// pointer location quite easy, provided that the programmer carefully tracks the old pointer location.

The **XOR** rule is a command sent to a //**GRAPHICBOX**// control. Here is what the code line looks like in **FoxCircle**: code format="vb" print #FoxCirc.GBox1, "rule XOR" code

Tracking Old and New Mouse Coordinates
As indicated earlier, the rubber band effect is created by "undrawing" the object between it's anchor point and the old cursor location, and rapidly "redrawing" the object between the anchor point and the new cursor location. So, tracking the cursor is key to creating a successful rubber band object.

Liberty BASIC makes tracking the GraphicBox coordinates of the cursor easy. Carl Gundel has reserved the variable names ** MouseX** and **MouseY** as holders of the current cursor coordinates in the active GraphicBox. The values of **MouseX** and **MouseY** are continually updated by the system without any additional coding provided by the user.

Of course, with every movement of the cursor, you not only need to know the current location of the mouse, but the last location of the mouse, too. This is because a rubber band object needs to be "undrawn" from its last location before it can be "redrawn" at it's current location.

Logically, the //"current"// location of the mouse converts into the //"old"// location of the mouse during the next cycle. So, all that needs to be done to capture the //"old"// location of the mouse is the following: code format="vb" OldMouseX = MouseX OldMouseY = MouseY code

Then, the next time through the cycle, the last position of the mouse has been preserved in the variables **OldMouseX** and **OldMouseY**, while **MouseX** and **MouseY** will take on new values with the movement of the cursor.

(As an aside, my experience is that **MouseX** and **MouseY** are //"case sensitive"//, like user-defined variables. Perhaps anyone with a different experience can speak up about it.)

Call the Drawing Routine With a Sub or Function
In the first challenge, we discussed the importance of handling mouse events correctly. In the second challenge so far, we've discussed the importance of **//"rule XOR"//**, and how to track the old and new locations of the cursor. Now it's time to discuss how you can efficiently draw the rubber band object with each new movement of the mouse.

To begin that discussion, recall a statement that I wrote in the first section of this article: **//"Most of the time, rubber band objects are defined by only two points. The first one is anchored in the graphics area, while the second point is continually moved by the user until the user completes her drawing operation."//**

Since a rubber band object can be completely defined by referencing only two points in the GraphicBox, and since each point in the GraphicBox has two coordinates, then the programmer should be able to write an object-drawing sub or function which only needs four arguements: the X and Y coordinates of the anchor point, and the X and Y coordinates of the moving point. Indeed, that is what you see in the code shown earlier and repeated below: code format="vb" QQQ = DrawTheFox(FoxCornerX, FoxCornerY, OldMouseX, OldMouseY) QQQ = DrawTheFox(FoxCornerX, FoxCornerY, MouseX, MouseY) code The **//SUBs//** or **//Functions//** themselves might be quite simple, as in the case when the rubber band object is a line. On other occasions, the **//SUB//** or **//Function//** might be complex. As an example of the latter, the **DrawTheFox** function requires over 200 lines by itself, counting blank lines and comments. code - - - code Regardless of the complexity of the **//SUB//** or **//Function//** however, //calling// the drawing routines should only require four arguements, making the operation very simple and efficient in the **//mousemove//** handler.
 * SIDEBAR: What's the deal with the    |
 * use of functions to draw graphics?   |
 * If you look at the code excerpts     |
 * above, or study the source code in   |
 * FoxCircle and The Cloud Tool, you    |
 * will notice that I am using Functions |
 * to draw graphic objects. Typically,  |
 * Liberty BASIC Functions are used to  |
 * return values, not draw.             |
 * I have a good reason for this: I'm   |
 * lazy.                                |
 * Actually, about 18 months ago, I     |
 * needed a routine which would draw    |
 * graphics after receiving 6 to 8      |
 * arguements. The conventional LB      |
 * tool to use for this would be the    |
 * Libert BASIC SUB. However, I had     |
 * never used a SUB, and found that     |
 * LB Functions would also do the job.  |
 * Since Functions have not let me      |
 * down yet, I continue to use them for |
 * graphics from time to time.          |
 * But kids, don't try this at home.    |
 * down yet, I continue to use them for |
 * graphics from time to time.          |
 * But kids, don't try this at home.    |
 * But kids, don't try this at home.    |

The Demo Programs: FoxCircle, and The Cloud Tool
Feel free to download and unzip the two demo programs included with this article.
 * [[Image:RBOPic02.jpg]]||....................||[[Image:RBOPic03.jpg]]||
 * FoxCircleJB.zip]]||


 * FoxCircle** allows you to draw, well, foxes and circles. I chose these two objects for this micro-application to demonstrate that both simple objects (circles) and complex objects (foxes) lend themselves to be rendered as "rubber band objects".  In FoxCircle, a mouse-left click is used both to start drawing an object and to finish drawing the object.

More developed than **Fox Circle**, **The Cloud Tool** allows the user to string together systems of rubber band arcs in order to make clouds. I built that program in order to imitate the cloud-drawing tools that one finds in the nicer CAD programs, such as AutoCad. To start drawing a cloud, left-click the mouse. Each new billow of a cloud is also formed by left-clicking the mouse. To complete the cloud, right-click the mouse.


 * The Cloud Tool** comes with loading, saving and printing functions, as well as an options dialog box.

--- Tom Nally Steelweaver52@aol.com

--- Remember that all references to Liberty BASIC (LB) pertain equally as well to Just BASIC (JB). There is one minor modification in FoxCircleJB.bas, that modification being 5 lines of code designed to change the cursor from normal to crosshair and back were rem'd out. CloudToolJB.bas also deviates from the original CloudTool.bas. CloudTool.bas modifications include rem'd out code for (1) changing cursor from normal to crosshair and back, (2) button tooltips, (3) colordialog, (4) shellExecuteA API call for printing the document. None of the disabled code prevents the user from learning how to code a "Rubber Band Object". Just BASIC users should consider inserting John Davidson's [|J-ColorPicker] available at [|John's Just BASIC Page] in lieu of the unavailable Colordialog command.

--- Back to Nally's Code