http://www.functionx.com/visualc/gdi/gdicoord.htm
自定义单位与坐标系
到目前为止,这些映射模式允许我们设置坐标轴朝向。但我们不能设置单位长度。因为每一种模式(MM_TEXT, MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, and MM_TWIPS) 都已经设置好了坐标轴朝向、单位长度等属性。如果你想自定义它们,应该怎么做呢(你使用过AutoCAD么)?
思考事件处理函数OnPaint。它绘制了200x200像素的方形,红色边框,浅绿色背景。为了更好的表示,此函数绘制了一条对角线,起点为原点(0,0)。
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));
CBrush BrushAqua(RGB(0, 255, 255));
dc.SelectObject(PenRed);
dc.SelectObject(BrushAqua);
// Draw a square with a red border and an aqua background
dc.Rectangle(-100, -100, 100, 100);
CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(BluePen);
// Diagonal line at 45 degrees starting at the origin (0, 0)
dc.MoveTo(0, 0);
dc.LineTo(200, 200);
}
如你所见,我们能看见右下角的部分方形。直线在第四象限内。
设想原点(0,0)点位于窗体中心。即将原点放在(340,220)处。你已经会使用CDC::SetViewportOrg()方法来指定原点。请看下面的例子(我们没有指定映射模式,因为MM_TEXT是默认映射模式)
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetViewportOrg(340, 220);
CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));
CBrush BrushAqua(RGB(0, 255, 255));
dc.SelectObject(PenRed);
dc.SelectObject(BrushAqua);
// Draw a square with a red border and an aqua background
dc.Rectangle(-100, -100, 100, 100);
CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(BluePen);
// Diagonal line at 45 degrees starting at the origin (0, 0)
dc.MoveTo(0, 0);
dc.LineTo(200, 200);
}
为了自定义你自己的坐标系,包括坐标轴的朝向和单位长度。可以使用MM_ISOTROPIC或MM_ANISOTROPIC模式。首先,你要调用CDC::SetMapMode()函数来指定其中一个(MM_ISOTROPIC或MM_ANISOTROPIC)。例子如下:
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetMapMode(MM_ISOTROPIC);
dc.SetViewportOrg(340, 220);
CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));
CBrush BrushAqua(RGB(0, 255, 255));
dc.SelectObject(PenRed);
dc.SelectObject(BrushAqua);
// Draw a square with a red border and an aqua background
dc.Rectangle(-100, -100, 100, 100);
CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(BluePen);
// Diagonal line at 45 degrees starting at the origin (0, 0)
dc.MoveTo(0, 0);
dc.LineTo(200, 200);
}
别认为,在传入参数MM_ISOTROPIC或MM_ANISOTROPIC参数调用CDC::SetMapMode()之后,就完毕了,别被上面图骗了。这两个模式的目的是让你控制坐标由的朝向或单位长度。
两种模式的不同之处在于,当使用MM_ISOTROPIC模式时,水平轴的单位长度与竖直轴的单位长度相等。这里没有列举MM_ANISOTROPIC模式。此模式可使你为水平轴竖直轴设置不同的单位长度。
所以,在使用MM_ISOTROPIC或MM_ANISOTROPIC参数调用SetMapMode之后,你必须调用函数CDC::SetWindowExt()。此函数决定了新单位长度的多少。新单位长度将与旧的或默认的单位长度相乘。CDC::SetWindowsExt()有两个版本。语法如下:
CSize SetWindowExt(int cx, int cy);
CSize SetWindowExt(SIZE size);
如果使用第一个版本,第一个参数cx指定了水平轴逻辑转化乘子。第二个参数cy
指定了竖直轴逻辑转化乘子。如果你想使用SIZE对角指定逻辑宽和高,便 可以使用第二个版本的函数。(译注:cx,cy应该是指定了缩放框的大小。Windows根据绽放框的大小计算出缩放比例)
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetMapMode(MM_ISOTROPIC);
dc.SetViewportOrg(340, 220);
dc.SetWindowExt(480, 480);
CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));
CBrush BrushAqua(RGB(0, 255, 255));
dc.SelectObject(PenRed);
dc.SelectObject(BrushAqua);
// Draw a square with a red border and an aqua background
dc.Rectangle(-100, -100, 100, 100);
CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(BluePen);
// Diagonal line at 45 degrees starting at the origin (0, 0)
dc.MoveTo(0, 0);
dc.LineTo(200, 200);
}
在调用SetWindowExt()函数之后,你可以调用SetViewporExt()函数。它的作用是指定水平轴和竖直轴的单位长度。语法如下:
CSize SetViewportExt(int cx, int cy);
CSize SetViewportExt(SIZE size);
为了使用第一个版本的函数,你必须提供水平轴的转化因子cx和竖直轴的转化因子cy。如果你知道高宽比,你可以使用第二个版本的函数。(译注:很明显SetWiewport是视口有关的函数)
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetMapMode(MM_ISOTROPIC);
dc.SetViewportOrg(340, 220);
dc.SetWindowExt(480, 480);
dc.SetViewportExt(440, -680);
CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));
CBrush BrushAqua(RGB(0, 255, 255));
dc.SelectObject(PenRed);
dc.SelectObject(BrushAqua);
// Draw a square with a red border and an aqua background
dc.Rectangle(-100, -100, 100, 100);
CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(BluePen);
// Diagonal line at 45 degrees starting at the origin (0, 0)
dc.MoveTo(0, 0);
dc.LineTo(200, 200);
}
坐标轴箭头例子
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
CBrush bgBrush(BLACK_BRUSH);
dc.SelectObject(bgBrush);
dc.Rectangle(Recto);
dc.SetMapMode(MM_ISOTROPIC);
dc.SetViewportOrg(0, 440);
dc.SetWindowExt(480, 480);
dc.SetViewportExt(440, -680);
CPen PenWhite(PS_SOLID, 1, RGB(255, 255, 255));
dc.SelectObject(PenWhite);
dc.MoveTo(21, 20);
dc.LineTo(21, 75);
// Up arrow
dc.MoveTo(16, 75);
dc.LineTo(21, 90);
dc.LineTo(26, 75);
dc.LineTo(16, 75);
dc.MoveTo(21, 22);
dc.LineTo(75, 22);
// Right arrow
dc.MoveTo(75, 17);
dc.LineTo(90, 22);
dc.LineTo(75, 27);
dc.LineTo(75, 17);
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(RGB(255, 255, 255));
dc.TextOut(16, 114, 'Y');
dc.TextOut(100, 32, 'X');
dc.Rectangle(15, 15, 30, 30);
}
绘制线状网格
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
CBrush bgBrush(BLACK_BRUSH);
dc.SelectObject(bgBrush);
dc.Rectangle(Recto);
CPen PenBlue(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(PenBlue);
for(int x = 0; x < Recto.Width(); x += 20)
{
dc.MoveTo(x, 0);
dc.LineTo(x, Recto.Height());
}
for(int y = 0; y < Recto.Height(); y += 20)
{
dc.MoveTo(0, y);
dc.LineTo(Recto.Width(), y);
}
}
绘制点状网格
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
CBrush bgBrush(BLACK_BRUSH);
dc.SelectObject(bgBrush);
dc.Rectangle(Recto);
for(int x = 0; x < Recto.Width(); x += 20)
{
for(int y = 0; y < Recto.Height(); y += 20)
{
dc.SetPixel(x, y, RGB(255, 255, 255));
}
}
}
绘制正弦曲线
void CExoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetViewportOrg(340, 220);
dc.SetWindowExt(1440, 1440);
dc.SetViewportExt(-1440, -220);
CPen PenBlue(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(PenBlue);
// Axes
dc.MoveTo(-300, 0);
dc.LineTo( 300, 0);
dc.MoveTo( 0, -1400);
dc.LineTo( 0, 1400);
// I am exaggerating with the PI value here but why not?
const double PI = 3.141592653589793238462643383279;
// The following two values were chosen randomly by me.
// You can chose other values you like
const int MultiplyEachUnitOnX = 50;
const int MultiplyEachUnitOnY = 250;
for(double i = -280; i < 280; i += 0.01)
{
double j = sin(PI / MultiplyEachUnitOnX * i) * MultiplyEachUnitOnY;
dc.SetPixel(i, j, RGB(255, 0, 0));
}
// Do not call CView::OnPaint() for painting messages
}