科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件VC6.0映射模式转换及如何消除坐标误差

VC6.0映射模式转换及如何消除坐标误差

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

vc编程中的映射模式介绍以及如何消除设备坐标和逻辑坐标之间的误差。

作者:刘 涛 来源:yesky 2007年10月22日

关键字: VC6.0 映射模式 转换 坐标误差

  • 评论
  • 分享微博
  • 分享邮件
在实际项目的开发过程中,经常需要绘制几何图形,并且要求用户可以与图形进行交互,既用户可以按照自己的思路对图形进行局部的任意调整,这些问题在Visual C++ 6.0可视化编程中可以很容易地解决,但是笔者在处理用户交互问题上,发现在坐标映射模式下,设备坐标和转换后的逻辑坐标有些偏差,不能一致地对应起来,通过仔细研究,最终很好的解决了这个问题,本文将解决该问题的方法拿出来与读者朋友们一起分享。

  一、Visual C++编程映射模式简介

  在一般的情况之下进行绘图操作都是以像素作为绘图单位,也就是所谓的设备坐标。但是为了更好的反映实际设计的对象的尺寸大小,因此多采用与对象的实际尺寸成一定比例的绘图映射模式。

  WINDOWS提供了几种映射方式,可以实现对应的逻辑坐标系。例如,如果我们指定了MM_TEXT方式,那么坐标系的坐标原点就位于屏幕的左上角,X轴和Y轴的正方向分别指向我们面对屏幕的右方和下方,它的绘图单位是像素,这时候逻辑坐标和设备坐标是一致的;如果我们指定了MM_LOENGLISH方式,那么一个逻辑坐标的单位就是百分之一英寸,坐标原点仍然位于屏幕的左上角,但是X轴和Y轴 的方向恰好和MM_TEXT方式下的轴方向相反。我们可以调用CDC的SetMapMode()来设置逻辑坐标系统,也可以调用CDC的SetViewpotOrg()函数来改变逻辑系的坐标原点,另外,结合CDC的SetWindowExt()用户可以定义自己的逻辑坐标系。对于这些函数的用法和Windows提供的一些逻辑坐标映射模式,不是本文的重点,读者可以自己参考微软的MSDN。

  对于坐标的映射模式,Visucal C++中虽然提供的许多MFC库函数,却只接受设备坐标,对于这些函数不能使用逻辑坐标,否则就会发现结果和自己要实现的目标出现误差。

  二、映射模式转换的坐标偏差的消除

  在绘图过程中,首先要设置逻辑坐标模式,例如,为了画一个边长为2厘米的正方形,首先在程序视图类CTestView::OnDraw()函数中将映射模式设置为MM_LOMETRIC,然后用CDC的Rectangle()函数绘制一个矩形,左上角和右下角的坐标分别为(200,-200),(400,-400)(MM_LOMETRIC模式下逻辑坐标的X轴向右为正,Y轴向上为正,坐标原点为屏幕的左上角,逻辑坐标的单位为0.1毫米)。代码如下:

void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->SetMapMode(MM_LOMETRIC);//设置逻辑坐标的模式;
pDC->Rectangle (200,-200,400,-400);//以逻辑坐标为单位;
}

  在实现交互式绘图过程中,要求用户采用鼠标在屏幕上对图形进行拾取,记录拾取点的坐标并进行处理和运算,因为当采用鼠标在逻辑坐标系统绘制出的图形上交互拾取时,其拾取点的坐标只能按照缺省设备坐标表示,也既是以像素单位表示,为了解决这个问题,就需要将拾取点的坐标转换到逻辑坐标下的坐标值。现在希望通过编程实现在该映射模式下点击鼠标后,以该位置为圆心,画一个半径为2厘米的圆。为此,利用ClassWizard创建CdrawingView类的响应鼠标左键按下的函数OnLButtonDown,在其中添加如下语句:

void CTestView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
double scale;
CClientDC *pDC=new CClientDC(this);
pDC->SetMapMode(MM_LOMETRIC);//设置逻辑坐标系统;
scale=12.0/800*25.4*10;//计算坐标转换的系数;
pDC->Ellipse(point.x*scale-200,-point.y*scale+200,point.x*scale+200,-point.y*scale-200);
//以用户拾取的设备坐标点为基础,计算对应的逻辑坐标点,并以该点为圆心作圆;
pDC->SetMapMode(MM_TEXT);//恢复为设备坐标模式;
pDC->MoveTo(point.x-5,point.y);//标记出鼠表标点击的位置;
pDC->LineTo(point.x+5,point.y);
pDC->MoveTo(point.x,point.y-5);
pDC->LineTo(point.x,point.y+5);
}

  在上面的程序段中,CClientDC为CDC的派生类,提供了一个用于画图的设备上下文对象,用来立即响应鼠标事件进行绘图:以鼠标的拾取点为圆心,以2厘米为半径,在屏幕上画一个圆。由于作图在MM_LOMETRIC映射模式下完成,所以它的逻辑单位为0.1毫米。程序中的scale既是屏幕拾取点坐标(以像素为单位)向MM_LOMETRIC映射模式下的逻辑坐标的转换系数。对于它的实现解释如下:要求将以像素表示的拾取点坐标换算成以0.1毫米为单位逻辑坐标系下的坐标,既要找出屏幕上每一个像素点代表多少个0.1毫米,也可以理解为像素之间的距离为多长(以毫米为单位)。

  由于笔者开发的程序运行在Windows 2000下,屏幕显示器设置为800*600像素,显示器的尺寸为15英寸(这个尺寸为屏幕对角线的长度),所以推断出显示器的长宽尺寸为12*9英寸(长宽比为4:3),每英寸相当于25.4毫米,所以每个像素相当于12*25.4*10/800个0.1毫米,这个结果就是我们要得到的转换系数scale。需要提醒的一点是,由于逻辑坐标的Y轴以向上为正,所以在对Y轴坐标进行换算时,需要再乘上个-1。采用该方法作出的圆的实际圆心位置和鼠标按下去的位置有明显的偏差,它的效果图如下:

  

  图中"十"字星表示用户鼠标按下后的位置,圆为经过坐标转换后得到的结果,从效果图可以看出,采用上述办法进行坐标变换存在一定的问题,笔者分析原因可能是显卡上的设备单位的尺寸有一定的增量,不能完全反映相应的映射关系以及屏幕不是纯平造成的。

  笔者经过多次实验,发现使用GetDeviceCaps()函数可以很好的消除这种误差。GetDeviceCaps(int nIndex)函数为CDC类的成员函数,它的参数为整型,根据该参数的不同,它的返回值为设备上下文对应的某个指标参量值,具体参数的设置可以参考MSDN。采用LOGPIXELSX,它的物理含义是在显示屏的水平方向上(X轴方向)每逻辑英寸内的像素数。为了消除上述计算方法产生的偏差,仅需要将上述函数中的scale改为:
scale=25.4*10/pDC->GetDeviceCaps(LOGPIXELSX)。

  经过笔者设置端点调试,得出屏幕上X轴的方向每英寸内有96个像素点,其倒数既为每个像素点所等价的英寸数,再与25.4*10相乘即表示每个像素等价于多少个0.1毫。经过上述改动后,屏幕结果显示正确,效果图如下:

  

  在关于几何图形的编程中,尽可能采用反映实际建模对象的映射模式,本文运用了GetDeviceCaps()函数很好的消除了坐标转换过程中的误差,相信可以对用户开发各类涉及到交互式图形拾取的项目有一定的帮助。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章