坐标与变换

坐标系统

在本套书的VB篇中,讲到了Windows绘图的三套坐标系,即用户坐标系、逻辑坐标系和设备坐标系。与此对应,GDI+中也有三套坐标系,但名称有所不同。GDI+中的三套坐标系称为通用坐标系、页面坐标系和设备坐标系。

通用坐标系是用户自己定义的坐标己,比她按照我们的习惯,把原点放在屏幕左下角人者中央,让纵轴正向朝上,横轴正向向右。页面坐标系与过去说的逻辑坐标系对应,是一个虚拟的坐标系,其原点始终位于页面的左上角,纵轴正向朝下,横轴正向向右。设备坐标系用在具体的输出设备如屏幕中,屏幕坐标系的原点在屏幕的左上角,纵轴正向朝下,横轴正向向右。当页面坐标系和设备坐标系的单位都是像素时,它们相同。

几何变换

在GDI+中实现几何变换很方便。因为Graphics类已经提供了进行各种变换的方法。例如,用 Tanslate ranstomm方法指定横向上和纵向上的位移量,进行平参变换;用Rotate Transform 方法指定旋转角度,进行旋转变换;用ScaleTransform方法指定按比例缩小或放大的比例,进行比例变换。

下面这个例子演示在全局状态下的各种几何变换方法和效果。首先建立项目,在窗体中放置图片框1个,命令按钮6个,如图3-9所示。各控件的属性设置如表3-9所示。

Document Image
\[\]

图3-9程序界面

表3-9控件属性设置

Document Image
\[\]

在Forml类中添加下面的代码:

【VB.NET】

