⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 curvectrl.cpp

📁 功能非常强大的数据采集系统
💻 CPP
📖 第 1 页 / 共 4 页
字号:
/*==============================================================================================================================*
// CurveCtrl.cpp											   
// 
// Author:		  Sunjoy Chen
// Email(MSN):    cfd@dl.cn
// Copyright 2004, Sunjoy Chen.
//
// Permission to use, copy, modify, distribute for any purpose is hereby
// granted without fee, provided that the above copyright notice and
// this permission notice included in all derived versions. 
// I will be glad to know you using it, let me know by email, and 
// your bugs report are also welcome. 
// This file is provided "as is" with no expressed or implied warranty.
//
// Special thanks to Chris Maunder(for his CGridCtrl which I had learned much from).
//
// History: 
//     09/09/2004 ~ 09/17/2004  basic function finished
//	   09/27/2004  	            select/deselect curve
//	   11/11/2004               bugs: disable edit if no curve selected;
//	                                  send messages if multi curves selected/deselected.
==============================================================================================================================*/
	   
#include "stdafx.h"
#include "CurveCtrl.h"
#include "MemDC.h"
 
#include <FLOAT.h>
#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define CURVE_EPSILON       FLT_EPSILON									//浮点数的最大值

const int CURVE_NEAR_RANGE	= 4;										//选择曲线范围:邻近4点

//-------------------------------------------------------------------------------------------------
// CCurve类
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
// CCurve:构造函数
//-------------------------------------------------------------------------------------------------
CCurve::CCurve()
{
	m_crColor = RGB(0, 0, 255);											//定义绘图颜色:红色;
	m_iStyle  = PS_SOLID;												//定义绘图类型:实线;
	m_nWidth  = 1;														//定义曲线线宽:1点;
	m_bSelected = FALSE;												//曲线选择标志:有效/无效;
	m_bVisible = TRUE;													//显示/不显示标志
	m_strName.Empty();													//临时变量
}

//-------------------------------------------------------------------------------------------------
// CCurve:析构函数
//-------------------------------------------------------------------------------------------------
CCurve::~CCurve()
{

}

//-------------------------------------------------------------------------------------------------
// 功能:
// description  : to judge if there is any key point near given point 
//                or if the given point on the line segment between neighbor key points
// in parameter : point  
// out parameter: iIndex -- index of key point if there is
//-------------------------------------------------------------------------------------------------
BOOL CCurve::IsPointNearCurve(const CPoint& point, int& iIndex)
{
	iIndex = -1;
	int nCount = m_ArrPoint.GetSize();
	if (nCount < 1)
		return FALSE;
	
	// m_ArrPoint is already sorted
	for (int iPt = 0; iPt < nCount; iPt++)
	{
		if (point.x < m_ArrPoint[iPt].x)
		{			
			break;
		}
	}

	// to check if point before the first one or after the last one
	if (iPt == 0 || iPt == nCount) 
	{
		if (iPt == nCount)
			iPt--;
		if (Distance(point, m_ArrPoint[iPt]) < CURVE_NEAR_RANGE)
		{
			iIndex = iPt;
			return TRUE;
		}
		else
			return FALSE;		
	}	

	// to check if point near next key point in  m_ArrPoint
	if (Distance(point, m_ArrPoint[iPt]) < CURVE_NEAR_RANGE)
	{
		iIndex = iPt;
		return TRUE;
	}
	// to check if point near previous key point in  m_ArrPoint
	if (Distance(point, m_ArrPoint[iPt - 1]) < CURVE_NEAR_RANGE)
	{
		iIndex = iPt - 1;
		return TRUE;
	}

	// to check if point near the line defined by two key points:m_ArrPoint[iPt] and m_ArrPoint[iPt - 1]
	if (abs(m_ArrPoint[iPt].x - m_ArrPoint[iPt - 1].x) < CURVE_NEAR_RANGE)
	{		
		if ((point.y > m_ArrPoint[iPt].y && point.y < m_ArrPoint[iPt - 1].y)
			|| (point.y < m_ArrPoint[iPt].y && point.y > m_ArrPoint[iPt - 1].y))
		{
			return TRUE;
		}
		else 
			return FALSE;
	}
	else // the line defined by two key points:y = k * x + b
	{
		// k = (y2 - y1) / (x2 - x1)
		float k = float(m_ArrPoint[iPt].y - m_ArrPoint[iPt - 1].y) 
			      / float(m_ArrPoint[iPt].x - m_ArrPoint[iPt - 1].x);
		// b = (x2 * y1 - x1 * y2) / (x2 - x1)
		float b = float(m_ArrPoint[iPt].x * m_ArrPoint[iPt - 1].y - m_ArrPoint[iPt - 1].x * m_ArrPoint[iPt].y) 
			      / float(m_ArrPoint[iPt].x - m_ArrPoint[iPt - 1].x);
		
		float y = k * point.x + b;
		if (abs(int(y - point.y)) < CURVE_NEAR_RANGE)
			return TRUE;
	}		

	return FALSE;
}

