几何变换(Transforms)

Easygraphics(和其使用的Qt系统)支持5种基本几何变换:

  • 平移(Translation): 将图形上的各点向同一方向移动相同距离。
  • 旋转(Rotation): 图形上各点在保持相对位置固定的同时,围绕一个定点运动。
  • 缩放(Scaling) : 在各轴方向上分别将图形放大(enlarge)或缩小(shrink)相应的倍数(factor)。
  • 错切(Shear Mapping) : 是 一种线性映射,将形状上的所有点向一个方向推移,推移的距离与各点到与该方向平行的一条直线的距离成正比。
  • 反射(Reflection): 图像通过一条直线(对称轴)变换成它的镜像。原图像上各点到对称轴的距离,和其在镜像上对应点到对称轴的距离相等。

下面的代码绘制了一辆简单的公共汽车。我们用它为基础,与本节中后续例子进行比较。

def draw_bus():
    """
    Draw a simple bus
    """
    set_color("lightgray")
    rect(60, 20, 270, 150)

    set_color("red")
    set_fill_color("blue")

    draw_circle(120, 120, 10)  # draw tyre
    draw_circle(200, 120, 10)  # draw tyre
    rect(80,40,250,120)

    # draw window
    x = 90
    while x < 175 :
        rect(x, 60, x+10, 70)
        x += 15

    # draw door
    rect(220, 60, 240, 120);
    line(230, 60, 230, 120);
    circle(230, 90, 5);

下面的程序绘制一辆未经变换的公共汽车。

from easygraphics import *
import draw_bus


def main():
    init_graph(500, 300)
    draw_bus.draw_bus()
    pause()
    close_graph()

easy_run(main)
../_images/09_no_transform.png

平移(Translation)

平移是最常见的几何变换。它将图形上各点在x轴方向上同时移动offset_x距离,在y轴方向上同时offset_y距离。也可以理解将原点和坐标系在x轴和y轴方向上分别移动了offset_x和offset_y。

下面的程序使用translate()函数来将坐标轴原点移动到绘图窗口的正中央,然后绘制公共汽车。

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)
    translate(250, 150)
    draw_bus.draw_bus()
    pause()
    close_graph()

easy_run(main)
../_images/09_translation.png

注意:set_origin()是translate()函数的别名。

旋转(Rotation)

使用rotate()函数来将坐标系绕点(x,y)进行顺时针旋转。

注意:rotate()函数和形状绘制函数(如draw_pie()等)中的角度方向是反的!

如果要作逆时针旋转,给一个负的旋转角度即可。

下面的程序中绘制了一辆绕其中心(105,65)逆时针旋转了45度的公共汽车

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)
    # rotate around the (105,65)
    rotate(-45, 105, 65)

    draw_bus.draw_bus()
    pause()
    close_graph()

easy_run(main)
../_images/09_rotation.png

缩放(Scaling)

我们可以使用scale()函数来对图形在x轴和y轴方向分别进行缩放。

下面的程序绘制了一辆在x轴方向上缩小,在y轴方向上放大的公共汽车。

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    scale(0.5, 2)
    draw_bus.draw_bus()

    pause()
    close_graph()

easy_run(main)
../_images/09_scale.png

错切(Shear Mapping)

我们使用shear()函数或它的别名skew()来围绕点(x,y)对图形进行错切。shear()需要两个参数”sv”和”sh”。错切时,原图上的每个点(x,y)被变换到(x+sh*y, y+sv*x)。我们可以通过下面的例子来了解错切的效果。

沿X轴方向错切

在下面的例子中,我们沿X轴方向对公共汽车进行错切。注意缺省状态下Y轴正向向下”

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    shear(0.5, 0)
    draw_bus.draw_bus()

    pause()
    close_graph()

easy_run(main)
../_images/09_skew_x.png

沿Y轴方向错切

在下面的例子中,我们沿Y轴方向对公共进行错切。

from easygraphics import *
import draw_bus


def main():
    init_graph(500, 300)

    shear(0, 0.5)
    draw_bus.draw_bus()

    pause()
    close_graph()

easy_run(main)
../_images/09_skew_y.png

