Rubber Band


 

Introduction

When you draw a line using the TCanvas::LineTo() method (even if you use the Win32 Windows GDI's LineTo() function), the compiler would draw a line from the starting point to the end point. By default, you would get a live feedback of where the line starts while you are drawing it. Rubber banding is a system of keeping track of a graphic, such as a line, a rectangle, or an ellipse, etc, you are drawing. The VCL ships with properties of the TPen class, properties that help with rubber banding.

You can start with a form equipped with a Color Grid control as follows:

 

 

Rubber Banding

Rubber banding is performed using logic and related properties of the TCanvas class. To start, you would need a Boolean variable that can check whether the user is drawing or not. Also, you would need two helpful points. One of the points would track the beginning of the graphic, that is, where the user clicked and held the mouse. The other point would keep track of where the mouse is as the user is moving it. These variables can be declared as follows:

//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "CGRID.h"
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
    TPanel *Panel1;
    TCColorGrid *CColorGrid1;
private:
    Boolean IsDrawing;
    int StartX, StartY;
    int EndX, EndY;	// User declarations
public:		// User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

One of the things you should do at all times is to know the situation or state of the mouse: is it moving, is it help, has the user released it? This means that you will combine the mouse events and the IsDrawing declared variable.

When the user clicks and holds the mouse down, this might be the beginning of the line. Therefore, you can use the OnMouseDown event to start the line by setting the IsDrawing variable to True. Also, because this is the beginning of the line, use the above declared variables to reserve the beginning of the line. To process rubber banding, we need to know the track on which the line is being drawn. The even can be implemented as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    // Reserve the starting point of the line
    // using our own StartX and StartY
    StartX = X;
    StartY = Y;
    // To keep track of the line the user is drawing,
    // we need to know at all times where the mouse is
    EndX   = X;
    EndY   = Y;

    // Change the pen mode
    Canvas->Pen->Mode = pmXor;

    // Start the line on the current point
    Canvas->MoveTo(StartX, StartY);
    // Behave as if the line ends here
    Canvas->LineTo(EndX, EndY);

    IsDrawing = True;
}
//---------------------------------------------------------------------------

Once the user has clicked and held the mouse down, he or she may start moving it to draw. While the mouse is moving the OnMouseMove event is fired. To perform rubber banding, we first need to know if the user is drawing. If he is not, then there is nothing to do. But if the user is drawing, we need to appropriately change the pen mode, update the position of the line, and restart the line drawing process. Therefore, we can implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
      int X, int Y)
{
    if(!IsDrawing)
        return;

    Canvas->Pen->Mode = pmNotXor;
    Canvas->Pen->Color = CColorGrid1->ForegroundColor;
    Canvas->MoveTo(StartX, StartY);
    Canvas->LineTo(EndX, EndY);

    EndX = X;
    EndY = Y;
    Canvas->MoveTo(StartX, StartY);
    Canvas->LineTo(EndX, EndY);
}
//---------------------------------------------------------------------------

Finally, when the user releases the mouse, we must behave again as if we are starting and ending a line, change the pen mode, draw the line again, and let the compiler know that, since the user has released the mouse, the IsDrawing variable can be set to False. The even can be implemented as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    EndX = X;
    EndY = Y;

    Canvas->Pen->Mode = pmXor;
    Canvas->MoveTo(StartX, StartY);
    Canvas->LineTo(EndX, EndY);

    Canvas->Pen->Mode = pmCopy;
    Canvas->MoveTo(StartX, StartY);
    Canvas->LineTo(EndX, EndY);

    IsDrawing = False;
}
//---------------------------------------------------------------------------

Download

 


Copyright © 2003-2007 FunctionX, Inc.