14.4.3 使用鼠标绘图(8)
可以在Curve.cpp文件中为构造函数添加如下定义:
- // Constructor for a curve object
- CCurve::CCurve(const CPoint& first, const CPoint& second, COLORREF color) :
- CElement(first, color)
- {
- // Store the second point in the vector
- m_Points.push_back(second);
- m_EnclosingRect = CRect(
- (std::min)(first.x, second.x), (std::min)(first.y, second.y),
- (std::max)(first.x, second.x), (std::max)(first.y, second.y));
- m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth);
- }
构造函数的参数包括前两个定义点,因此构造函数定义了一个只有一条线段的曲线。第一个点由CElement构造函数存储在m_StartPoint成员中,第二个点存储在m_Points矢量中。push_back()函数可以在矢量末尾添加元素。创建封闭矩形时,使用了std::min()和std::max()模板函数。所以需要在Curve.cpp文件中,给algorithm头文件添加一个#include指令。
绘制曲线的Draw()函数可以定义为:
- // Draw a curve
- void CCurve::Draw(CDC* pDC)
- {
- // Create a pen for this object and initialize it
- CPen aPen;
- CreatePen(aPen);
- CPen* pOldPen = pDC->SelectObject(&aPen); // Select the pen
- // Now draw the curve
- pDC->MoveTo(m_StartPoint);
- for(auto& point : m_Points)
- pDC->LineTo(point);
- pDC->SelectObject(pOldPen); // Restore the old pen
- }
Draw()函数必须为曲线提供任意数量的点。一旦设置了钢笔,第一步就是在设备上下文中将当前位置移动到m_StartPoint。曲线的各个线段在for循环中绘制,每条线段对应于矢量m_Points中的一个点。LineTo()操作将当前位置移动到它绘制的直线的末端,这里每次调用函数时都从当前位置到矢量中的下一个点绘制一条直线。按照这种方式,按顺序绘制组成曲线的所有线段。
可以如下面这样实现AddSegment()函数:
- // Add a segment to the curve
- void CCurve::AddSegment(const CPoint& point)
- {
- m_Points.push_back(point); // Add the point to the end
- // Modify the enclosing rectangle for the new point
- m_EnclosingRect.DeflateRect(m_PenWidth, m_PenWidth);
- m_EnclosingRect = CRect((std::min)(point.x, m_EnclosingRect.left),
- (std::min)(point.y, m_EnclosingRect.top),
- (std::max)(point.x, m_EnclosingRect.right),
- (std::max)(point.y, m_EnclosingRect.bottom));
- m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth);
- }
这段代码给矢量添加point,并调整封闭矩形,确保左上角的点是最小的x和最小的y,右下角的点是最大的x和最大的y。完整曲线的封闭矩形的左上角是从所有定义点中获得的(最小x, 最小y)对,右下角是从所有定义点中获得的(最大x, 最大y)对。接着要将得到的矩形放大一个线宽。因此,必须将m_EnclosingRect减小一个线宽,才能找到左上角和右下角的坐标。否则就得不到正确的封闭矩形。调用CRect的DeflateRect()成员函数,会执行与InflateRect()相反的操作,所以这里需要调用DeflateRect()。
9. 完成鼠标消息处理程序
现在可以回到WM_MOUSEMOVE消息处理程序,填充一些细节。在Class View窗格中选择CSketcherView,然后双击处理程序名OnMouseMove(),就可以找到这个消息处理程序。
鼠标进行移动时,这个处理程序只绘制元素的一系列临时版本,因为最终的元素是在释放鼠标左键时创建的。因此,可以把像拉橡皮筋那样绘制临时元素的过程看作是这个函数的局部操作,而由视图的OnDraw()函数成员绘制元素的最终版本。这种方法使橡皮筋元素的绘制变得非常有效率,因为它不涉及CSketcherView中最后负责绘制完整文档的OnDraw()函数。
利用CDC类的一个成员SetROP2(),很容易实现这种方法,这个成员函数特别适合于橡皮筋操作。
设置绘图模式
在与CDC对象相关联的设备上下文中,SetROP2()函数为所有后续输出操作设置绘图模式。这个函数名中的"ROP"代表光栅操作(Raster OPeration),因为绘图模式的设置将应用于光栅显示器。可能您要问,"SetROP1()是什么呢?"-- 没有这种函数。SetROP2()这个函数名表示Set Raster OPeration to(设置光栅操作为),不是数字2!