MFC学习(01) 矩形框选择移动三角形 (VC++6.0版本)

MFC学习(01) 矩形框选择移动三角形 (VC++6.0版本)


小注:这是一次计算机图形学的课后作业,作业原文:给定三角形(顶点坐标自己给定,不一定非要画三角形,画多于3个顶点的平面图形也可以),利用鼠标与鼠标响应函数,选取矩形区域,判断三角形是否在矩形区域内,如果完全落入矩形区域内,三角形改变颜色,并且当鼠标处于待判断矩形区域时,拖动鼠标可以移动三角形。

仅供参考,不喜请喷

0. 代码地址

1. 先来看看老师提供的样例程序

样例程序

2. 分析

  1. 三角形初始是黑色的,三个顶点分别有一个小正方形
  2. 拖动时,鼠标样式没有改变,拖动的矩形是绿色实线
  3. 如果三个点都在矩形框里(这我认为压线也算),那么三角形的颜色会改变
  4. 三角形为红色时,按住鼠标左键并且移动鼠标时红色三角形才会跟随
  5. 拖动结束的时候(鼠标左键弹起时),三角形继续变成黑色

3. 伪代码

// 鼠标左键按下事件
void OnLButtonDown(单击的坐标点point)
{
    m_IsLButtonDown = true;   // 标记一下此时鼠标左键已经按下了
    if (可以开始移动)
    {
        记录开始移动的点p1;
    }
    else // 不能移动,就开始画矩形
    {
        记录矩形的左上角坐标p0 = point;
        初始化矩形右下角坐标pm = point;
    }
}
// 鼠标移动事件
void OnMouseMove(移动的当前坐标点point)
{
    // 我们只考虑鼠标左键按下时鼠标的移动事件
    if (true == m_IsLButtonDown)
    {
        if (可以开始移动)
        {
            利用当前鼠标位置和初始记录的位置来修改三角形的每个顶点; // 做到图随鼠标动的效果
        }
        else // 不能移动,就继续画矩形
        {
            记录矩形右下角坐标pm = point;
        }
    }
}
// 鼠标左键弹起事件
void OnLButtonUp(弹起的坐标点point)
{
    m_IsLButtonDown = false;   // 标记一下此时鼠标左键已经弹起了
    if (可以移动)
    {
        本次移动结束,标记不可以移动;
        修改画笔颜色为黑色;
    }
    else // 不能移动,就开始判断当前画的矩形是否包含了三角形
    {
        if (矩形包含了三角形)
        {
            标记可以移动;
            修改画笔颜色为红色;
        }
        清空本次矩形信息;
    }
}

4. 手把手开始做框架

1.新建项目

新建项目

2.基本设置

  • 单文档、MFC标准、静态库,然后点“完成”

单文档

3.添加类变量

  • 找到类视图
  • 右键CRectangleSelectTriangleView
  • 单击Add Member Variable…

添加类变量

  • 依次添加以下类变量
bool m_IsLButtonDown; // 左键是否按下
bool m_IsReadyToMove; // 是否可以开始移动
CPoint p0; // 记录矩形框左上角坐标
CPoint pm; // 记录矩形右下角坐标
CPoint p1; // 移动三角形的时候会用到
CPen * m_CurrentPen; // 指向当前画笔,初始化指向黑色画笔
CPoint m_Points[3]; // 保存三角形的三个顶点

4.添加自定义消息处理函数

  • **在CRectangleSelectTriangleView类上右键

这里写图片描述

  • 依次添加下列消息处理函数
WM_LBUTTONDOWN
WM_MOUSEMOVE
WM_LBUTTONUP
  • 完成时应该是这样的:

这里写图片描述

至此,我们已经完成了基础的“框架”
接下来就是代码部分了
按照我们的伪代码思路,一步一步来

5.一步步写代码

  • 1.在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.在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.在CRectangleSelectTriangleView类构造函数里对类变量赋值
//初始不可以移动
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);
//初始指向黑色画笔
m_CurrentPen = &BlackPen; 
  • 4.在OnDraw函数里
void CRectangleSelectTriangleView::OnDraw(CDC* pDC)
{
    CRectangleSelectTriangleDoc* pDoc = GetDocument(); 
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    // TODO: 在此处为本机数据添加绘制代码

    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;  // 声明内存DC
    CBitmap NewBitmap, *pOldBitmap;
    memDC.CreateCompatibleDC(pDC);  // 创建一个与显示DC兼容的内存DC 
    NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());     // 创建兼容内存位图 
    pOldBitmap = memDC.SelectObject(&NewBitmap);    // 将兼容位图选入内存DC
    memDC.FillSolidRect(rect, pDC->GetBkColor());   // 按原来背景色填充客户区,否则是黑色
    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); // 设置绘图方式

    DrawObject(&memDC, rect);  // 画三角形,画矩形框都在这一个函数里

    // 将内存DC中的位图拷贝到设备DC
    pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, -rect.Width() / 2, -rect.Height() / 2, SRCCOPY); 
    memDC.SelectObject(pOldBitmap);
}
  • DrawObject函数的声明与定义
// 在CRectangleSelectTriangleView.h里CRectangleSelectTriangleView类内部public部分声明
void CRectangleSelectTriangleView::DrawObject(CDC* pDC, const CRect& rect);
// 在CRectangleSelectTriangleView.cpp里实现
void CRectangleSelectTriangleView::DrawObject(CDC* pDC, const CRect& rect)
{
    int i = 0;
    CPen * oldPen = pDC->SelectObject(m_CurrentPen);

    // 画出三角形
    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));

    // 画出顶点附近的小正方形
    CPoint v1(6, 6); // 圆的半径为6个像素
    for (i = 0; i < 3; i++)
    {
        pDC->Rectangle(CRect(CPoint(Trans(m_Points[i], rect) - v1), CPoint(Trans(m_Points[i], rect) + v1)));
        // 如果不喜欢正方形,可以用下面这句改成小圆圈
        // pDC->Ellipse(CRect(CPoint(Trans(m_Points[i], rect) - v1), CPoint(Trans(m_Points[i], rect) + v1)));
    }

    // 接下来画矩形框
    HBRUSH hb = (HBRUSH)GetStockObject(NULL_BRUSH);     // 得到一个透明填充句柄
    CBrush* Brush = CBrush::FromHandle(hb);             // 通过该句柄得到一个对象
    CBrush *pOldBrush = pDC->SelectObject(Brush);       // 将透明画刷选入设备描述表

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

    pDC->SelectObject(oldPen);
}

至此,我们可以编译运行看一下三角形效果图了
三角形

  • 5.在OnLButtonDown函数里
void CRectangleSelectTriangleView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值

    m_IsLButtonDown = true;   // 标记一下此时鼠标左键已经按下了

    if (m_IsReadyToMove)    // 如果可以开始移动
    {
        p1 = point;
    }
    else    // 不能移动,就开始画矩形
    {
        p0 = point;     // 记录矩形的左上角坐标
        pm = point;     // 初始化矩形右下角坐标
    }

    CView::OnLButtonDown(nFlags, point);
}
  • 6.在OnOnMouseMove函数里
void CRectangleSelectTriangleView::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值

    // 我们只考虑鼠标左键按下时鼠标的移动事件
    if (true == m_IsLButtonDown)
    {
        if (m_IsReadyToMove)    // 如果可以开始移动
        {
            // 利用当前鼠标位置 point 和 p1 来修改三角形的每个顶点,做到图随鼠标动的效果
            for (int i = 0; i < 3; i++)
            {
                m_Points[i] += point - p1;
            }
            p1 = point;
        }
        else // 不能移动,就继续画矩形
        {
            pm = point;     // 记录矩形右下角坐标
        }

        // 触发OnDraw函数,动态效果
        Invalidate(FALSE);
    }
    CView::OnMouseMove(nFlags, point);
}
  • 7.在OnLButtonUp函数里
void CRectangleSelectTriangleView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值

    m_IsLButtonDown = false;   // 标记一下此时鼠标左键已经弹起了

    if (m_IsReadyToMove)    // 如果可以开始移动
    {
        m_IsReadyToMove = false;    // 本次移动结束,标记不可以移动
        m_CurrentPen = &BlackPen;   // 修改画笔颜色为黑色
    }
    else // 不能移动,就开始判断当前画的矩形是否包含了三角形
    {

        if (IsInside())
        {
            m_IsReadyToMove = true;     // 标记可以移动
            m_CurrentPen = &RedPen;     // 修改画笔颜色为红色
        }
    }
    p0 = pm = 0;    // 清空本次矩形信息

    // 触发OnDraw函数,动态效果
    Invalidate(FALSE);
    CView::OnLButtonUp(nFlags, point);
}
  • IsInside函数的声明与定义
// 在CRectangleSelectTriangleView.h里CRectangleSelectTriangleView类内部public部分声明
bool CRectangleSelectTriangleView::IsInside();
// 在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
            )
            // 如果当前顶点在矩形内部,就继续判断下一个顶点
            continue;
        // 如果当前顶点不在矩形内部,直接返回 false
        return false;
    }
    // 三个点都在矩形内部,那么三角心就在矩形内部
    return true;
}

6.总结

  • 本人能力有限,在这篇文章里没有判断准备拖动时鼠标是否在三角形内
  • 需要的朋友请在下面评论,我会把代码奉上的

这里写图片描述

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值