14.4.3 使用鼠标绘图(5)
调用继承的CreatePen()函数,会在aPen中建立钢笔。将钢笔选择到设备上下文中后,就把当前位置移动到直线的起点,这个点定义在m_StartPoint数据成员中,然后从这个点将直线绘制到m_EndPoint。最后在设备上下文中还原旧钢笔,绘图到此结束。
创建边界矩形
乍一看,获得一个形状的边界矩形好像很简单。例如,直线始终是封闭矩形的对角线,圆由它的封闭矩形定义,但还是有一些稍微复杂的问题。形状必须完全在封闭矩形的内部;否则,部分形状可能无法绘制出来,所以在创建边界矩形时,必须考虑形状如何相对于其定义参数来绘制,以及绘图时使用的线宽度。此外,如何调整定义边界矩形的坐标取决于映射模式,所以也必须考虑这个方面。
在计算不同映射模式中边界矩形的坐标时,计算方法之间的区别只与y坐标有关;x坐标的计算对于所有映射模式都一样:都是从矩形左上角的x坐标中减去线宽,再将它加到矩形右下角的x坐标中。在MM_TEXT映射模式中,要从定义矩形的左上角的y坐标中减去线宽,但是在MM_LOENGLISH映射模式(以及其他所有映射模式)中,由于y轴在相反的方向上增大,因此需要在定义矩形的左上角的y坐标中加上线宽。下面要在每种形状的构造函数中创建该形状的边界矩形。
需要提醒的是,CRect对象的各个数据成员是left和top (分别存储左上角的x和y坐标)以及right和bottom (分别存储右下角的x和y坐标)。这些数据成员都是公共成员,所以可以直接访问它们。一个常见的错误是把坐标对写成(top, left),而不是正确的顺序(left, top)。
规范化的矩形
规范化矩形的left值小于right值,top值小于bottom值。如果矩形不是规范化的,在CRect对象上执行的函数就不会正确执行。例如,CRect类的InflateRect()成员函数从矩形的top和left成员中减去参数值,给bottom和right成员加上这些值。这意味着,如果矩形不是规范化的,对它应用InflateRect(),矩形实际上有可能缩小。只要矩形是规范化的,则无论在什么映射模式中,InflateRect()函数都可以正常工作。通过调用CRect对象的NormalizeRect()成员,可以确保这个对象是规范化的。
5. 计算直线的封闭矩形
现在,在CLine构造函数内编写计算封闭矩形的代码:
- CLine::CLine(const CPoint& start, const CPoint& end, COLORREF color):
- CElement(start, color), m_EndPoint(end)
- {
- // Define the enclosing rectangle
- m_EnclosingRect = CRect(start, end);
- m_EnclosingRect.NormalizeRect();
- m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth);
- }
调用m_EnclosingRect对象的NormalizeRect()成员,则无论直线的起点和终点的相对位置如何,都能确保边界矩形的left和top值分别小于right和bottom值。调用InflateRect(),使边界矩形的尺寸增大一个线宽。第一个参数是左边和右边的调整量,第二个参数是顶部和底部的调整量。InflateRect()的一个重载版本只有一个SIZE类型的参数,它也接受CSize对象,该对象封装了SIZE结构。CSize对象通过long类型的公共成员cx和cy定义了大小。
6. CRectangle类
在定义矩形对象时使用的数据和定义直线时相同-- 矩形对角点,可以把CRectangle类定义为:
- class CRectangle: public CElement
- {
- public:
- virtual ~CRectangle(void);
- virtual void Draw(CDC* pDC) override; // Function to display a rectangle
- // Constructor for a rectangle object
- CRectangle(const CPoint& start, const CPoint& end, COLORREF color);
- protected:
- CPoint m_BottomRight; // Bottom-right point for the rectangle
- CRectangle(void); // Default constructor - should not be used
- };
无参数构造函数现在是protected类型,以防止外界使用它。这个矩形的定义非常简单,只包括一个构造函数、一个虚函数Draw()和这个类的protected部分中的一个无参数构造函数。
CRectangle类构造函数
新的CRectangle类构造函数的代码比Cline复杂一些:
- // CRectangle constructor
- CRectangle:: CRectangle (
- const CPoint& start, const CPoint& end, COLORREF color) :
- CElement(start, color)
- {
- // Normalize the rectangle defining points
- m_StartPoint = CPoint((std::min)(start.x, end.x),(std::min)(start.y, end.y));
- m_BottomRig ht = CPoint((std::max)(start.x, end.x), (std::max)(start.y, end.y));
- // Ensure width and height between the points is at least 2
- if((m_BottomRight.x - m_StartPoint.x) < 2)
- m_BottomRight.x = m_StartPoint.x + 2;
- if((m_BottomRight.y - m_StartPoint.y) < 2)
- m_BottomRight.y = m_StartPoint.y + 2;
- // Define the enclosing rectangle
- m_EnclosingRect = CRect(m_StartPoint, m_BottomRight);
- m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth);
- }