14.4.3 使用鼠标绘图(6)
在初始化列表中调用CElement构造函数,以初始化钢笔的颜色和线宽。CElement构造函数还初始化了m_StartPoint,但后面要替换它。可以只绘制宽度和高度至少是2的CRect对象,在安排了定义规范化矩形的点后,在必要时调整两个定义点的坐标。由于调整了这些定义点,所以不需要调用NormalizeRect()。std::min()和std::max()是algorithm头文件中定义的模板函数,它们分别返回其参数的最小和最大值。在Rectangle.cpp中给algorithm头文件添加一个#include指令。
代码中围绕std::min和std::max的圆括号是必需的。在其他C++(www.cppentry.com)编译器中,这不常见,但它是Visual C++(www.cppentry.com)中的一种特殊情况。windows.h头文件为min和max定义了宏,且不带括号,在编译器启动之前,预处理器会替代这些宏。这会使编译器生成错误消息。圆括号可防止预处理器替代宏。
绘制矩形
CDC类的Rectangle()成员可以绘制矩形。这个函数将绘制闭合图形,然后利用当前画笔进行填充。您可能只想绘制矩形的轮廓,这时只要将NULL_BRUSH选入设备上下文即可。CDC还有一个函数PolyLine(),它根据一组点绘制由多条线段组成的形状,也可以使用LineTo()绘制矩形的4条边,但是最容易的方法是使用Rectangle()函数:
- // Draw a CRectangle object
- void CRectangle::Draw(CDC* pDC)
- {
- // Create a pen for this object and initialize it
- CPen aPen;
- CreatePen(aPen);
- // Select the pen and the null brush
- CPen* pOldPen = pDC->SelectObject(&aPen);
- CBrush* pOldBrush = dynamic_cast<CBrush*>(pDC->SelectStockObject(NULL_BRUSH));
- // Now draw the rectangle
- pDC->Rectangle(m_StartPoint.x, m_StartPoint.y,
- m_BottomRight.x, m_BottomRight.y);
- pDC->SelectObject(pOldBrush); // Restore the old brush
- pDC->SelectObject(pOldPen); // Restore the old pen
- }
在设置了钢笔和画笔以后,就调用Rectangle()函数,绘制矩形。其参数是矩形的左下角和右上角坐标。这个函数有一个重载版本,它把CRect对象作为参数来指定矩形。剩下的工作就是在绘制结束后,还原设备上下文的旧钢笔和画笔。
7. CCircle类
CCircle类的接口类似于CRectangle类。使用CDC类的Ellipse()成员可以绘制圆,这个成员绘制用一个矩形来界定的椭圆。只要该边界矩形是正方形,该椭圆就是圆。圆用两个定义点来定义,分别是圆心和圆周上的一点。所以这个类的定义是:
- class CCircle: public CElement
- {
- public:
- virtual ~CCircle(void);
- virtual void Draw(CDC* pDC) override; // Function to display a circle
- // Constructor for a circle object
- CCircle(const CPoint& start, const CPoint& end, COLORREF color);
- protected:
- CPoint m_BottomRight; // Bottom-right point for defining rectangle
- CCircle(void); // Default constructor - should not be used
- };
这段代码定义了一个公共构造函数,它使用绘图颜色,根据两个点创建圆,并且把无参数构造函数设置为protected,以防止外界使用它。另外在这个类定义中还添加了这个绘制圆的重载函数的声明。
实现CCircle类
在创建圆时,按下鼠标左键时的点将成为圆心,移动光标后,释放鼠标左键时的点将是圆周上的一个点。构造函数的工作是从这些点中获得边界矩形的角点。
CCircle类构造函数
释放鼠标左键时的点可以位于圆周上的任何地方,所以需要计算指定封闭矩形的点的坐标,如图14-15所示。

由图14-15可以看出,我们可以计算封闭矩形的左上角和右下角相对于圆心(x1,y1)的坐标。假设映射模式是MM_TEXT,计算(left,top)点的坐标时,只需要要从圆心的坐标中减去半径。类似地,把圆心的x和y坐标分别加上半径,就可以得到(right,bottom)点的坐标。半径可以计算为图14-15中表达式的平方根。因此,可以把CCircle类构造函数的代码编写为:
- // Constructor for a circle object
- CCircle::CCircle(const CPoint& start, const CPoint& end, COLORREF color) :
- CElement(start, color)
- {
- // Calculate the radius using floating-point values
- // because that is required by sqrt() function (in cmath)
- 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))));
- if(radius < 1L) radius = 1L; // Circle radius must be >= 1
- // Define left-top and right-bottom points of rectangle for MM_TEXT mode
- m_StartPoint = CPoint(start.x - radius, start.y - radius);
- m_BottomRight = CPoint(start.x + radius, start.y + radius);
- // Define the enclosing rectangle
- m_EnclosingRect = CRect(m_StartPoint.x, m_StartPoint.y,
- m_BottomRight.x, m_BottomRight.y);
- m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth);
- }