code.vb.net
Private Sub Button1_Click(ByVal sender As System.Object, _
               ByVal text-blue-400">e As System.EventArgs) Handles Button1.text-blue-400">Click
     '左移
     g = PictureBoxl.CreateGraphics()
     g.Clear(Color.White)
     g.Translate Transform(-50,0)
     Draw(g)
End Sub
Private Sub PictureBox1_Paint(ByVal sender As Object, _
               ByVal text-blue-400">e As Windows.Forns.PaintEventArgs) Handles PictureBox1.Paint
     g=text-blue-400">e.Graphics
     Draw(g)
End Sub
Private Sub Draw(ByVal g As Graphics)
     '绘图
     g.DrawLine(Pens.Black, 10,10,100,100)
     g.DrawEllipse(Pens.Black, 50, 50, 200, 100)
     g.DrawArc(Pens.Black, 100, 10,100,100, 20,160)
     g.FillRectangle(Brushes.Green, 50, 200, 150,100)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, _
               ByVal text-blue-400">e As System.EventArgs) Handles Button2.text-blue-400">Click
     '旋转
     g= PictureBox1.CreateGraphics()
     g.Clear(Color.White)"
     g.Rotate Transform(-30)
     Draw(g)
End Sub
Private Sub Button4_Click(ByVal sender As System.Object, _
               ByVal text-blue-400">e As System.EventArgs) Handles Button4.text-blue-400">Click
     '放大
     g = PictureBox1.CreateGraphics()
     g.Clear(Color.White)
     g.ScaleTransform(1.5,1.5)
     Draw(g)
End Sub
Private Sub Button5_Click(ByVal sender As System.Object, _
               ByVal text-blue-400">e As System.EventArgs) Handles Button5.text-blue-400">Click
     '缩小
     g = PictureBox1.CreateGraphics()
     g.Clear(Color.White)
     g.Scale Transform(0.8, 0.8)
     Draw(g)
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, _
               ByVal text-blue-400">e As System.EventArgs) Handles Button3.text-blue-400">Click
     '右移
     Dim g As Graphics = Picture Box1.CreateGraphics()
     g.Clear(Color.White)
     g.Translate Transform(50, 0)
     Draw(g)
End Sub
Private Sub Button6_Click(ByVal sender As System.Object, _
               ByVal text-blue-400">e As System.EventArgs) Handles Button6.text-blue-400">Click
     '取消运行
     End
End Sub
Module Module1
     Public g As Graphics
End Module"

【VC#.NET】

code.vc#.net
private System.Windows.Formns.PictureBox pictureBox1;
internal System.Windows.Forms.Button Button6;
internal System.Windows.Forms.Button Button5;
internal System.Windows.Forms.Button Button4;
internal System.Windows.Forms.Button Button3;
internal System.Windows.Forms.Button Button2;
internal System.Windows.Forms.Button Button1;
private Graphics g:
private void Button1_Click(object sender,EventArgs e)
{
     //左移
     g= pictureBox1.CreateGraphics();
     g.Clear(Color.White);
     g.Translate Transform(-50,0);
     Draw(g);
}
private void Picture Box1_Paint(object sender, EventArgs e)
{
     g= pictureBox1.CreateGraphics();
     Draw(g);
}
private void Draw(Graphics g)
{
     //绘图
     g.DrawLine(Pens.Black,10,10, 100,100);
     g.DrawEllipse(Pens.Black, 50, 50, 200, 100);
     g.DrawArc(Pens.Black, 100, 10,100,100, 20,160);
     g.FillRectangle(Brushes.Green, 50, 200,150,100);
}
private void Button3_Click(object sender, EventArgs e)
{
     //右移
     Graphics g= pictureBox1.CreateGraphics();
     g.Clear(Color.White);"
     g.Translate Transform(50, 0);
     Draw(g);
}
private void Button2_Click_1(object sender, System.EventArgs e)
{
     //旋转
     g= pictureBox1.CreateGraphics();
     g.Clear(Color.White);
     g.Rotate Transform(-30);
     Draw(g);
}
private void Button4_Click_1(object sender, System.EventArgs e)
{
     //放大
     g= pictureBox1.CreateGraphics();
     g.Clear(Color.White);
     g.ScaleTransform(1.2f,1.2f);
     Draw(g);
}
private void Button5_Click_1(object sender, System.EventArgs e)
{
     //缩小
     g= pictureBox1.CreateGraphics();
     g.Clear(Color.White);
     g.Scale Transform(0.8f, 0.8f);
     Draw(g);
}
private void Button6_Click_1(object sender, System.EventArgs e)
{
     //取消运行
     this.Dispose();
}

程序运行结果如图3-10和图3-11所示。"

Document Image
\[\]

图3-10 几何变换效果一

Document Image
\[\]

图3-11 几何变换效果二

全局坐标与局部坐标

前面的各种变换都是在全局坐标系下面进行的,其变换对于绘图表面上的每个图元都会产生影响。全局变量常用于设定通用坐标系,比如重新指定原点位置和坐标轴正向等。由于它对全部图元都产生影响,所以也可以用于实现平移和缩小放大操作。

下面的例子首先将原点移到屏幕中心,并使Y轴正向朝上,然后再坐标系下进行一些几何变换和图形绘制工作。

【VB.NET】

code.vb.net
Protected Overrides Sub OnPaint(By Val text-blue-400">e As Windows. Forms. PaintEventArgs)
     '本程序建立又轴正向向右,Y轴正向向上,原点在屏幕中心的全局坐系圩
     ',使Y轴朝上,需要做相对于又轴的镜像,变换矩阵为[1,0,0,-1,0,0]
     Dim g As Graphics = text-blue-400">e.Graphics
     g.FillRectangle(Brushes.White, Me.ClientRectangle)
     g.DrawEllipse(Pens.Black,-100,-100, 200,200)
     '相对于X轴进行镜像变换
     Dim mat As New Matrix(1,0,0,-1,0,0)
     g.Transform = mat
     Dim rect As Rectangle = Me.ClientRectangle
     Dim w As Integer = rect.Width
     Dim h As Integer = rect.Height
     将原点移到屏幕中心
     g.Translate Transform(w/2,-h/2)
     '以原点为中心,绘一个半径为100的圆
     g.DrawEllipse(Pens.Red,-100,-100,200,200)
     g.Translate Transform(100, 100)
     g.DrawEllipse(Pens.Green,-100,-100, 200,200)
     g.ScaleTransform(2, 2)
     g.DrawEllipse(Pens.Blue,-100,-100, 200,200)
End Sub
Private Sub Form1_MouseMove(ByVal sender As Object, _
          ByVal text-blue-400">e As Windows.Forms. MouseEventArgs) Handles MyBase.MouseMove
     '在文本框中显示当前点的坐标。屏幕坐标被转换为工作区坐标
     Dim g As Graphics = CreateGraphics()
     Dim rect As Rectangle = Me.ClientRectangle
     Dim w As Integer = rect.Width
     Dim h As Integer = rect.Height
     TextBox1.Text=" "
     TextBox1.Text= Str(text-blue-400">e.X-w/2) & " " & Str(-(text-blue-400">e.Y-h/2))
End Sub

【VC#.NET】

code.vc#.net
protected override void OnPaint(PaintEventArgs e)
{
     //本程序建立X轴正向向右,Y轴正向向上,原点在屏幕中心的全局坐标系
     //使Y轴朝上,需要做相对于X轴的镜像,变换矩阵为[10,0,-1,0,0]
     Graphics g= e.Graphics;
     g.FillRectangle(Brushes.White, this.ClientRectangle);
     g.DrawEllipse(Pens.Black,-100,-100, 200, 200);
     //相对于X轴进行镜像变换
     Matrix mat=new Matrix(1,0,0,-1,0,0);
     g.Transform = mat;
     Rectangle rect= this.ClientRectangle;
     int w= rect.Width;
     int h= rect.Height;
     //将原点移到屏幕中心
     g.Translate Transform(w/2,-h/2);
     //以原点为中心,绘一个半径为100的圆
     g.DrawEllipse(Pens.Red,-100,-100,200,200);
     g.Translate Transform(100, 100);
     g.DrawEllipse(Pens.Green,-100,-100, 200,200);
     g.ScaleTransform(2,2);
     g.DrawEllipse(Pens.Blue,-100,-100, 200,200);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
     //在文本框中显示当前点的坐标。屏幕坐标被转换为工作区坐标
     Graphics g = CreateGraphics();
     Rectangle rect= this.ClientRectangle;
     int w= rect.Width;
     int h= rect.Height;
     textBox1.Text=" "
     textBox1.Text=e.X-w/2+" "+(-(e.Y-h/2));
}

程序运行结果如图3-12所示。文本框中显示了通用坐标系下鼠标的当前位置。"

Document Image
\[\]

图3-12 全局变换

现在假设只对某个或某些图形进行变换,而其他图形元素不变,就需要用到局部坐标系。进行局部变换,需要首先创建一个GraphicsPath对象,添加图元到该对象以后,创建一个Matrix对象,确定变换矩阵,然后将这个Matrix对象作为参数传递给GraphicsPath 对象的Transform方法。这样,路径中的所有图元都会做相应的变换,而其他图元不会变化。

下面的程序首先进行全局变换,建立原点在屏幕中心,Y轴正向朝上的通用坐标系:然后创建一条路径,在路径中添加一个圆,再在该路径所在的局部坐标系中进行平移变换和旋转变换。路径中的圆在变换后的另一个位置显示。

【VB.NET】

code.vb.net
Proutected Overides Sub OnPaint(ByVal text-blue-400">e As Windows.Forms PaintEventArgs)
     Dim g As Graphics = text-blue-400">e.Graphics
     '把客户区设置为白色
     g.FillRectangle(Brushes.White, Me.ClientRectangle)
     '定义相对于X轴镜像的转换矩阵,使得Y轴朝上
     Dim mat As New Matrix(1,0,0,-1,0,0)
     g.Transform = mat
     '将坐标原点移动到屏幕中心
     Dim rect As Rectangle = Me.ClientRectangle
     Dim w As Integer = rect.Width
     Dim h As Integer = rect.Height
     g.Translate Transform(w/2,-h/2)
     '在全局坐标下绘制椭圆
     g.DrawEllipse(Pens.Red,-100,-100,200,200)
     g.FillRectangle(Brushes.Black, 100 - 2,0+2,4,4)
     g.DrawRectangle(Pens.Blue, 0- 2,-100 +2,4, 4)
     g.DrawRectangle(Pens.Blue,-100 - 2,0+2,4,4)
     g.DrawRectangle(Pens.Blue, 0- 2, 100+2,4,4)
     '创建一个椭圆,然后在局部坐标系中进行几何变换
     Dim gp As New GraphicsPath()
     gp.AddEllipse(-100,-100,200, 200)
     Dim mat2 As New Matrix()
     '平移变换
     mat2.Translate(150, 150)
     '旋转变换,缺省时按顺时针方向旋转
     mat2.Rotate(30)
     gp.Transform(mat2)
     g.DrawPath(Pens.Blue,gp)
     Dim p() As PointF = gp.PathPoints()
     g.FillRectangle(Brushes.Black,p(0).X-2,p(0).Y +2,4,4)
     g.DrawRectangle(Pens.Blue,p(3).X-2,p(3).Y +2,4, 4)
     g.DrawRectangle(Pens.Blue,p(6).X-2,p(6).Y +2,4,4)
     g.DrawRectangle(Pens.Blue,p(9).X-2,p(9).Y +2,4,4)
     gp.Dispose()
End Sub

【VC#.NET】

code.vc#.net
protected override void OnPaint(PaintEventArgs e)
{
     Graphics g = e.Graphics;
     //把客户区设置为白色
     g.FillRectangle(Brushes.White,this.ClientRectangle);
     //定义相对于X轴镜像的转换矩阵,使得Y轴朝上
     Matrix mat=new Matrix(1,0,0,-1,0,0);
     g.Transform = mat;
     //将坐标原点移动到屏幕中心
     Rectangle rect= this.ClientRectangle;
     int w= rect.Width
     int h= rect.Height;
     g.Translate Transform(w /2,-h/2);
     //在全局坐标下绘制椭圆
     g.DrawEllipse(Pens.Red,-100,-100, 200,200);"
     g.FillRectangle(Brushes.Black, 100 - 2,0+2,4,4);
     g.DrawRectangle(Pens.Blue,0- 2,-100 +2,4,4);
     g.DrawRectangle(Pens.Blue,-100- 2,0+2,4,4);
     g.DrawRectangle(Pens.Blue,0- 2,100 +2,4,4);
     //创建一个椭圆,然后在局部坐标系中进行几何变换
     GraphicsPath gp=new GraphicsPath();
     gp.AddEllipse(-100,-100,200,200);
     Matrix mat2=new Matrix();
     //平移变换
     mat2.Translate(150,150);
     //旋转变换,缺省时按顺时针方向旋转
     mat2.Rotate(30);
     gp.Transform(mat2);
     g.DrawPath(Pens.Blue,gp);
     PointFl p= gp.PathPoints;
     g.FilRectangle(Brushes.Black,p[0J.X- 2,p[0].Y +2,4,4);
     g.DrawRectangle(Pens.Blue,p[3].X- 2,p[3].Y +2,4,4);
     g.DrawRectangle(Pens.Blue,p[6].X-2,p[6].Y +2,4,4);
     g.DrawRectangle(Pens.Blue,p[9].X-2,p[9].Y+2,4,4);
     gp.Dispose();
}

程序运行结果如图3-13所示。图中还显示了圆上的4个关键点,可见,变换以后,关键点也随着移动了位置。

Document Image
\[\]

图3-13 局部变换