1 affine仿射变换概念
在几何上定义为两个向量空间之间的一个仿射变换或者仿射映射(来自拉丁语,affinis,"和。..相关")由一个线性变换接上一个平移组成。
2 agg::trans_affine成员函数说明
2.1 缩放
inline const trans_affine&trans_affine::scale(double x, double y)
参数一对x横坐标的缩放系数,参数二对y纵坐标的缩放系数
这里有一个问题:就是图形的缩放之后,并不是在原有的位置上,进行缩放,而是整体的缩放,比如最明显的是圆形,圆心的位置发生了改变,所以需要进行平移,恢复到以前的圆心。
2.2 旋转
inline const trans_affine&trans_affine::rotate(double a)
参数对图形进行旋转,旋转的圆心是坐标的原点(0,0),也就是显示界面的左上角,和一般的笛卡尔坐标不一样的地方,Y轴的纵坐标进行了翻转,Y轴向下逐渐增大。
应用注意事项:参数采用的是弧度的形式,至于弧度(radians)和角度(degrees)之间的区别,请参考其他的章节,不再赘述。所以该参数的范围是[-pi,pi].pi = 3.141592653.正值表示顺时针旋转,负值逆时针旋转,旋转的中心对称点是(0,0),切记!!很可能会旋转到界面之外。
Tips:角度转弧度agg::deg2rad(doubledegrees)
2.3 平移
inline const trans_affine&trans_affine::translate(double x, double y)
参数一,X轴平移量,参数二,Y轴平移量
3 关于仿射变换的数学知识
本文不打算描述trans_affine仿射变换的基本原理,其中的代码在agg_trans_affine.h文件中定义,涉及到的六个仿射变量如下:
double sx, shy, shx, sy, tx, ty;
可以搜索仿射变换的基本原理。
4 一些测试实例
4.1 旋转出界面
实例代码如下:
ras.reset();
agg::ellipse ell(400,400,20,70);
//坐标转换
agg::trans_affine mtx;
//mtx.scale(0.5,1); //x轴缩小到原来的一半
mtx.rotate(agg::deg2rad(30));//旋转30度
//mtx.translate(200,200);//X,Y坐标分别平移100
typedef agg::conv_transform<agg::ellipse> ell_ct_type;
ell_ct_type ctell(ell,mtx); //矩阵变换
ras.add_path(ctell);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba8(255,0,0));
ras.reset();
这是网上最经典的一个使用例子,但是并没有说明旋转是如何实现的,稍微修改了一下代码:
mtx.rotate(agg::deg2rad(60));//旋转60度
结果:发现界面上什么也没有,椭圆不见了!!
原因:顺时针旋转出界面。
4.2 通过滑动条查看旋转的过程
void RotateEclipse()
{
//关于agg::slider_ctrl<agg::rgba8>的基本使用,请参考其他的章节
int value = m_slider1.value();//取值
agg::rendering_buffer &rbuf = rbuf_window();
agg::pixfmt_bgr24 pixf(rbuf);
typedef agg::renderer_base<agg::pixfmt_bgr24> renderer_base_type;
renderer_base_type renb(pixf);
agg::rasterizer_scanline_aa<> ras;
agg::scanline_u8 sl;
ren_bas.clear(agg::rgba8(255,255,255));
agg::trans_affine mtx;
//mtx.scale(0.5,0.5); //x轴缩小到原来的一半
mtx.rotate(agg::deg2rad(value));//旋转30度
//mtx.translate(100 ,100);//X,Y坐标分别平移100
agg::ellipse ell(900,900,20,30);
typedef agg::conv_transform<agg::ellipse> ell_ct_type;
ell_ct_type ctell(ell,mtx); //矩阵变换
ras.reset();
ras.add_path(ctell);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba8(255,0,0));
agg::render_ctrl(ras, sl, renb, m_slider1);
}
5版本差异以及具体应用
1)AGG2.3版本
目前已经很难下载到,早期的项目有应用到,没有继续更新!!
如下代码对矩形进行缩放:
ras.reset();
agg::path_storage ps;
ps.move_to(30,30);
ps.line_to(50,30);
ps.line_to(50,50);
ps.line_to(30,50);
ps.line_to(30,30);
agg::trans_affine mtx;
//横坐标放大2倍,纵坐标放大3倍
mtx*= agg::trans_affine_scaling(2, 3);
//横坐标平移100,纵坐标平移300,正数向右,负数向左
mtx*=agg::trans_affine_translation(100,100);
typedefagg::conv_transform<agg::path_storage> ell_ct_type;
ell_ct_type ctell(ps,mtx); //矩阵变换
typedef agg::conv_stroke<ell_ct_type>ell_cc_cs_type;
ell_cc_cs_typecsccell(ctell);
ras.add_path(ctell);
2)AGG2.4/2.5版本
agg::trans_affine mtx;
mtx.scale(0.5,0.5); //x轴缩小到原来的一半
mtx.rotate(agg::deg2rad(40));//旋转30度
mtx.translate(100 ,100);//X,Y坐标分别平移100
typedefagg::conv_transform<agg::path_storage> ell_ct_type;
ell_ct_type ctell(ps,mtx); //矩阵变换
typedef agg::conv_stroke<ell_ct_type>ell_cc_cs_type;
ell_cc_cs_type csccell(ctell);
ras.add_path(csccell);
6 摘自agg_trans_affine.h的翻译
在笛卡尔坐标系中(Cartesian coordinates)仿射转换(affine transformation)是一种线性的转换(在一开始的时候就设定了)。她们可以自由的旋转(rotation),缩放(scaling),平移(translation)和剪切变换(skewing).经过任意的仿射变换,线段仍然是线段,她永远不可能编程一根曲线。
一言以蔽之,任何的矩阵变换都可以用一系列的离散变换实现。
原文如下:
//============================================================trans_affine
//
// See Implementation agg_trans_affine.cpp
//
// Affine transformation are lineartransformations in Cartesiancoordinates
// (strictly speaking not only inCartesian, but for the beginning wewill
// think so). They are rotation, scaling,translation and skewing.
// After any affine transformation a linesegment remains a line segment
// and it will never become a curve.
//
// There will be no math about matrixcalculations, since it has been
// described many times. Ask yourself avery simple question:
// "why do we need to understand anduse some matrix stuff insteadof just
// rotating, scaling and so on". Theanswers are:
//
// 1. Any combination of transformationscan be done by only 4multiplications
// and 4 additions in floatingpoint.
// 2. One matrix transformation isequivalent to the number ofconsecutive
// discrete transformations,i.e. the matrix "accumulates" alltransformations
// in the order of theirsettings. Suppose we have 4 transformations:
// * rotate by 30 degrees,
// * scale X to 2.0,
// * scale Y to 1.5,
// * move to (100, 100).
// The result will depend on theorder of these transformations,
// and the advantage of matrixis that the sequence of discret calls:
// rotate(30), scaleX(2.0),scaleY(1.5), move(100,100)
// will have exactly the sameresult as the following matrixtransformations:
//
// affine_matrix m;
// m *= rotate_matrix(30);
// m *= scaleX_matrix(2.0);
// m *= scaleY_matrix(1.5);
// m *= move_matrix(100,100);
//
// m.transform_my_point_at_last(x, y);
//
// What is the good of it? In real life wewill set-up the matrix onlyonce
// and then transform many points, letalone the convenience to set any
// combination of transformations.
//
// So, how to use it? Very easy - literallyas it‘s shown above. Notquite,
// let us write a correct example:
//
// agg::trans_affine m;
// m *= agg::trans_affine_rotation(30.0 *3.1415926 / 180.0);
// m *= agg::trans_affine_scaling(2.0,1.5);
// m *=agg::trans_affine_translation(100.0, 100.0);
// m.transform(&x, &y);
//
// The affine matrix is all you need toperform any lineartransformation,
// but all transformations have originpoint (0,0). It means that we needto
// use 2 translations if we want to rotatesometing around (100,100):
//
// m *= agg::trans_affine_translation(-100.0,-100.0); // move to (0,0)
// m *= agg::trans_affine_rotation(30.0 *3.1415926 / 180.0); // rotate
// m *=agg::trans_affine_translation(100.0, 100.0); // move back to (100,100)
//----------------------------------------------------------------------