// description  : static function, to calculate distance between two points
// in paramenter: pt1, pt2
float CCurve::Distance(const CPoint& pt1, const CPoint& pt2)
{
	return (float)sqrt((pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y));
}

//-------------------------------------------------------------------------------------------------
// CCurveCtrl类
//-------------------------------------------------------------------------------------------------
IMPLEMENT_DYNCREATE(CCurveCtrl, CWnd)

//-------------------------------------------------------------------------------------------------
// CCurveCtrl:析构函数
//-------------------------------------------------------------------------------------------------
CCurveCtrl::CCurveCtrl()  
{
    RegisterWindowClass();

    _AFX_THREAD_STATE* pState = AfxGetThreadState();
    if (!pState->m_bNeedTerm && !AfxOleInit())
	{
		AfxMessageBox(_T("OLE initialization failed. Make sure that the OLE libraries are the correct version"));
	}

// init:
	m_fHoriMax = m_fVertMax = -FLT_MAX / 2;
	m_fHoriMin = m_fVertMin = FLT_MAX / 2;

	m_Margin = CRect(80, 50, 70, 40);
	m_crBack = RGB(255, 255, 255);		// white
	m_crGridLine = RGB(192, 192, 192);
	m_iGridLineStyle = PS_DOT;			// as: - - - - - 

	m_bShowCross	 = FALSE;
	m_crAxis = RGB(0, 0, 0);			// black

	m_bEdit   = FALSE;
	m_bVert = false;													//纵轴数据重新计算标志
	m_pCurveEdit = NULL;
	m_iCurPoint  = -1;
	m_strHoriLabel = _T("");
	m_strVertLabel = _T("");

	m_iZoom = 0;
}

//-------------------------------------------------------------------------------------------------
// CCurveCtrl:析构函数
//-------------------------------------------------------------------------------------------------
CCurveCtrl::~CCurveCtrl()
{
	RemoveAll();														// free memory
}

//-------------------------------------------------------------------------------------------------
// 消息映射
//-------------------------------------------------------------------------------------------------
BEGIN_MESSAGE_MAP(CCurveCtrl, CWnd)
	//{{AFX_MSG_MAP(CCurveCtrl)
	ON_WM_PAINT()
	ON_WM_MOUSEMOVE()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONDOWN()
	ON_WM_MOUSEWHEEL()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//-------------------------------------------------------------------------------------------------
// 函数定义
//-------------------------------------------------------------------------------------------------
 
