MFC 动态曲线 支持缩放 显示图例(CStatic派生类)(续)

前面的文章已经介绍了在MFC环境下绘制动态曲线的方法(双缓冲绘图)和基本的步骤(分三步),以及用到的库函数。下面就介绍一下布局和鼠标响应事件,这里我做过一点

小小的创新(至少我没有看到有前人做过,哈哈。。)

(一)关于布局

   一般的绘制动态曲线的过程就是,先把坐标定下来,什么地方画轴,什么地方写字。当需要变更布局时,就有点麻烦了。当然也可以定义一些手动设置的属性来控制。本文借助

CSS的布局思想,创建了一些CRect的对象,每个对象绑定一个绘图元素。例如,

CRect m_rectCtrl;  // 获取整个控件的区域
CRect m_rectPlot;  // 用于绘制曲线的区域
CRect m_rectTitle; // 绘制标题的区域
CRect m_rectLegend;  // 绘制图例的区域

坐标轴、曲线、图例等元素都在指定的CRect绘制。同时定义一些属性用来控制几个CRect的相对位置,这样要改变布局只需要修正他们的相对位置即可,呵呵。。

(二)关于缩放

   由于真实系统中各点坐标都是浮点数,而MFC中CPoint的坐标是整数。所以一般是向直线中加入一个新的点是,都按一定的公式转化为CPoint的整数坐标值。但这样对缩放操作

不是很方便。本项目中,为每个直线对象(CLine类的对象,CLine是自定义的,它有很多和该线相关的属性,如线型、线宽、颜色等,同时包含一个点的数组,点的坐标都是float

型的)内部的点都以float型存储,在要绘制曲线时,临时申请一个CPoint型的数组,用于绘图。

  同时每个坐标轴(Axis类,也是自定义类)都带有指示该坐标轴表示范围的属性。在计算上述CPoint的值时,需要联合CLine和Axis两个属性值确定。当在进行缩放时,只需要改

变坐标轴的范围即可。当在进行下一次绘图时,计算的CPoint坐标值会自动改变。从而实现了缩放。

  但这也会带来一个问题,刚才已经说过,绘制曲线实在m_rectPlot矩形框中的。缩放时必然会有点的坐标超出这个矩形。出现如下的情况:

 

 

要解决这个问题可不容易,最简单的方法是,如果某一个点的某一维坐标值超出矩形,则就将其限定在矩形的那个边上,但这样图像看起来有点奇怪。

复杂的方法是,遍历所有的点,判断每个点是否在矩形内部,分多种情况

①如果上一点和该点都在矩形内部,则从上一个点LineTo()当前的点;

②如果上一个点不在矩形内,当前的点在矩形内部,或者当前的点不在矩形内部,而上一个点在矩形内部,则需要计算这两个点的连线与矩形的交点,并绘制

一条从交点到矩形内部那个点的直线;

③如果上一个点和当前的点都不在矩形内部,则什么也不做。

这里还需要自己写几个函数,

// 求两个矩形 rect1 和 rect2 的交集矩形
CRect IntersectionRect(const CRect& rect1,const CRect& rect2);
// 判断一个点是否在矩形上
BOOL PointOnRect(const CRect& rect,const CPoint& point );
// 判断一个点point是否在矩形rect内部
BOOL PointInRect(const CRect& rect,const CPoint& point );	
// 求两个点之间连线与rect边的交点
CPoint IntersectionRectLine(const CRect& rect,const CPoint& inPoint,const CPoint& exPoint );

这里面就有点算法的内容,当然我用的算法肯定不是最好的,还望广大网友指正,尤其是最后一个函数,情况有点复杂

    缩放后如何恢复呢?在这种结构下就非常简单了,用一个数据记录最初始的坐标轴范围,当鼠标双击时,将当前的坐标轴范围更改为最原始的那个范围,按照缩放的原理,自

然复原了。


(三)关于鼠标响应事件

   要实现更复杂的功能,离不开响应鼠标事件。当然“CStatic控件响应鼠标” ,Google一下可以得到完美解答,这里就顺便提及一下。也是最简单的方法。

①在“资源视图”中,找到你用于绘制曲线的CStatic控件,将属性“Notify”改为True;

②编写的CStatic继承类的.h文件中添加

DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDown(UINT nFlag,CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);

③在CStatic继承类的实现的.cpp文件中添加消息映射:

