When a stroke is added to a drawing in Scribble, the rectangular region that contains the new stroke is the only area that needs to be updated; the remainder of the drawing can be left alone. Therefore, a logical choice for a hint in Scribble is the bounding rectangle of the new stroke.
Instead of creating a separate class to represent the hint, it’s more convenient to pass a CStroke
pointer as a hint. Store the bounding rectangle for each stroke in the CStroke
object itself, so that it can be quickly referred to by OnUpdate
to determine which area of the window needs to be repainted.
The following procedure assumes you have your Scribble project file (Scribble.dsw) open in the workspace.
To define bounding rectangles for strokes
CStroke
.m_pointArray
declaration: protected:
CRect m_rectBounding; // smallest rect that surrounds all
// of the points in the stroke
public:
CRect& GetBoundingRect() { return m_rectBounding; }
The protected member variable m_rectBounding
is a CRect object storing the bounding rectangle, and the public member function GetBoundingRect
allows the rectangle to be retrieved by the view.
IMPLEMENT_SERIAL( CStroke, CObject, 2 )
This version of Scribble changes what’s stored in a CStroke
object by adding a new member variable. Changing the schema number distinguishes strokes saved by this version of Scribble from those of other versions.
CStroke
constructor (the one that initializes the pen width) and add the following line:m_rectBounding.SetRectEmpty();
This initializes the bounding rectangle to an empty rectangle in the constructor.
CStroke
Serialize
function and add the following line just after the first if condition:ar << m_rectBounding;
This stores the m_rectBounding
member variable in the archive.
ar >> m_rectBounding;
This reads the m_rectBounding
member variable from the archive.
In the next procedure, you’ll add a helper function, FinishStroke
. This function calculates the bounding rectangle, which is needed for smart repainting.
To add the FinishStroke helper function
CStroke
class icon and click the right mouse button.The Add Member Function dialog box appears.
void
).FinishStroke()
ClassWizard adds the declaration to the header file, creates a starter definition in the implementation file, and jumps you to the body of the definition so you can begin typing your application-specific code.
FinishStroke
:if( m_pointArray.GetSize() == 0 )
{
m_rectBounding.SetRectEmpty();
return;
}
CPoint pt = m_pointArray[0];
m_rectBounding = CRect( pt.x, pt.y, pt.x, pt.y );
for (int i=1; i < m_pointArray.GetSize(); i++)
{
// If the point lies outside of the accumulated bounding
// rectangle, then inflate the bounding rect to include it.
pt = m_pointArray[i];
m_rectBounding.left = min(m_rectBounding.left, pt.x);
m_rectBounding.right = max(m_rectBounding.right, pt.x);
m_rectBounding.top = min(m_rectBounding.top, pt.y);
m_rectBounding.bottom = max(m_rectBounding.bottom, pt.y);
}
// Add the pen width to the bounding rectangle. This is needed
// to account for the width of the stroke when invalidating
// the screen.
m_rectBounding.InflateRect(CSize(m_nPenWidth, m_nPenWidth));
return;
The FinishStroke
member function calculates the bounding rectangle for a stroke. In this function, the stroke object iterates through its array of points, testing the location of each; if a point falls outside the current bounding rectangle, the stroke object enlarges the bounding rectangle just enough to contain it. Then the bounding rectangle is expanded on each side by the width of the pen.