MFC Learning (01) Rectangular Box Selects Moving Triangle (VC++6.0 Version)

Keywords: Handlebars Mobile

MFC Learning (01) Rectangular Box Selects Moving Triangle (VC++6.0 Version)

Note: This is a computer graphics homework after class, homework original text: Given a triangle (vertex coordinates themselves given, not necessarily to draw triangles, more than three vertices of the plane graphics can also be drawn), using the mouse and mouse response function, select the rectangular area, determine whether the triangle is in the rectangular area, such as If the triangle falls completely into the rectangular area, the color of the triangle changes, and when the mouse is in the rectangular area to be judged, dragging the mouse can move the triangle.

For reference only, please spray if you don't like it.

0. Code address

1. Let's first look at the sample program provided by the teacher.

2. Analysis

  1. The triangle is initially black, with three vertices each having a small square.
  2. When dragging, the mouse style remains unchanged. The dragged rectangle is a solid green line.
  3. If all three points are in the rectangular box (which I think is the same as the line pressing), then the color of the triangle will change.
  4. When the triangle is red, the red triangle will follow when the left mouse button is pressed and the mouse is moved.
  5. At the end of the drag (when the left mouse button pops up), the triangle continues to turn black.

3. Pseudo-code

// Left mouse button press event
 Void OnLButton Down (click the coordinate point)
{
    M_IsLButton Down = true; // Mark this time the left mouse button has been pressed
    if (can start moving)
    {
        Record the starting point p1;
    }
    Others// Can't move, start drawing rectangles
    {
        Record the upper left corner coordinate p0 = point of the rectangle.
        Initialize the lower right corner coordinate pm = point of the rectangle;
    }
}
// Mouse movement events
 Void OnMouseMove
{
    // We only consider the movement of the mouse when the left mouse button is pressed.
    if (true == m_IsLButtonDown)
    {
        if (can start moving)
        {
            Modify each vertex of the triangle by using the current mouse position and the position of the initial record; //Make the graph move with the mouse
        }
        Others// Can't move, just continue drawing rectangle
        {
            Record the lower right corner coordinate pm = point of the rectangle.
        }
    }
}
// Left mouse button pop-up event
 Void OnLButton Up (pop-up coordinate point)
{
    M_IsLButton Down = true; // Mark this time the left mouse button has been pressed
    if (can start moving)
    {
        At the end of this move, the mark can not be moved.
        Modify the brush color to black.
    }
    Others // Can't move, so start judging whether the current drawing rectangle contains a triangle or not.
    {
        if (the rectangle contains a triangle)
        {
            Markers can be moved;
            Modify the brush color to red.
        }
        Clear the rectangular information;
    }
}

4. Handlebars begin to frame.

1. New projects

2. Basic Settings

  • Single document, MFC standard, static library, and then click Finish

3. Adding class variables

  • Find Class View
  • Right-click CRectangleSelectTriangleView
  • Click Add Member Variable...

  • Add the following class variables in turn
bool m_IsLButtonDown; // Whether to press the left key or not
bool m_IsReadyToMove; // Can I start moving?
CPoint p0; // Record the upper-left coordinates of the rectangular box
CPoint pm; // Record the lower right corner coordinates of the rectangle
CPoint p1; // It's used when moving triangles.
CPen * m_CurrentPen; // Point to the current brush, initialize to point to the black brush
CPoint m_Points[3]; // Preserving the three vertices of a triangle

4. Adding custom message handlers

  • ** Right-click on the CRectangleSelectTriangleView class

  • Add the following message handlers in turn
WM_LBUTTONDOWN
WM_MOUSEMOVE
WM_LBUTTONUP
  • This should be done as follows:

So far, we have completed the basic "framework"
Next comes the code section.
According to our pseudo-code idea, step by step

5. Step-by-step coding

  • 1. Define three different color brushes in CRectangleSelectTriangleView.cpp