同时沿两轴错切

在下面的例子中,我们同时沿X和Y轴两个方向对公共汽车进行错切。

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    shear(0.5, 0.5)
    draw_bus.draw_bus()

    pause()
    close_graph()

easy_run(main)
../_images/09_skew.png

反射(Reflection)

我们可以使用reflect()(或者它的别名mirror()和flip())来生成图像的镜像。它将图像以经过两点(x1,y1)和(x,y)的直线为对称轴进行反射。

以垂直线为轴反射

下面的程序将公共汽车进行水平翻转(以公共汽车的垂直中心线x=105为轴进行反射) :

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)
    reflect(105, 0, 105, 1)
    draw_bus.draw_bus()
    pause()
    close_graph()

easy_run(main)
../_images/09_flip_h.png

以水平线为轴反射

下面的程序将公共汽车进行垂直翻转(以公共汽车的水平中心线y=65为轴进行反射)

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    reflect(0, 65, 1, 65)

    draw_bus.draw_bus()
    pause()
    close_graph()

easy_run(main)
../_images/09_flip_v.png

以任意直线为轴反射

下面的程序将公共汽车以经过(0,300)和(500,0)的直线为轴进行反射。为了更好的观察效果,我们先画一辆无几何变换的公共汽车和反射轴,然后画反射后的公共汽车。

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    draw_bus.draw_bus()

    set_color("gray")
    set_line_style(LineStyle.DASH_LINE)
    line(0, 300, 500, 0)
    set_line_style(LineStyle.SOLID_LINE)

    reflect(0, 300, 500, 0)
    draw_bus.draw_bus()
    pause()
    close_graph()

easy_run(main)
../_images/09_flip.png

复合变换

几何变换可以进行复合。

在下面的例子中,我们首先将原点移动到绘图窗口的正中央,然后将公共汽车绕其中心旋转,然后对其沿中心错切,最后将其放大1.2倍。

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    # move the origin to the center of the image
    translate(250, 150)

    # rotate around the bus center
    translate(105, 65)
    rotate(180)
    translate(-105, -65)

    # shear arount the bus center
    translate(105, 65)
    shear(0.5, 0.5)
    translate(-105, -65)

    # scale
    scale(1.2, 1.2)
    draw_bus.draw_bus()
    pause()
    close_graph()

easy_run(main)
../_images/09_compound.png

在Y轴向上的坐标系中绘图

你可能已经注意到了,在图片上打开反射(镜像)后,图片上的文字也会被反射绘制。如果你需要按照普通的平面直角坐标系(Y轴向上)绘图,那么这可能不符合你的需要。

Easygraphics专门提供了set_flip_y()函数,来处理这种情况(只翻转图片,不翻转文字)。

需要注意的是,在图片翻转后,所有绘图函数中角度参数的含义也都随之改变。不翻转时,一个正的角度表示顺时针旋转;反转后,正的角度表示逆时针旋转。

下面的两个程序,第一个程序使用set_flip_y()函数来实现Y轴向上;第二个程序使用reflect(1,0)来实现。比较一下它们的结果。

使用set_flip_y()来实现Y轴向上:

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    translate(250, 150)
    translate(105, 65)
    rotate(-45)
    translate(-105, -65)

    set_flip_y(True)
    # reflect(1,0)

    translate(105, -65)
    shear(0.2, 0.2)
    translate(-105, 65)

    draw_bus.draw_bus()
    set_color("blue")
    draw_rect_text(0, 0, 210, 130, "This is a very good day!")
    pause()
    close_graph()

easy_run(main)
../_images/09_y_set.png

使用reflect(1,0)来实现Y轴向上:

from easygraphics import *
import draw_bus

def main():
    init_graph(500, 300)

    translate(250, 150)
    translate(105,65)
    rotate(-45)
    translate(-105,-65)

    reflect(1,0)

    translate(105, -65)
    shear(0.2,0.2)
    translate(-105, 65)

    draw_bus.draw_bus()
    set_color("blue")
    draw_rect_text(0,0,210,130,"This is a very good day!")
    pause()
    close_graph()

easy_run(main)
../_images/09_y_reflect.png