1 / 61

Mouse-Driven Graphics: Drawing with Ease and Precision

This chapter explores drawing graphics using a mouse, covering handling mouse messages, creating shapes like circles and rectangles, and practical exercises like OX-chess. Learn how to draw precisely with practical examples and code snippets. Enhance your skills in graphic drawing through mouse interactions.

rlaws
Télécharger la présentation

Mouse-Driven Graphics: Drawing with Ease and Precision

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 16 (cont.)Drawing in a WindowUsing 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 • 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. 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. 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.

  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.

  14. Storing the Position of the Cursor • You may store the position of the cursor in the CSketcherView class. • Figure 16-11(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 widthCElement(); 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) // 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() • SetRaster 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.

  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, pDoc->GetElementColor()); case LINE: return new CLine(m_FirstPoint, m_SecondPoint, pDoc->GetElementColor()); 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. 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.

  37. 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 };

  38. 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; }

  39. 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;

  40. 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(); }

  41. 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 };

  42. 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(); }

  43. 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 }

  44. 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 };

  45. 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.

  46. #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 }

  47. 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 }

  48. Rectangles & Circles

  49. 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

  50. 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 };

More Related