The most common example of the XOR drawing moue is in drawing rubber lines. A rubber line is a line with one of its endpoints fixed on the screen and the other endpoint moving around, following the movement of the pointer. Using rubber lines, the user can verify the final position of a line (and practically every other shape) before committing it to the screen. You have seen this tool in action in just about any drawing application you’ve used, and now you’ll leam how to mcorporate this technique in your applications. To implement a rubber line feature in an application, youmust make use of the three mouse events: MouseDown, MouseMove, and MouseUp. The MouseDowll event signals the starting point of the rubber line. The coordinates of this point must be stored in two Form-wide variables, which we’ll call . XStart and YStart .•Set the DrawMode to 7 (XOR) so that you can continuously erase the previous line.
As long as the pointer is moving, Visual Basic generates MouseMove events. With each MouseMove event, you must erase the previous line and draw a new one from the starting point to the current. point. To be able to erase the old line, you must store the coordinates of the old ending point in two more Form-wide variables, XOld and YO/d. The MouseMove handler must draw two lines, one between the points (XStart, YStart) to (XOld, YOld) and another from the same starting point to the current point. The first line erases the previous rubber line, and the second becomes the current rubber line, which is erased with the next MouseMove event. When the mouse button is released, you must draw the last line in the standard . (COPY_FEN) mode. However, you must first erase the previous line, from (XStart, YStart) to (XOld, YOld), by drawing another line on top of it in XOR mode. Therefore, the event handler must erase the last rubber line, change the drawing mode momentarily to COPY_PEN, and draw a line between the original point (XStari, YStart) and the point where the button is released (X, Y).
VB6 at Work: The Rubber Project
The Rubber application implements the technique just described and lets you draw rubber lines on a Form. Run it and press the left mouse button at the starting point of the line. When you move the mouse around without releasing the button, the second endpoint of the line follows the movement of the mouse. Every time you move the mouse, a new line is drawn between the starting point and the current position of the pointer. This line is called a rubber line because it can swing, shrink, and stretch, as needed, to follow the mouse. It’s as if you had a rubber band attached to the starting point on one end and to the mouse pointer on the other end.
Once you decide on the exact placement of the line, release the mouse button, and the last rubber line is committed on the Form .
The Rubber Application
It’s easy to modify this application so that it draws rubber rectangles or even rubber circles. You’ll see how this technique is used in the context of a drawing application in the next section
VB6 at Work: The Draw Project
The Draw project (see Figure 6.14) demonstrates lust about every method and technique presented in this chapter. It’s a simple application that lets the user draw vanous shapes, such as lines, boxes, and circles, and even display text. All the shapes are drawn in rubber mode. The menus of the application let the user set the drawing parameters, such as the drawing or filling color, the width of the shapes, the style of the lines, and so on. The File menu can tams commands for storing the drawings in BMP files and loading images from disk files (images that were saved earlier with the Draw application or images in MBP format).
The Edit menu contains the usual Copy, Cut, and Paste commands tl)at manipulate rectangular sections of the drawing. The parts of the drawing being copied aren’t transferred to the Clipboard, though. They are saved temporarily on an invisible PictureBox control from which they can be pasted back in the same document (you’ll see shortly why we aren’t using the Clipboard for the Copy and Paste operations), The Width,DrawStyle, and Colors menus contain commands to change the width of the pen, the drawing style and the pen, and fill and background colors, These commands should be icons on a toolbar, but I wanted to focus on the drawing features of the project rather than itt> interface.
Drawing Rubber .Shapes The Draw application’s listing i~ too long to print in its entirety, so we’ll focus on its most important aspects. Let’s start with the code for, drawing rubber shapes, When the user selects the shape to be drawn from the Shape menu, the program sets the Shape global variable to LINE, CIRCLE, BOX, or TEXT. nus variable is used in the Mousefrown, MouseUp, and MouseMove eyent handlers as explained earlier. In the MouseDown event, the program stores the starting coordinates of the rubber shape in the variables Xstart and YStart. These are the coordinates of a line’s first endpoint, a box’s upper-left comer, or a circle’s center.
In the MouseMove event, the program draws the rubber shape by erasing the shape at the old coordinates and then drawing another one at the new coordinates. The Mouselvlove event handler is a Case switch, which erases the previous rubber lines and draws the new one (more on the Case switch later).
The Mousemost events
Unlike drawing standard shapes, printing the text requires a few extra steps. When the user selects Text from the Draw project’s Shape menu, the program prompts for the string to be printed on the Form, It then waits for the user to click the mouse on the Form and move it around. As the pointer moves, the text follows the movement of the pointer and is printed on the Form when the user releases the mouse button.
The Print method isn’t affected by the drawing mode, so you can’t count on the XOR drawing mode to erase the text. The Draw application does the trick by placing the text on a transparent, borderless Label control. As the user moves the pointer around on the Form, the program ‘moves the Label by changing its Left and Top properties. When the mouse is released, the program calls the Print method to print the text on the Form.
The process just described is implemented in the Text command and in the MouseDown and MouseUp events. The Text command of the Shape menu executes the code in Code 6.8.
The Text Command
The text is drawn in the current drawing color and the Draw application doesn’t have a menu option for changing the font, its size, and style. You can easily add this feature to the program, as long as you change the font of both the Label control and the Form. The Label’s text is displayed while the user moves the string around to its final position, where it’s printed in the Form’s font with the Print method.
Using the Copy Command When choosing the Copy or the Cut command the user can select a rectangular area of the drawing with the mouse. A rubber rectangle is drawn around the selection as the user presses the button and moves the mouse. As soon as the user releases the button, the program copies the selected area of the bitmap to a hidden PictureBox control with the PaintPicture method. You can’t use the Clipboard’s $etData method because it doesn’t provide any arguments to let you: specify which part of the image will be copied. This method transfers the entire bitmap to the Clipboard. Thus, to copy part of the image, you must first move it to a “local Clipboard,” which is PictureBox control. It’s the project’s internal clipboard in which bitmaps are stored between Copy’ and Paste operations. The Copy operation is imf>lemented from within the Form’s Mouse events, as follows:
1. In the MouseDown event, the program sets the rectangle’s starting coordinates. It also temporarily resets the DrawWidth property to 1 (pixel).
2. In the MouseMove event, the program draws a rubber rectangle, which encloses the area to be copied.
3. Finally, in the Mousel.Jp event, the rectangular area is transferred to the PictureBox control, with the following statements:
The two If statements ensure th~t the proper segment of the bitmap is copied, even if the user starts the selection from its lower-left comer. The Copy Width and CopyHeight global variables hold the dimensions of the rectangle being copied so that later on, the program will paste or’ this area of the hidden PictureBox control.
Picturel is the name of a hidden container where copied images (or parts of images) are stored with the Copy command and retrieved with the Paste command,
Using the Paste Command The Paste operation is simpler. When the user selects the Paste command, the program waits until the mouse button is pressed and then moves the bitmap to be pasted around, following the movement of the pointer. The pasted bitmap is drawn in XOR mode until the user releases the mouse button, and then the program copies the bitmap in COPY_PEN mode. This process IS also implemented from within the Form’s mouse events. .Moving an image segment around by redrawing it continuously in XOR mode is a slow process. This section of the application will be revised later in the chapfer, in ”Belter Rubber Shapes,” where I will first discuss the AutoRedraw property’ in detail, and then use it to implement a better method of refreshing the Form.
Further Improvements The Draw application is a functional application that you can use as a starting point for many custom applications. Because it can draw on top of bitmaps, you can use it to annotate drawings and graphics such as fax images.
You can also specify the font for printing text and add an Undo feature. To implement an Undo command, temporarily store the drawing in another hidden PictureBox control after each drawing operation. When the qser selects tl-e Undo command, copy this PictureBox control’s contents back on the Form. You can even implement a hierarchical Undo by tracking the drawing commands. To do this, you must set up a structure in which each command wilIbe stored. This structure could be a list, such as the following:
You must encode all the operations and save their descriptions in a list. If the user wants to undo an operation, you can present this list, let the user delete certain entries or change their order, and then clear the Form and execute the commands
in the list. This requires quite a lot of coding, but in principle, it’s’ a straightforward process. ,
A problem with this application is that the rubber shapes can assume strange colors, depending on the values of the underlying pixels. This is a side effect of the XOR drawing mode, which isn’t a problem when you draw shapes over a solid background, but it-becomes a problem when you draw on top of bitmaps. We’ll fix this problem later in the chapter, after we look at the AutoRedraw property and the Refresh method. We’ll also look at the QDraw application, which is practically identical to the Draw application, except it uses a more robust method for drawing rubber shapes.
The Line and Circle methods are sufficient for drawing bar graphs or pie charts, but what if you want to draw interesting, mathematically defined curves like the ones shown in Figures 6.15 and 6.16? To graph curves, you must provide the code that plots every single point of the curve. As you can guess, drawing curves isn’t going to be nearly as quick as drawing lines, but Visual Basic isn’t too slow.
Suppose you want to plot afunction, such as exp(2/t)*sin(2*t), for the values of t from 1 to 10.The • variable is time and it will be mapped to the x-axis, Clearly, you must set up a user-defined scale mode that extends from 1 to 10along the x- axis, But what about the vertical axis? You must either guess or calculate the minimum and maximum values of the function in the r.ange 1 to 10and then set up the scale . To plot the function, you calculate the function at consecutive X values and turn . on the point that corresponds to this value. So, at how many points along the x-axis must you calculate the function? The function must be evaluated at each pixel along the x-axis. If you evaluate the function at more points, some will map to the same pixel. If you evaluate the function at fewer points, gaps will appear between successive points on the graph
Since you’re going to calculate the function at every pixel along the x-axis, you must p1ap the pixels to the corresponding values of the t variable. Let’s assume that the t variable goes from XMin to XMax and that the number of pixels along the x-axis is XPixels. The following loop scans all the pixels along the x-axis and calculates the value of the time variable at each pixel:
The variable tstarts at XMin.’When i reaches XPixels, its maximum value, the variable t becomes XMax. This loop goes through all the pixels along the x-axis and calculates the value of the t variable that corresponds to each pixel-All you have to do now is calculate the function for each value of the time variable. Let’s write a function that calculates the function for any given value of the independent variable:
The function Functlonliuall () accepts as argument the value of the independent variable, calculates the function at the point, and returns the value of the function.
In essence, the loop breaks the plot in as many points as there are pixels along the x-axis, calculates the value of the function at each point, and then turns the corresponding pixel on. Now that you have a technique for mapping the values of the variable t to pixel values, creating the actual plot is straightforward. The last issue to be resolved is the value of the variable XPixels. You can switch the PictureBQx control’s ScaleMode property to 3 (Pixels) temporarily, use the ScaleWidth property to find out the control’s horizontal resolution, and then set up a user-defined coordinate system that’s appropriate for the plot, as follows:
The last step is to figure out the dimensions of the user-defined coordinate system for each function you want to plot. Use a loop similar to the one that produces the plot, only this time, instead of plotting the function, track its minimum and maximum values:
After the completion of the loop, the variables YMin and YMlzfhold the minimum and maximum values of the function in the range XMin to XMaX. To set up the coordinate system for the specific function, you can call the Scale method with the following arguments:
Picture1.Scale (XMin, YMin) – (XMax, YMax)
You’ve seen all the important pieces of the code for plotting functions. Let’s put it all together to build the application shown in Figure 6.17. The application is called Graph and you’ll find it on this chapter’s folder on the CD. The Draw First Function and Draw Second FUnction buttons plot the two functions shown In Figures 6.15 and 6.16,’and the Draw Both Functions button draws them both on the same PictureBox.