//-------------------------------------------------------------------------------------------------
// 功能:create this window, use like any other window create control
// 参数: rect       -- window rect
//       pParentWnd -- pointer of parent window 
//       nID        -- resource ID
//		 dwStyle    -- style
//-------------------------------------------------------------------------------------------------
BOOL CCurveCtrl::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
{
    ASSERT(pParentWnd->GetSafeHwnd());

    if (!CWnd::Create(CURVECTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
        return FALSE;

	//Add ToolTips
	if (!m_Tooltip.Create(this))
		TRACE(_T("Unable to create tip window for CCurveCtrl."));
	else if (!m_Tooltip.AddTool(this, _T("Control ToolTips")))
		TRACE(_T("Unable to add tip for the control window for CCurveCtrl."));
	else
		m_Tooltip.Activate(TRUE);

    return TRUE;
}

//-------------------------------------------------------------------------------------------------
// 功能:
//-------------------------------------------------------------------------------------------------
BOOL CCurveCtrl::RegisterWindowClass()
{
    WNDCLASS wndcls;
    HINSTANCE hInst = AfxGetInstanceHandle();

    if (!(::GetClassInfo(hInst, CURVECTRL_CLASSNAME, &wndcls)))
    {
        // otherwise we need to register a new class
        wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        wndcls.lpfnWndProc      = ::DefWindowProc;
        wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
        wndcls.hInstance        = hInst;
        wndcls.hIcon            = NULL;
        wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
        wndcls.lpszMenuName     = NULL;
        wndcls.lpszClassName    = CURVECTRL_CLASSNAME;

        if (!AfxRegisterClass(&wndcls))
        {
            AfxThrowResourceException();
            return FALSE;
        }
    }

    return TRUE;
}

//-------------------------------------------------------------------------------------------------
// 功能:绘制窗口图形(每次)
//-------------------------------------------------------------------------------------------------
void CCurveCtrl::OnPaint() 
{
	CPaintDC dc(this); 
 	CMemDC memdc(&dc);													// use CMemDC to avoid flicker

	CRect rect;
	GetClientRect(&rect);

	// background and margin    
	CBrush bkBrush(m_crBack);
	CBrush* pOldBrush = memdc.SelectObject(&bkBrush);	
 	memdc.FillRect(rect, &bkBrush);
	rect.InflateRect(1, 1, 0, 0);
	memdc.DrawEdge(&rect, EDGE_ETCHED, BF_TOPLEFT);
	memdc.SelectObject(pOldBrush);

	m_RectCoord = rect;													//获得矩形区(客户区)
	m_RectCoord.DeflateRect(m_Margin);									//缩减到绘图区
	if(m_bVert)															//纵轴需要重新计算?
		ReCalcAllVert();												//重新计算Y轴数据范围
 	DrawGrid(&memdc, m_RectCoord);										//绘制背景网格
	ReCalcAllPoint();													//计算绘制点(must do this before DrawCurve) 
	DrawCurve(&memdc);													//绘制曲线
	DrawCross(&memdc);													//绘制交叉线
}

//-------------------------------------------------------------------------------------------------
// 功能:draw grid line(画曲线网格)
// 参数: pdc  -- pointer of CDC(绘图设备类指针)
//       rect -- paint area(绘图区)
//-------------------------------------------------------------------------------------------------
void CCurveCtrl::DrawGrid(CDC *pdc, const CRect& rect)
{
	CPen	penStroke;													//pen for drawing grid line
	CPen	penGrid;													//pen for 
	
	//cells in horizontal and vertical, may be as member viariables of CCurveCtrl
	int		nGrid = 10;													//水平网格数量
	int		nVertGrid = 10;												//垂直网格数量

	CPen	penRect;													//pen for drawing
	penRect.CreatePen(0, 1, m_crAxis);									//CreatePen( int nPenStyle, int nWidth, COLORREF crColor)
	CPen	*pOldPentmp = pdc->SelectObject(&penRect);					//

	CBrush	bkBrush(m_crBack);											//
	CBrush* pOldBrush=pdc->SelectObject(&bkBrush);						//
		
	// draw rectangle aera contains all(绘制矩形)
	CRect	rctmp = rect;												//获得矩形区
	rctmp.DeflateRect(-1, -1, -1, -1);									//四边各扩大1点
	pdc->Rectangle(&rctmp);												//绘制矩形

	//draw arrow(绘制箭头)
	pdc->MoveTo(rect.left-1, rect.top);									//y轴
	pdc->LineTo(rect.left-1, rect.top-30);
	pdc->LineTo(rect.left-5, rect.top-20);
	pdc->MoveTo(rect.left-1, rect.top-30);
	pdc->LineTo(rect.left+3, rect.top-20);
	pdc->MoveTo(rect.right, rect.bottom);								//x轴
	pdc->LineTo(rect.right+40, rect.bottom);
	pdc->LineTo(rect.right+30, rect.bottom+4);
	pdc->MoveTo(rect.right+40, rect.bottom);
	pdc->LineTo(rect.right+30, rect.bottom-4);
	
	pdc->SelectObject(pOldBrush);
 	pdc->SelectObject(pOldPentmp);

	penGrid.CreatePen(0, 1, m_crGridLine);
	CPen *pOldPen=pdc->SelectObject(&penGrid);

	penStroke.CreatePen(m_iGridLineStyle, 1, m_crGridLine);				//生成笔的属性点画线、灰色
	pdc->SelectObject(&penStroke);										//设置绘图属性

	// to draw grid line (绘制网格线)
	for(int i = 1; i < nVertGrid; i++)									// in horizontal
	{
		pdc->MoveTo(rect.left, rect.top + i * rect.Height() / nVertGrid);
		pdc->LineTo(rect.right, rect.top + i * rect.Height() / nVertGrid);
	}

	for(int k = 1; k < nGrid; k++)										// in vertical
	{
		pdc->MoveTo(rect.left + rect.Width() * k / nGrid, rect.bottom);
		pdc->LineTo(rect.left + rect.Width() * k / nGrid, rect.top);
	}
	
	// draw label in horizontal(绘制X轴单位标记)
	pdc->SetTextColor(m_crAxis);										// the same color as axis' color
	pdc->SetBkMode(TRANSPARENT);										//
	int nTextHei;
	nTextHei = pdc->GetTextExtent(m_strHoriLabel).cy;					// length of horizontal label

	CRect rectLabel;													//绘制位置
	rectLabel.left   = m_RectCoord.right+34;							//左
	rectLabel.right  = rectLabel.left+60;								//右
	rectLabel.top	 = m_RectCoord.bottom + 7;							//顶
	rectLabel.bottom = rectLabel.top + 16;								//底
	pdc->DrawText(m_strHoriLabel, &rectLabel, DT_LEFT|DT_SINGLELINE);	//绘制单位标记(如t、m等)

	// draw X-scale(绘制水平数字)
	if (m_ArrCurve.GetSize())											//有曲线绘图?
	{																	//有
		CString		strScale;											//绘制的字符变量
		float		fDlt = (m_fHoriEnd - m_fHoriBegin) / nGrid;			//每格的数量	

		if (fabs(fDlt) > CURVE_EPSILON)									//大于浮点数最小分别率
		{
			for (int iScale = 0; iScale <= nGrid; iScale++)				//循环计数
			{
				strScale.Format(_T("%d"), int(10*(m_fHoriBegin + fDlt * iScale)) / 10 );
																		//计算显示数字,并转换为字符串
				CSize szLabel = pdc->GetTextExtent(strScale);			//获得字符串的长度和宽度
				if (szLabel.cx * (nGrid + 1) > m_RectCoord.Width() && (iScale % 2) == 1 )
					continue;
				pdc->TextOut(m_RectCoord.left + iScale * m_RectCoord.Width() / nGrid - szLabel.cx / 2, m_RectCoord.bottom + szLabel.cy / 2, strScale);
																		//绘制水平数字字符串
			}
		}
	}

	// draw label in vertical(绘制Y轴单位标记) 
	pdc->TextOut(m_RectCoord.left +6, m_RectCoord.top + -30, m_strVertLabel);
																		//绘制单位标记(如实验物理量等)
	// draw Y-scale(绘制垂直数字)
	if (m_ArrCurve.GetSize())											//有曲线绘图?
	{																	//有
		CString   strScale;												//绘制的字符变量
		float	  fDlt = (m_fVertMax - m_fVertMin) / nGrid;				//每格的数量

		if (fabs(fDlt) > CURVE_EPSILON)									//大于浮点数最小分别率	
		{
			for (int iScale = 0; iScale <= nVertGrid; iScale++)			//循环计数
			{
				//计算显示数字,并转换为字符串
				if(fDlt<0.01)											//每格间隔<0.01
					strScale.Format(_T("%.4f"), int(0.5 + 10000 * (m_fVertMin + fDlt * iScale)) / 10000.0f); 
				else
				{
					if(fDlt<1)											//每格间隔<1
						strScale.Format(_T("%.2f"), int(0.5 + 100 * (m_fVertMin + fDlt * iScale)) / 100.0f); 
					else												//每格间隔>=1
						strScale.Format(_T("%d"), int(0.5 + m_fVertMin + fDlt * iScale)); 
				}
				CSize szLabel = pdc->GetTextExtent(strScale);			//获得字符串的长度和宽度
				if (szLabel.cy * (nVertGrid + 1) > m_RectCoord.Height() && (iScale % 2 == 1))
					continue;
				pdc->TextOut(m_RectCoord.left - szLabel.cx - 2, m_RectCoord.bottom - m_RectCoord.Height() * iScale/ nVertGrid - szLabel.cy / 2, strScale);
																		//绘制垂直数字字符串
			}
		}
	}
	
	pdc->SelectObject(pOldPen);											//恢复绘图笔的类型
}

//-------------------------------------------------------------------------------------------------

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -