Pudgy's World
posts - 13,  comments - 16,  trackbacks - 0

摘自http://www-128.ibm.com/developerworks/cn/xml/x-BatikSvg/?ca=dwcn-newsletter-xml
陈柯

技术总监, 南京安元科技
2005 年 1 月

本文是作者在 SVGGIS 系统的开发实践过程中关于 SVG 坐标转换的总结。在描述 SVG 坐标变换原理的同时,使用 Apache Batik 项目实现了相关例子。

SVG 是一种用 xml 语言来描述二维图形对象的语言,SVG 允许三种图形对象:1.矢量图形,2.图片,3.文本对象。这三种图形对象都可以支持分组,使用样式渲染,进行几何变换。

SVG 还能够通过脚本来实现交互操作和动态显示。可以通过定义动画对象或使用script 脚本来实现动画。

1. SVG 下几种常见的几何变换方式

1.1 一个 SVG 例子
我们首先来看一个 SVG 的例子,窗口右上角有四个色块,每个色块是一个50×50的矩形。

图 1. 一个 SVG 的样本
图 1. 一个 SVG 的样本

图 2. 样本文档

<?xml version="1.0"?>
<svg >
    <g> 
	<rect x="0" y="0" width="50" height="50" style="fill:red" />
	<rect x="50" y="0" width="50" height="50" style="fill:yellow" />
	<rect x="0" y="50" width="50" height="50" style="fill:green" />
	<rect x="50" y="50" width="50" height="50" style="fill:black" />
    </g>
</svg>

1.2 使用 Adobe SVG Viewer 展示在 SVG 文档中实现的几何变换
缩放<g transform="scale(2)">

图 3. 放大一倍
图 3. 放大一倍

平移<g transform="translate(200 ,200)">

图 4. 平移200,200个像素
图 4. 平移200,200个像素

旋转<g transform="rotate(45)">

图 5. 顺时针旋转45度
图 5. 顺时针旋转45度

横切<g transform="skewX(45)">

图 6. 以 y 轴为基线在 X 方向横切45度
图 6. 以 y 轴为基线在 X 方向横切45度

2. 在 Batik 下实现 SVG 的几何变换

2.1 Batik 的基础知识
2.1.1 Batik 的用途

Batik 是基于 java 语言实现的一个 SVG 应用的工具集,用于实现对 SVG 对象的显示、编辑以及将 SVG 图形对象转换成其他图片格式,如 jpg、gif 等。

这个项目的目标就是给开发人员一套用于处理或应用 SVG 对象的基础核心模型。作为Apache 项目成员之一,该项目也为开发人员提供了一个开发的可扩展的平台。同时 batik 也维护了一个可以查看 SVG 文件的浏览器。虽然 batik 还没有完全实现 SVG 的所有标准语法和标记,但通过比较不同版本的区别就会发现,他正在以很高的效率覆盖 SVG 所有的标准。

2.1.2 让我们实现一个简单的 Batik 程序

首先让我们实现一个简单的基于 Batik 的 SVG 浏览器。Batik 封装了org.apache.batik.swing.JSVGCanvas 对象可以用于在 swing 中嵌入 SVG 显示容器,并可以通过 org.apache.batik.swing.JSVGCanvas 提供的方法对 SVG 文档和图像进行操作。这个浏览器可以支持大部分 SVG 的语法和标准包括脚本交互的功能,但暂时还没有引入动画。关于动画和脚本交互的内容我们会在以后的文章中讲述,今天先集中解决几何变换的问题。

图 7. 运行程序打开我们编写的 SVG 的例子
图 7. 运行程序打开我们编写的 SVG 的例子

可以通过该页面引导运行该程序:
从附件中可以查看该程序的完整代码,也可通过 网上下载地址 运行该程序。

图 8. 将一个 SVGCanvas 添加到界面

private javax.swing.JPanel SVGPanel = new javax.swing.JPanel();
private JSVGCanvas svgCanvas = new org.apache.batik.swing.JSVGCanvas();
SVGPanel.add("Center", svgCanvas);

2.2 通过 Batik 的 GVT 模型实现 SVG 的几何变换
2.2.1 为什么要使用 Batik 来实现 SVG 的几何变换

当我们用 Batik 工具集作为 SVG 客户端的解决方案时,如缩放平移这样的操作就在所难免了。但 Batik 并没有直接支持如 Adobe SVG Viewer 那样的鼠标拖动几何变换的操作,这就要求我们对这些功能进行编程处理。

在分析 SVG 的几何变换的细节之前,先让我们了解一下基本的操作编程方式。

2.2.2 Batik 下通过 java.awt.geom.AffineTransform 来实现 SVG 的几何变换

JSVGCanvas 提供方法可以获取 java.awt.geom.AffineTransform 对象。AffineTransform 是用于实现2D 几何图形坐标变换处理的对象,可以通过该对象进行二维几何空间中两个坐标系的相互映射和变换。

平移:



//向左和向下平移50个像素
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.translate(50,50);
svgCanvas.setRenderingTransform(rat);

缩放:



//以屏幕左上角原点为固定点进行缩放操作
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.scale(0.5,0.5);
svgCanvas.setRenderingTransform(rat);

旋转:



//以屏幕左上角原点为固定点进行旋转缩放
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.rotate(3.1415926/4);
svgCanvas.setRenderingTransform(rat);

复合变换:



//一个综合平移、放大和旋转90度的复合变换
java.awt.geom.AffineTransform  rat = svgCanvas.getRenderingTransform();
rat.translate(50,50);
rat.scale(2,2);
rat.rotate(3.1415926/4);
svgCanvas.setRenderingTransform(rat);

3. 当我们需要进行复合几何变换的时候

3.1 先来让我们通过不同的变换代码组合实现复合几何变换
先让我们看第一个例子:

//放大一倍和平移50个像素的复合变换

AffineTransform rat = svgCanvas.getRenderingTransform();

        rat.scale(2,2);
rat.translate(50,50);
svgCanvas.setRenderingTransform(rat);

      

图 9. 复合变换一
图 9. 复合变换一

可以看得出来,这个变换的最终效果是:图形的形状放大一倍,原图形的(0,0)原点坐标平移100个像素。

再来看第二个例子:



//放大一倍和平移50个像素的复合变换
AffineTransform rat = svgCanvas.getRenderingTransform();

        rat.translate(50,50);
rat.scale(2,2);
svgCanvas.setRenderingTransform(rat);

      

图 10. 复合变换二
图 10. 复合变换二

这个变换的最终效果是:图形的形状放大一倍,原图形的(0,0)原点坐标平移50个像素。

3.2 关键是顺序
比较一下这两种平移后的效果会发现,只是因为缩放和平移的顺序不同,变换后的结果就发生了区别。第一个例子实际平移的不是50个像素,而是100个像素。而第二个例子则是平移了50个像素。

有兴趣的读者可以添加其他几何变换方式,并测试不同的变换顺序,会发现复合变换的顺序与复合变换的最终效果是紧密相关的。那么如何分析和计算复合变换的变换结果呢?这里我们需要补充一点数学知识了。

3.3 对单一的几何变换进行数学模型分析
对计算机图形学中图形变换相关理论很熟悉的人可以跳过这部分直接看 Batik 的实现方式。这节使用的齐次式图片引用自 SVG标准中关于坐标变换的齐次式的例子插图。

首先我们来了解一下图形变换的齐次式计算方法:

图 11. 基本几何 变换齐次式
图 11. 基本几何 变换齐次式

这是一个基本图形变换齐次式。等式的最右边是一个坐标点未变换前的坐标矩阵,最左边是该坐标点变换后所在位置的一个三行一列的坐标矩阵,中间那个三行三列的矩阵就是变换矩阵。不同的变换方式将对应不同的变换矩阵,图形平移效果就是通过这样一个变换齐次式来实现的。

平移:如果需要将图形平移(tx,ty)个坐标时,采用如下的变换矩阵带入变换方程。

图 12. 平移变换矩阵
图 12. 平移变换矩阵

缩放:如果需要在 x 轴方向实现 sx 倍缩放,在 y 轴方向实现 sy 轴缩放时,采用如下的变换矩阵带入变换方程。

图 13. 缩放变换矩阵
图 13. 缩放变换矩阵

旋转:如果需要将图像旋转 a 度时,使用如下的变换矩阵带入变换方程。

图 14. 旋转变换矩阵
图 14. 旋转变换矩阵

3.4 采用复合几何变换的数学模型分析
3.4.1 数学分析

当两组变换同时作用于同一个图像时,连续使用该等式,得出如下等式。由于做变换的时候是将变换矩阵放在矩阵积的左边,所以对于复合变换的式子,应该从右向左进行读。对于如下的式子:从右至左依次是变换前的坐标,第一次转换的转换矩阵,第二次转换的转换矩阵,转换后的坐标值。

图 15. 进行两次变换的复合矩阵
图 15. 进行两次变换的复合矩阵

进一步推导 n 次几何变换的复合变换等式:

图 16. n次变换的复合矩阵
图 16. n次变换的复合矩阵

3.4.2 来实践一下

图 17. 变换前效果
图 17. 变换前效果

使用这样一个复合变换方式:

<g transform="translate(50,90) rotate(-45) translate(130,160)">

根据前面的分析结果带入变换方程,得出如下等式

图 18. 推导矩阵
图 18. 推导矩阵

根据计算后的变换矩阵,可得变换结果是图形整体平移到(255.03,111.21),同时图形自身沿顺时针方向旋转45度。查看实际变换结果,这里我们可以发现,对于 SVG 的变换来说,虽然我们习惯上从左往右写变换参数的,实际上图形做变换的时候是从右边的变换参数开始依次进行图形变换的。

图 19. 变换后效果
图 19. 变换后效果

3.5 分析一下 Batik 是如何实现 SVG 的复合几何变换的
3.5.1 先看第一个例子:

这个变换是先放大后平移的变换,其变换效果最终是,先将图形的形状放大一倍,然后将图形整个平移100个像素。这里我们可以看出,虽然我们是先进行的放大变换,后进行的平移操作,但当我们进行复合变换的时候由于实际运算时上是先进行了平移,后进行了缩放。或者简单的理解为从左向右先写缩放矩阵,再写平移矩阵,这样得出的变换矩阵就可以对变换后的效果进行计算了。



//放大一倍和平移50个像素的复合变换
//<g transform=" scale(2 ) translate(50 50)">
AffineTransform rat = svgCanvas.getRenderingTransform();

        rat.scale(2,2);
rat.translate(50,50);
svgCanvas.setRenderingTransform(rat);

      

我们可以将这个变换对应的计算矩阵写出来:

图 20. 复合变换例一的变换矩阵
图 20. 复合变换例一的变换矩阵

根据上面的分析我们可以看的出,先进行缩放变换在进行平移变换的复合变换时,变换后原图元的坐标会映射到新的位置,其中:

X1=Sx(X+dx)
Y1=Sy(Y+dy)

3.5.2 再来看第二个例子:

这个变换是先放大后平移的变换,其变换效果最终是,先将图形整个平移50个像素,然后将图形的形状放大一倍。



//放大一倍和平移50个像素的复合变换
//<g transform="translate(50,50) scale(2 )">
AffineTransform rat = svgCanvas.getRenderingTransform();

        rat.translate(50,50);
rat.scale(2,2);
svgCanvas.setRenderingTransform(rat);

      

我们可以将这个变换对应的 svg 文档实现写出来:

<g transform="translate(50 50) scale(2 )">

图 21. 复合变换例二的变换矩阵
图 21. 复合变换例二的变换矩阵

根据上面的分析我们可以看的出,先进行平移变换再进行缩放变换的复合变换时,变换后原图元的坐标会映射到新的位置,其中:

X1=Sx*X+dx
Y1=Sy*Y+dy

3.5.3 实用这两个例子的成果

假设我们需要将图形原点的位置移动到(150,300),同时形状放大到原来的3倍,该如何进行变换来实现这样的效果呢?

图 22. 变换效果
图 22. 变换效果

使用第一个例子的分析结果,先进行缩放变换,再进行平移变换的方程变换:

图 23. 方程推演
图 23. 方程推演

根据推导可知需先进行3倍的缩放变换,再平移(50,100)个坐标就可以实现指定的变换效果。实现程序如下



//<g transform=" scale(3 ) translate(50,100)">
AffineTransform rat = svgCanvas.getRenderingTransform();

        rat.scale(3,3);
rat.translate(50,100);
svgCanvas.setRenderingTransform(rat);

      

若先进行平移变换,再进行缩放变换的话,根据第二个例子的推导结果:

图 24. 方程推演
图 24. 方程推演

可知应先进行(150,300)的平移操作,再实现3倍的缩放操作。实现程序如下:



//<g transform=" translate(150,300) scale(3 )">
AffineTransform rat = svgCanvas.getRenderingTransform();

        rat.translate(150,300);
rat.scale(3,3);
svgCanvas.setRenderingTransform(rat);

      

4. 一个常用复合变换实例的实现--定点变换
上边我们分析了 Batik 实现 SVG 图形变换的原理和计算方法。下面我们用分析的结果实现一个常用的变换实例:定点变换。变换的种类包括缩放,旋转(一般不考虑定点平移这个概念)。

所谓定点变换就是在图形变换中指定一个固定的点,在变换结束后,该点的位置不发生变化。定点变换在 GIS 的实际运用中很常见,比如将地图放大到指定倍数而保持地图的某个位置(如:鼠标点击的位置)不发生变化。我们以定点缩放为例描述定点变换的使用方法。

假设我们用(x1,y1)点来做定点变换的基准点进行几何变换,要求实现变换后(x1,y1)的位置不变。

定点变换的基本思想是基于这样一个特性:当图像进行缩放,或旋转变换的时候,坐标原点的位置并不发生变化。定点变换的实现方法就是,先将基准点(x1,y1)平移到原点即做一次[-x1,-y1]变换,进行变换后再将变换后的原点平移到(x1,y1)即再进行一次[x1,y1]变换。

图 25. 基于(x1,y1)定点变换
图 25. 基于(x1,y1)定点变换

实际编程的时候,我们无需对定点变换做如此复杂的运算。例如可以采用如下的方式实现针对(50,50)的定点变换:



//以(50,50)点为基准点进行几何变换
//<g transform="translate(50,50) …………… translate(-50,-50)">
AffineTransform rat = svgCanvas.getRenderingTransform();

        rat.translate(50,50);
//其他变换方式
………………
………………
………………
rat.translate(-50,-50);
svgCanvas.setRenderingTransform(rat);

      

参考资料

posted on 2005-08-20 08:55 Pudgy's World 阅读(2476) 评论(0)  编辑  收藏 所属分类: Graphics

只有注册用户登录后才能发表评论。


网站导航:
 

<2005年8月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(1)

随笔分类(13)

随笔档案(13)

文章分类(4)

文章档案(5)

相册

Developer

Favorite blogs

搜索

  •  

积分与排名

  • 积分 - 22169
  • 排名 - 1633

最新评论

阅读排行榜

评论排行榜