CPen BlackPen(BS_SOLID, 2, RGB(0, 0, 0));
CPen GreenPen(BS_SOLID, 2, RGB(0, 255, 0));
CPen RedPen(BS_SOLID, 2, RGB(255, 0, 0));
  • 2. Define a macro for coordinate transformation at the beginning of CRectangleSelectTriangleView.cpp
#define Trans(p1, rect) CPoint(long((p1.x+0.5)/1) - rect.Width()/2, long((p1.y+0.5)/1) - rect.Height()/2)
  • 3. Assignment of class variables in the CRectangleSelectTriangleView class constructor
//Initially immovable
m_IsReadyToMove = false;
m_IsLButtonDown = false;
p0 = pm = p1 = 0;
m_Points[0] = CPoint(143, 113);
m_Points[1] = CPoint(245, 64);
m_Points[2] = CPoint(205, 214);
//Initial point to black brush
m_CurrentPen = &BlackPen; 
  • 4. In the OnDraw function
void CRectangleSelectTriangleView::OnDraw(CDC* pDC)
{
    CRectangleSelectTriangleDoc* pDoc = GetDocument(); 
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    // TODO: Add drawing code for native data here

    CRect rect;
    GetClientRect(&rect);
    pDC->SetWindowExt(rect.Width(), rect.Height());
    pDC->SetViewportExt(rect.Width(), -rect.Height());
    pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);

    CDC memDC;  // Declare memory DC
    CBitmap NewBitmap, *pOldBitmap;
    memDC.CreateCompatibleDC(pDC);  // Create a memory DC compatible with the display DC 
    NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());     // Creating Compatible Memory Bitmaps 
    pOldBitmap = memDC.SelectObject(&NewBitmap);    // Select Compatible Bitmaps into Memory DC
    memDC.FillSolidRect(rect, pDC->GetBkColor());   // Fill the client area with the original background color, otherwise it's black.
    rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);
    memDC.SetWindowExt(rect.Width(), rect.Height());
    memDC.SetViewportExt(rect.Width(), -rect.Height());
    memDC.SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
    memDC.SetROP2(R2_COPYPEN); // Setting up the drawing mode

    DrawObject(&memDC, rect);  // Draw triangles, draw rectangular boxes in this function

    // Copy bitmaps from memory DC to device DC
    pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, -rect.Width() / 2, -rect.Height() / 2, SRCCOPY); 
    memDC.SelectObject(pOldBitmap);
}
  • Declarations and Definitions of DrawObject Functions
// The public part declaration within the CRectangleSelectTriangleView class in CRectangleSelectTriangleView.h
void CRectangleSelectTriangleView::DrawObject(CDC* pDC, const CRect& rect);
// Implementation in CRectangleSelectTriangleView.cpp
void CRectangleSelectTriangleView::DrawObject(CDC* pDC, const CRect& rect)
{
    int i = 0;
    CPen * oldPen = pDC->SelectObject(m_CurrentPen);

    // Draw a triangle
    for (i = 0; i < 2; i++)
    {
        pDC->MoveTo(Trans(m_Points[i], rect));
        pDC->LineTo(Trans(m_Points[i + 1], rect));
    }
    pDC->MoveTo(Trans(m_Points[2], rect)), pDC->LineTo(Trans(m_Points[0], rect));

    // Draw a small square near the vertex
    CPoint v1(6, 6); // The radius of a circle is six pixels
    for (i = 0; i < 3; i++)
    {
        pDC->Rectangle(CRect(CPoint(Trans(m_Points[i], rect) - v1), CPoint(Trans(m_Points[i], rect) + v1)));
        // If you don't like squares, you can change the following sentence into a small circle.
        // pDC->Ellipse(CRect(CPoint(Trans(m_Points[i], rect) - v1), CPoint(Trans(m_Points[i], rect) + v1)));
    }

    // Next draw the rectangular box
    HBRUSH hb = (HBRUSH)GetStockObject(NULL_BRUSH);     // Get a transparent fill handle
    CBrush* Brush = CBrush::FromHandle(hb);             // Get an object from this handle
    CBrush *pOldBrush = pDC->SelectObject(Brush);       // Select the transparent brush into the device description table

    pDC->SelectObject(&GreenPen);
    pDC->Rectangle(CRect(Trans(p0, rect), Trans(pm, rect)));

    pDC->SelectObject(oldPen);
}

At this point, we can compile and run to see the triangle effect map.

  • 5. In OnLButton Down function
void CRectangleSelectTriangleView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: Add message handler code and/or call default values here

    m_IsLButtonDown = true;   // Mark this time the left mouse button has been pressed

    if (m_IsReadyToMove)    // If you can start moving
    {
        p1 = point;
    }
    else    // If you can't move, start drawing rectangles.
    {
        p0 = point;     // Record the upper-left coordinates of the rectangle
        pm = point;     // Initialize the lower right corner coordinates of the rectangle
    }

    CView::OnLButtonDown(nFlags, point);
}
  • 6. In On OnOnMouseMove function
void CRectangleSelectTriangleView::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: Add message handler code and/or call default values here

    // We only consider the movement of the mouse when the left mouse button is pressed.
    if (true == m_IsLButtonDown)
    {
        if (m_IsReadyToMove)    // If you can start moving
        {
            // Modify each vertex of the triangle by using the current mouse position point and p1 to achieve the effect that the graph moves with the mouse
            for (int i = 0; i < 3; i++)
            {
                m_Points[i] += point - p1;
            }
            p1 = point;
        }
        else // If you can't move, continue drawing rectangles.
        {
            pm = point;     // Record the lower right corner coordinates of the rectangle
        }

        // Trigger OnDraw function, dynamic effect
        Invalidate(FALSE);
    }
    CView::OnMouseMove(nFlags, point);
}
  • 7. In the OnLButton Up function
void CRectangleSelectTriangleView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add message handler code and/or call default values here

    m_IsLButtonDown = false;   // Mark this time the left mouse button has popped up

    if (m_IsReadyToMove)    // If you can start moving
    {
        m_IsReadyToMove = false;    // At the end of this move, the mark cannot be moved.
        m_CurrentPen = &BlackPen;   // Modify the brush color to black
    }
    else // If you can't move, start judging whether the rectangle currently drawn contains a triangle.
    {

        if (IsInside())
        {
            m_IsReadyToMove = true;     // Markers can be moved
            m_CurrentPen = &RedPen;     // Modify the brush color to red
        }
    }
    p0 = pm = 0;    // Clear this rectangular information

    // Trigger OnDraw function, dynamic effect
    Invalidate(FALSE);
    CView::OnLButtonUp(nFlags, point);
}
  • Declarations and Definitions of IsInside Functions
// The public part declaration within the CRectangleSelectTriangleView class in CRectangleSelectTriangleView.h
bool CRectangleSelectTriangleView::IsInside();
// Implementation in CRectangleSelectTriangleView.cpp
bool CRectangleSelectTriangleView::IsInside()
{
    for (int i = 0; i < 3; i++)
    {
        if (m_Points[i].x >= p0.x && m_Points[i].y >= p0.y &&
            m_Points[i].x <= pm.x && m_Points[i].y <= pm.y
            )
            // If the current vertex is inside the rectangle, continue to determine the next vertex
            continue;
        // If the current vertex is not inside the rectangle, return false directly
        return false;
    }
    // If all three points are inside the rectangle, then the center of the triangle is inside the rectangle.
    return true;
}

6. Summary

  • I have limited ability. In this article, I do not judge whether the mouse is in the triangle when I am ready to drag.
  • If you need any friends, please comment below. I will send you the code.

Posted by showman on Thu, 04 Jul 2019 15:53:33 -0700