0.背景
0.1问题的由来
视图里画图经常会有负坐标或者很大数值的坐标(地图),在处理的时候需要清楚DC的各种视口的意义,看了几天,头脑越看越混,视口啊,坐标零点啊什么的,一团乱麻,索性按照自己的想法来个简单的解决方案。
0.2几个概念
这是调整前的逻辑关系图,有负坐标,有超出客户区的坐标。
这是调整后的逻辑关系图,经过坐标平移和缩放,在客户区可以完整的显示整个图形。
0.3图形的数据
这是本例使用的数据在excel里画出来的图形。数据文件在这里可以下载。
0.4默认的结果
这是默认的不处理的结果,只有正值。
1.思路
1.1读取数据
读取所有坐标,存入CArray里去。
CStdioFile sfile;
CString s,sx,sy;
CPoint pt;
nXmin = 0;
nYmin = 0;
nXmax = 0;
nYmax = 0;
nXW = 0;
nYH = 0;
sfile.Open(_T("testdata.txt"),CFile::modeRead);
while(sfile.ReadString(s))
{
AfxExtractSubString(sx,s,0,'\t');
AfxExtractSubString(sy,s,1,'\t');
pt.x = _wtoi(sx);
pt.y = _wtoi(sy);
m_PTArray.Add(pt);
if (pt.x > nXmax)
{
nXmax = pt.x;
}
if (pt.x< nXmin)
{
nXmin = pt.x;
}
if (pt.y > nYmax)
{
nYmax = pt.y;
}
if (pt.y < nYmin)
{
nYmin = pt.y;
}
}
nXW = nXmax - nXmin;
nYH = nYmax - nYmin;
1.2缩放
根据图形的范围和客户区的范围xy的比例确定缩放比例,所有坐标按这个比例缩小。
CRect rc;
GetClientRect(&rc);
if ((double)nXW / (double)rc.Width() > (double)nYH / (double)rc.Height())
{
dRatio = (double)rc.Width() / (double)nXW ;
}
else
{
dRatio = (double)rc.Height() / (double)nYH ;
}
1.3平移
先把所有负坐标变为正,方法是所有坐标减去最小的负坐标。同时求得图形范围。
for (int i=0;i<m_PTArray.GetSize();i++)
{
m_PTArray.GetAt(i).x += -nXmin;
m_PTArray.GetAt(i).x *= dRatio;
m_PTArray.GetAt(i).y += -nYmin;
m_PTArray.GetAt(i).y *= dRatio;
}
1.4留出空白
所有坐标加上空白的宽度数值就可以。
2.工程设置及其他代码
2.1设置
1.MFC单文档工程
2.视图从ScrollView派生
2.2双缓冲绘图
定义
CDC* m_pMemoryDC;
CBitmap * m_pBitmap;
视图类构造函数里初始化
m_pMemoryDC = new CDC();
m_pBitmap = new CBitmap();
视图类析构函数里析构
delete m_pBitmap;
delete m_pMemoryDC;
响应视图类的OnPaint消息
CDC* pDC = GetDC();
CPoint m_ScrolPt = GetScrollPosition();
CRect rc;
GetClientRect(&rc);
m_pMemoryDC->CreateCompatibleDC(pDC);
m_pBitmap->CreateCompatibleBitmap(m_pMemoryDC,rc.Width(),rc.Height());
CBitmap * pOldbmp = m_pMemoryDC->SelectObject(m_pBitmap);
m_pMemoryDC->PatBlt(0,0,rc.right, rc.bottom,WHITENESS);//white background
m_pMemoryDC->MoveTo((m_PTArray.GetAt(0).x - m_ScrolPt.x) * m_dScale,
(m_PTArray.GetAt(0).y - m_ScrolPt.y) * m_dScale);
for (int i=1;i<m_PTArray.GetSize();i++)
{
m_pMemoryDC->LineTo((m_PTArray.GetAt(i).x - m_ScrolPt.x) * m_dScale,
(m_PTArray.GetAt(i).y - m_ScrolPt.y) * m_dScale);
}
pDC->BitBlt(0, 0, rc.right, rc.bottom, m_pMemoryDC, 0, 0, SRCCOPY);
m_pMemoryDC->SelectObject(pOldbmp);
m_pBitmap->DeleteObject();
m_pMemoryDC->DeleteDC();
2.3设置滚动条
SetScrollSizes(MM_TEXT, CSize(nXW * m_dScale * dRatio,nYH * m_dScale * dRatio));
2.4鼠标中键滚动缩放
定义缩放比尺
double m_dScale ;
初始化为1.0。
响应视图类的
BOOL CCoordinateView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// TODO: Add your message handler code here and/or call default
m_dScale += 0.05 * zDelta/ 120;
if(m_dScale <= 0.02)
{
m_dScale = 0.02;
}
if(m_dScale >= 4.0)
{
m_dScale = 4.0;
}
SetScrollSizes(MM_TEXT, CSize(nXW * m_dScale * dRatio,nYH * m_dScale * dRatio));
Invalidate();
return TRUE;/*CScrollView::OnMouseWheel(nFlags, zDelta, pt)*/;
}
3.结果
无缩放情况
缩放的情况
4.继续改进
GDI里画图的坐标只能是int型,但在很多图像里画的都是float型,解决方法就是使用Direct2D。后续结合2者进行程序设计。