📄 curvectrl.cpp
字号:
/*==============================================================================================================================*
// 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 + -