小注:这是一次计算机图形学的课后作业,作业原文:给定三角形(顶点坐标自己给定,不一定非要画三角形,画多于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 (矩形包含了三角形)
{
标记可以移动;
修改画笔颜色为红色;
}
清空本次矩形信息;
}
}
bool m_IsLButtonDown; // 左键是否按下
bool m_IsReadyToMove; // 是否可以开始移动
CPoint p0; // 记录矩形框左上角坐标
CPoint pm; // 记录矩形右下角坐标
CPoint p1; // 移动三角形的时候会用到
CPen * m_CurrentPen; // 指向当前画笔,初始化指向黑色画笔
CPoint m_Points[3]; // 保存三角形的三个顶点
WM_LBUTTONDOWN
WM_MOUSEMOVE
WM_LBUTTONUP
至此,我们已经完成了基础的“框架”
接下来就是代码部分了
按照我们的伪代码思路,一步一步来
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));
#define Trans(p1, rect) CPoint(long((p1.x+0.5)/1) - rect.Width()/2, long((p1.y+0.5)/1) - rect.Height()/2)
//初始不可以移动
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;
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);
}
// 在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);
}
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);
}
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);
}
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);
}
// 在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;
}