BEGIN_MESSAGE_MAP(CPlot, CStatic)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()

并实现上述三个函数。

OK,大功告成,MFC下绘制动态曲线的任务就完成啦,下面贴出.h文件,供大家参考,欢迎讨论。

完整资源请到 上一篇博文给出的地址进行下载。

#pragma once

#include <vector>

using namespace std;

namespace RealtimeCurve{
	const int MaxPointsCount = 512;	// 曲线最大存储点数
	const int MaxLinesCount = 10;	// 控件容纳最大曲线个数

	class CLine
	{
	public:
		CLine(CString name=_T(""),
			int lineStyle = PS_SOLID, 
			COLORREF lineColor = RGB(255,0,0),
			int lineWidth = 2);
		CLine(const CLine& nLine);
		CLine& operator = (const CLine& nLine);
		~CLine();
		
		_declspec(property(get = getLineName,put = setLineName))	CString lineName;
		_declspec(property(get = getShowStatus,put = setShowStatus)) BOOL showStatus;
		_declspec(property(get = getLineColor,put = setLineColor)) COLORREF lineColor;
		_declspec(property(get = getLineType,put = setLineType)) int lineType;
		_declspec(property(get = getLineWidth,put = setLineWidth)) int lineWidth;

		void	AddPoint(float x, float y);
		float	GetPointX(int nIndex);
		float	GetPointY(int nIndex);
		int		GetPointCount();
		
		inline CString	getLineName()				{	return this->LineName;	}
		inline void		setLineName(CString name)	{	this->LineName = name;	}
		inline BOOL		getShowStatus()				{	return this->IsShow;	}
		inline void		setShowStatus(BOOL showStatus){	this->IsShow = showStatus;	}
		inline COLORREF getLineColor()				{	return this->LineColor;}
		inline void		setLineColor(COLORREF color){	this->LineColor = lineColor;	};
		inline int		getLineType()				{	return this->LineType;}
		inline void		setLineType(int lineType)	{	this->LineType = lineType;	};
		inline int		getLineWidth()				{	return this->LineWidth;}
		inline void		setLineWidth(int lineWidth)	{	this->LineWidth = lineWidth;	};

	public:
		CString		LineName;		// 曲线名称
		BOOL        IsShow;			// 曲线是否显示
		COLORREF	LineColor;		// 颜色
		int         LineType;		// 线型 solid,dash
		int         LineWidth;		// 线宽 pixel
		struct Point{
			float x;
			float y;
		};
	private:		
		int			m_currentSize;		// 有效的坐标点数
		Point *		m_pointArrayPtr;	// 存储曲线中Point的数组
		CRITICAL_SECTION g_cs ;
	};

	class CAxis
	{
	
	public:		
		struct AxisRangeStruct{		// 坐标轴范围
			float AxisMinValue;		// 轴最小值
			float AxisMaxValue;		// 轴最大值
		};
		
		CAxis(COLORREF color = RGB(0,255,0),
				int style = PS_SOLID, 
				int width = 2,
				float minValue = 0.0f, 
				float maxValue = 500.0);
		~CAxis(){	}
		CAxis(const CAxis& axis);
		CAxis &operator = (const CAxis& axis);

		inline void SetAxisRange(float minValue=0.0f,float maxValue=500.0f){
			m_axisRange.AxisMinValue = minValue;
			m_axisRange.AxisMaxValue = maxValue;
		}
		inline float GetAxisRange(){	
			return this->m_axisRange.AxisMaxValue-this->m_axisRange.AxisMinValue;
		}
		inline float GetRangeLowerLimit()	{	return this->m_axisRange.AxisMinValue;	}
		inline float GetRangeUpperLimit()	{	return this->m_axisRange.AxisMaxValue;	}
			
	public:
		BOOL        IsShow;			// 是否显示坐标轴
		COLORREF	AxisColor;		// 颜色
		int         AxisStyle;		// 线型
		int         AxisWidth;		// 线宽		

		int			CoorTextBoxWidth;	// 坐标文本框 宽度
		int			CoorTextBoxHeight;	// 坐标文本框 高度
	private:
		AxisRangeStruct m_axisRange;	// 轴代表值的范围
	};

	class CPlot :
		public CStatic
	{
		DECLARE_DYNAMIC(CPlot)

	public:
		CPlot(void);
		virtual ~CPlot(void);

		void Start();
		void Stop();
	
		
		void AddNewLine(CString name = _T(""),
					int style = PS_SOLID, 
					COLORREF color = RGB(255,0,0), 
					int iThick = 2);
		void AddNewPoint(float x,float y,int nLineIndex);	//向某一条曲线中添加数据
		CLine* GetLineByIndex(int nIndex);	// 得到第nIndex(从0开始)条曲线的引用
		CLine* GetLineByName(CString name);	// 根据曲线名称获取曲线
		int    GetLineCount();				// 获取当前有效曲线个数

		void SetRate(int nRate);
		void SetBkColor(COLORREF clrBkColor);
		CAxis& GetAxisY();
		CAxis& GetAxisX();

	private:
		void RefreshLayout();			// 刷新页面布局
		void RefreshPlot(CDC* pDC);		// 刷新控件
		void DrawBackground(CDC* pDC);	// 绘制背景
		void DrawAxises(CDC* pDC);		// 绘制坐标轴
		void DrawGrids(CDC* pDC);		// 绘制网格
		void DrawLine(CDC* pDC,const CRect& rectZone,const CPoint* pPtr,int nCount);
		void DrawLines(CDC* pDC);		// 绘制全部曲线
		void DrawLegend(CDC* pDC,CRect& rectZone);		// 绘制图例
		void DrawTile(CDC* pDC,CRect& rectZone);		// 绘制标题

		void ScaleProcess();		// 放大操作
		
		// 求两个矩形 rect1 和 rect2 的交集矩形
		CRect IntersectionRect(const CRect& rect1,const CRect& rect2);
		// 判断一个点是否在矩形上
		BOOL PointOnRect(const CRect& rect,const CPoint& point );
		// 判断一个点point是否在矩形rect内部
		BOOL PointInRect(const CRect& rect,const CPoint& point );	
		// 求两个点之间连线与rect边的交点
		CPoint IntersectionRectLine(const CRect& rect,const CPoint& inPoint,const CPoint& exPoint );
	public:
		//float		XMoveStep;		// 每次移动X轴增加的值

		BOOL        ShowGrid;		// 是否显示网格
		COLORREF	GridColor;		// 网格颜色
		int			GridStyle;		// 网格线型
		int			GridWidth;		// 网格线宽
		int			GridSize;		// 网格宽度		
		int			ShowTextGrap;	// 每个多个格显示一个坐标值
		COLORREF	BkGndColor;		// 背景颜色
		int			RefreshRate;	// 刷新图像周期,ms
	private:
		BOOL	m_bAdjustable;		// 是否需要自动调整
		// 布局相关
		CRect	m_rectCtrl;                 // 控件矩形区域
		CRect	m_rectPlot;                 // 绘图矩形区域
		CRect	m_rectTitle;				// 标题矩形区域
		CRect	m_rectLegend;				// 图例矩形区域
		int		m_marginLeft;				// 左侧留白宽度
		int		m_marginRight;				// 右侧留白宽度
		int		m_marginTop;				// 上部留白宽度
		int		m_marginBottom;				// 下部留白宽度
		int		m_titleHight;				// 标题高度
		int		m_legendHeight;				// 图例高度
		//	坐标轴相关
		CAxis m_axisX;		// X 轴
		CAxis m_axisY;      // Y 轴

		float		m_vppXAxis;		// 每个像素代表的X轴值
		float		m_vppYAxis;		// 每个像素点代表的Y轴值
		// 曲线相关
		CLine * m_linePtrArray[MaxLinesCount];	// 指针数组
		int validLinesCount;					// 有效线的个数
		// 缩放相关
		float m_originXLwLmt,m_originXUpLmt;	// 已经在自适应情况下的X,Y轴范围
		float m_originYLwLmt,m_originYUpLmt;	
		CPoint m_startPoint,m_endPoint;			// 鼠标左键起止点

	public:
		DECLARE_MESSAGE_MAP()
		afx_msg void OnTimer(UINT nIDEvent);
		afx_msg void OnPaint();
		afx_msg BOOL OnEraseBkgnd(CDC* pDC);
		afx_msg void OnSize(UINT nType, int cx, int cy);
		afx_msg void OnLButtonDown(UINT nFlag,CPoint point);
		afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
		afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);	
	};
}


 

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值