Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 16 (cont.) Drawing in a Window Using the Mouse

Similar presentations


Presentation on theme: "Chapter 16 (cont.) Drawing in a Window Using the Mouse"— Presentation transcript:

1 Chapter 16 (cont.) Drawing in a Window Using the Mouse

2 Drawing Graphics in Practice
The easiest mechanism for drawing is using just the mouse.

3 Circle

4 Rectangle

5 Curve

6 Message from the Mouse WM_LBUTTONDOWN WM_LBUTTONUP WM_MOUSEMOVE
Left mouse button is pressed WM_LBUTTONUP Left mouse button is released WM_MOUSEMOVE The mouse is moved.

7 Mouse Message Handlers
Create a handler by clicking on the ID of a mouse message. Then select the down arrow in its right column. For example, select <add> OnLButtonUp for the WM_LBUTTONUP message. The wizard generates the WM_LBUTTONUP message handler: void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CView::OnLButtonUp(nFlags, point); }

8 Verify the left mouse button is down
OnMouseMove() void CSketcherView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if (nFlags & MK_LBUTTON) m_SecondPoint = point; // Test for a previous temporary element // We get to here if there was a previous mouse move // so add code to delete the old element } // Add code to create new element // and cause it to be drawn Verify the left mouse button is down

9 bitwise AND operator (P.80)
nFlags MK_CONTROL Ctrl key being pressed MK_LBUTTON Left mouse button being down MK_MBUTTON Middle mouse button being down MK_RBUTTON Right mouse button being down MK_SHIFT Shift key being pressed To test for the Ctrl key being pressed if (nFlags & MK_CONTROL) // Do something bitwise AND operator (P.80)

10 Homework OX-chess Write a program to draw a 3x3 chess board.
When the user click the mouse left button, the program will alternatively draw “O” or “X” in the clicked cell. Note that even if the user may not accurately click at the center of the cell, the “O” or “X” should be drawn precisely at the center. void CSketcherView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default const int r = 40;// radius static int round = 0; CClientDC aDC(this); int x = 0; int y = 0 ; if (point.x < 300) x = point.x / 100 * ; y = point.y / 100 * ; } else x = point.x; y = point.y; switch (round++ % 2) case 0:// Draw O aDC.Ellipse(x-r, y-r, x+r, y+r); break; case 1: // Draw X aDC.MoveTo(x-r, y-r); aDC.LineTo(x+r, y+r); aDC.MoveTo(x-r, y+r); aDC.LineTo(x+r, y-r); CView::OnLButtonDown(nFlags, point);

11 Draw the board void CSketcherView::OnDraw(CDC* pDC) { CSketcherDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; pDC->Rectangle(100, 100, 400, 400); for (auto i=2; i<4; i++) pDC->MoveTo(i*100, 100); pDC->LineTo(i*100, 400); pDC->MoveTo(100, i*100); pDC->LineTo(400, i*100); }

12 Draw the circle at mouse click
void CSketcherView::OnLButtonDown(UINT nFlags, CPoint point) { const int r = 40;// radius CClientDC aDC(this); aDC.Ellipse(point.x-r, point.y-r, point.x+r, point.y+r); CView::OnLButtonDown(nFlags, point); }

13 Homework Repeat the OX-chess homework, with two additional features:
Check whether a cell is already occupied. AfxMessageBox(L"That cell is occupied."); Check whether any player wins. void DrawX(CClientDC* pDC, int row, int column) { const int r = 40;// radius int x = row* ; int y = column* ; pDC->MoveTo(x-r, y-r); pDC->LineTo(x+r, y+r); pDC->MoveTo(x-r, y+r); pDC->LineTo(x+r, y-r); } void DrawO(CClientDC* pDC, int row, int column) pDC->Ellipse(x-r, y-r, x+r, y+r); void CSketcherView::OnLButtonDown(UINT nFlags, CPoint point) // TODO: Add your message handler code here and/or call default static bool cell[10][10] = { false }; static int round = 0; CClientDC aDC(this); int x = 0; int y = 0 ; #ifdef _DEBUG #endif x = point.x / 100; y = point.y / 100; #ifndef _DEBUG if (cell[x][y]) AfxMessageBox(L"That cell is occupied."); return; else cell[x][y] = true; switch (round++ % 2) case 0:// Draw O DrawO(&aDC, x,y); break; case 1: // Draw X DrawX(&aDC, x,y); /* aDC.MoveTo(x-r, y-r); aDC.LineTo(x+r, y+r); aDC.MoveTo(x-r, y+r); aDC.LineTo(x+r, y-r); */ if (round == 5) CPen aPen(PS_SOLID, 2, RGB(0, 0, 255)); CPen* pOldPen = aDC.SelectObject(&aPen); DrawO(&aDC, 1, 1); DrawO(&aDC, 2, 2); DrawO(&aDC, 3, 3); CView::OnLButtonDown(nFlags, point);

14 Storing the Position of the Cursor
You may store the position of the cursor in the CSketcherView class. Figure (P.966)

15 m_FirstPoint & m_SecondPoint
Initialization in the constructor, CSketcherView::CSketcherView(): m_FirstPoint(CPoint(0,0)), m_SecondPoint(CPoint(0,0)) Assigning values in the message handler, void CSketcherView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here m_FirstPoint = point; // Record the cursor position } Now, let’s define an element for drawing.

16 Defining Classes for Elements
Figure 16-12

17 Add a New Class into Your Project
Right-click the Sketcher project in Class View, and then select Add – Class. Select MFC.

18 Creating the CElement Class

19 Creating the CLine Class
Create element classes using CElement as the base class Choose C++ as the installed template

20 Create CLine, CRectangle, CCircle, CCurve

21 Storing a Temporary Element in the View
In the view class, add a pointer to a CElement object Right-click the CSketcherView class Add > Add Variable

22 m_pTempElement The Add Member Variable Wizard adds some code to initialized the new variable. NULL fits nicely for us! CSketcherView::CSketcherView() : m_FirstPoint(CPoint(0,0)) , m_SecondPoint(CPoint(0,0)) , m_pTempElement(NULL) { // TODO: add construction code here }

23 Check SketcherView.h At the beginning, there is a line:
#include “atltypes.h” The wizard assumed the CElement type is an ATL (Active Template Library) type. Delete this line and add the following statement, immediately following the #pragma once directive: #include "Elements.h";

24 The CElement Class class CElement : public CObject { protected:
COLORREF m_Color; // Color of an element int m_PenWidth; // Pen width CElement(); public: virtual ~CElement(); virtual void Draw(CDC* pDC) {} // Virtual draw operation CRect GetBoundRect(); // Get the bounding rectangle for an element }; Colors are common for all types of elements The constructor is changed from public to protected.

25 The CLine Class (in Elements.h)
class CLine :public CElement { public: virtual ~CLine(void); virtual void Draw(CDC* pDC); // Function to display a line // Constructor for a line object CLine(const CPoint& start, const CPoint& end, COLORREF aColor); protected: CPoint m_StartPoint; // Start point of line CPoint m_EndPoint; // End point of line CLine(void); // Default constructor should not be used };

26 Implementation of the CLine Constructor (in Elements.cpp)
CLine::CLine(const CPoint& start, const CPoint& end, COLORREF aColor) { m_StartPoint = start; m_EndPoint = end; m_Color = aColor; m_PenWidth = 1; // Set pen width }

27 Drawing a Line Pen Width (set in P.974) defined in CElement
// Draw a CLine object void CLine::Draw(CDC* pDC) { // Create a pen for this object and // initialize it to the object color and line width of 1 pixel CPen aPen; if(!aPen.CreatePen(PS_SOLID, m_PenWidth, m_Color)) // Pen creation failed. Abort the program AfxMessageBox(_T("Pen creation failed drawing a line"), MB_OK); AfxAbort(); } CPen* pOldPen = pDC->SelectObject(&aPen); // Select the pen // Now draw the line pDC->MoveTo(m_StartPoint); pDC->LineTo(m_EndPoint); pDC->SelectObject(pOldPen); // Restore the old pen defined in CElement If the pen cannot be created, display a message box and abort the program.

28 Setting the Drawing Mode
SetROP2() Set Raster OPeration to R2_BLACK All drawing is in black R2_WHITE All drawing is in white R2_NOP Drawing operations do nothing R2_COPYPEN Drawing is in the pen color. This is the default. R2_XORPEN Drawing is in the color produced by exclusive ORing the pen color and the background color. R2_NOTXORPEN Drawing is in the color that is the inverse of the R2_XORPEN color.

29 R2_NOTXORPEN If you draw the same shape again, the shape disappears. R
B Background – white 1 Pen – red XORed NOT XOR – produces red R G B Background – red 1 Pen – red XORed NOT XOR – produces white

30 Coding the OnMouseMove() Handler
Using the CClientDC class rather than CDC (P.987) It automatically destroys the device context when you are done. void CSketcherView::OnMouseMove(UINT nFlags, CPoint point) { // Define a Device Context object for the view CClientDC aDC(this); // DC is for this view aDC.SetROP2(R2_NOTXORPEN); // Set the drawing mode if (nFlags & MK_LBUTTON) { m_SecondPoint = point; // Save the current cursor position if(m_pTempElement) { // Redraw the old element so it disappears from the view m_pTempElement->Draw(&aDC); delete m_pTempElement; // Delete the old element m_pTempElement = NULL; // Reset the pointer to 0 } // Create a temporary element of the type and color that // is recorded in the document object, and draw it m_pTempElement = CreateElement();// Create a new element m_pTempElement->Draw(&aDC); // Draw the element defined in CSketcherView (P.971) will be defined on P.988

31 CreateElement()

32 Implementing CreateElement()
CElement* CSketcherView::CreateElement(void) { // Get a pointer to the document for this view CSketcherDoc* pDoc = GetDocument(); // Now select the element using the type stored in the document switch(pDoc->GetElementType()) { case RECTANGLE: return new CRectangle(m_FirstPoint, m_SecondPoint, pDoc->GetElementColor()); case CIRCLE: return new CCircle(m_FirstPoint, m_SecondPoint, case LINE: return new CLine(m_FirstPoint, m_SecondPoint, default: // Something's gone wrong AfxMessageBox(_T("Bad Element code"), MB_OK); AfxAbort(); return NULL; } will be defined on P.989

33 GetElementType() class CSketcherDoc : public CDocument {
// Rest of the class definition as before … // Operations public: // Get the element type unsigned int GetElementType() { return m_Element; } // Get the element color COLORREF GetElementColor() { return m_Color; } // Rest of the class definition as before };

34 Dealing with WM_LBUTTONUP
void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point) { // Make sure there is an element if (m_pTempElement) // Call a document class function to store the element // pointed to by m_pTempElement in the document object delete m_pTempElement; m_pTempElement = 0; }

35 Exercising Sketcher Now you should be able to draw lines using your mouse. Try to complete the implementation of CCircle & CRectangle.

36 Exercising Sketcher Download a partially finished project sketcher-ch16-line.zip Uncompress the Sketcher folder to Your Documents\Visual Studio 2010\Projects CLine is implemented. You may compile and run it. Try to complete the implementation of CCircle & CRectangle.

37 Bounding Rectangles Positive Not exactly coincide with the enclosing rectangles (defining rectangles) which are used to draw the elements. Thickness must be taken into account.

38 Modify the CElement Class Definition
class CElement : public CObject { protected: COLORREF m_Color; // Color of an element CRect m_EnclosingRect; // Rectangle enclosing an element int m_PenWidth; // Pen width CElement(); public: virtual ~CElement(); virtual void Draw(CDC* pDC) {} // Virtula draw operation CRect GetBoundRect(); // Get the bounding rectangle for an element };

39 GetBoundRect() Assuming the MM_TEXT mapping mode:
// Get the bounding rectangle for an element CRect CElement::GetBoundRect() const { CRect boundingRect(m_EnclosingRect); // Object to store bounding rectangle // Initially, store the enclosing rectangle // Increase the rectangle by the pen width boundingRect.InflateRect(m_PenWidth, m_PenWidth); return boundingRect; }

40 Enlarge the Enclosing Rectangle by the Pen Width
BoundingRect.InflateRect(m_PenWidth, m_PenWidth); BoundingRect = m_EnclosingRect + CRect(m_PenWidth, m_PenWidth, m_PenWidth, m_PenWidth); BoundingRect = m_EnclosingRect; BoundingRect.top -= m_PenWidth; BoundingRect.left -= m_PenWidth; BoundingRect.bottom += m_PenWidth; BoundingRect.right += m_PenWidth;

41 Calculating the Enclosing Rectangle for a Line
CLine::CLine(CPoint Start, CPoint End, COLORREF aColor) { m_StartPoint = Start; m_EndPoint = End; m_Color = aColor; m_PenWidth = 1; // Define the enclosing rectangle m_EnclosingRect = CRect(Start, End); m_EnclosingRect.NormalizeRect(); }

42 The CRectangle Class class CRectangle : public CElement { public:
~CRectangle(void); virtual void Draw(CDC* pDC); // Function to display a rectangle // Constructor for a rectangle object CRectangle(CPoint Start, CPoint End, COLORREF aColor); protected: CRectangle(void); // Default constructor - should not be used };

43 The CRectangle Class Constructor
Similar to that for a CLine constructor, but you don’t need m_StartPoint,m_EndPoint to store the defining points. // CRectangle class constructor CRectangle:: CRectangle(CPoint Start, CPoint End, COLORREF aColor) { m_Color = aColor; // Set rectangle color m_PenWidth = 1; // Set pen width // Define the enclosing rectangle m_EnclosingRect = CRect(Start, End); m_EnclosingRect.NormalizeRect(); }

44 Drawing a Rectangle void CRectangle::Draw(CDC* pDC) {
// Create a pen for this object and // initialize it to the object color and line width of 1 pixel CPen aPen; if(!aPen.CreatePen(PS_SOLID, m_PenWidth, m_Color)) // Pen creation failed AfxMessageBox(_T("Pen creation failed drawing a rectangle"), MB_OK); AfxAbort(); } // Select the pen CPen* pOldPen = pDC->SelectObject(&aPen); // Select the brush CBrush* pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH); // Now draw the rectangle pDC->Rectangle(m_EnclosingRect); pDC->SelectObject(pOldBrush); // Restore the old brush pDC->SelectObject(pOldPen); // Restore the old pen

45 The CCircle Class Similar to the CRectangle class
class CCircle : public CElement { public: ~CCircle(void); virtual void Draw(CDC* pDC); // Function to display a circle // Constructor for a circle object CCircle(CPoint Start, CPoint End, COLORREF aColor); protected: CCircle(void); // Default constructor - should not be used };

46 Implementing the CCircle Class
The point you press the left mouse button is the center. The point you release the left mouse button is a point on the circumference. We need to design the constructor to convert this to the enclosing rectangle of a circle.

47 The CCircle Class Constructor
#include <cmath.h> in Elements.cpp The CCircle Class Constructor CCircle::CCircle(CPoint Start, CPoint End, COLORREF aColor) { // First calculate the radius // We use floating point because that is required by the // library function (in cmath.h) for calculating a square root. long Radius = static_cast<long> (sqrt( static_cast<double>((End.x-Start.x)*(End.x-Start.x)+ (End.y-Start.y)*(End.y-Start.y)))); // Now calculate the rectangle enclosing // the circle assuming the MM_TEXT mapping mode m_EnclosingRect = CRect(Start.x-Radius, Start.y-Radius, Start.x+Radius, Start.y+Radius); m_EnclosingRect.NormalizeRect(); m_Color = aColor; // Set the color for the circle m_PenWidth = 1; // Set pen width to 1 }

48 Drawing a Circle void CCircle::Draw(CDC* pDC) {
// Create a pen for this object and // initialize it to the object color and line width of 1 pixel CPen aPen; if(!aPen.CreatePen(PS_SOLID, m_PenWidth, m_Color)) // Pen creation failed AfxMessageBox(_T("Pen creation failed drawing a circle"), MB_OK); AfxAbort(); } CPen* pOldPen = pDC->SelectObject(&aPen); // Select the pen // Select a null brush CBrush* pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH); // Now draw the circle pDC->Ellipse(m_EnclosingRect); pDC->SelectObject(pOldPen); // Restore the old pen pDC->SelectObject(pOldBrush); // Restore the old brush

49 Rectangles & Circles

50 CCurve Class The CCurve class needs to deal with a variable number of defining points. Use the sequence container “vector” in Chapter 10. This data structure is similar to an array, so you can access each element by an index. However, you don’t need to declare the size of this vector. It will automatically “grow up” as needed. You use the function size() to know the number of elements in this vector, and use the function push_back() to add an element. Add #include <vector> in Elements.h

51 CCurve Class Definition
class CCurve : public CElement { public: virtual ~CCurve(void); virtual void Draw(CDC* pDC);// Function to display a curve // Constructor for a curve object CCurve(const CPoint& first, const CPoint& second, COLORREF aColor); void AddSegment(const CPoint& point); // Add a segment to the curve protected: CCurve(void); std::vector<CPoint> m_Points; // Points defining the curve };

52 Constructor for a curve object
CCurve::CCurve(const CPoint& first, const CPoint& second, COLORREF aColor) { m_Points.push_back(first); m_Points.push_back(second); m_Color = aColor; m_PenWidth = 1; m_EnclosingRect = CRect(min(first.x, second.x), min(first.y, second.y), max(first.x, second.x), max(first.y, second.y)); }

53 Draw a curve void CCurve::Draw(CDC* pDC) { // Create a pen for this object and // initialize it to the object color and line width of 1 pixel CPen aPen; if(!aPen.CreatePen(PS_SOLID, m_PenWidth, m_Color)) // Pen creation failed. Abort the program AfxMessageBox(_T("Pen creation failed drawing a line"), MB_OK); AfxAbort(); } CPen* pOldPen = pDC->SelectObject(&aPen); // Select the pen // Now draw the curve pDC->MoveTo(m_Points[0]); for (size_t i=1; i<m_Points.size(); ++i) pDC->LineTo(m_Points[i]); pDC->SelectObject(pOldPen); // Restore the old pen

54 Re-compute the bounding rectangle incrementally

55 Add a segment to the curve
void CCurve::AddSegment(const CPoint& point) { m_Points.push_back(point); m_EnclosingRect = CRect( min(point.x, m_EnclosingRect.left), min(point.y, m_EnclosingRect.top), max(point.x, m_EnclosingRect.right), max(point.y, m_EnclosingRect.bottom)); }

56 Modify OnMouseMove() (P.987)
if (CURVE == GetDocument()->GetElementType()) // Is it a curve? { // We are drawing a curve so add a segment to the existing curve static_cast<CCurve*>(m_pTempElement)->AddSegment(m_SecondPoint); m_pTempElement->Draw(&aDC); // Now draw it return; // We are done } // If we get to here it’s not a curve so // Redraw the old element so it disappears from the view aDC.SetROP2(R2_NOTXORPEN); // Set the drawing mode m_pTempElement->Draw(&aDC); delete m_pTempElement; // Delete the old element m_pTempElement = NULL; // Reset the pointer to 0

57 Now you can draw the four elements

58 Capturing Mouse Message
If you try drawing a line while dragging the cursor outside the client view area, you’ll notice some peculiar effects.

59 SetCapture() Tell Windows 7/XP that you want your view window to get all the mouse messages until you release it. void CSketcherView::OnLButtonDown (UINT nFlags, CPoint point) { m_FirstPoint = point; SetCapture(); }

60 ReleaseCapture() void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point) { if (this == GetCapture()) // Release the mouse only if ReleaseCapture(); // you’ve captured it earlier // Make sure there is an element if (m_pTempElement) // Call a document class function to store the element // pointed to by m_pTempElement in the document object delete m_pTempElement; m_pTempElement = 0; }

61 CSketcherView::OnMouseMove()
Only deal with messages that have been captured by this view : if ((nFlags & MK_LBUTTON) && (this == GetCapture()) )

62 Midterm Exam Time: May 25th, 14:00-15:40 Scope: Chapter 7,8,9,14,15,16


Download ppt "Chapter 16 (cont.) Drawing in a Window Using the Mouse"

Similar presentations


Ads by Google