[翻譯]PyCairo指南--變換

變換

PyCairo 圖形學編程指南的這個部分,咱們將討論變換。 python

一個仿射變換由0個或者多個線性變換(旋轉,放縮或切變)和平移(移位)組成。一些線性變換能夠被結合起來放進一個單一的矩陣中。一個旋轉是將一個精確的對象沿着一個固定的點作移動的變換。一個放縮是將對象進行放大或縮小的變換。放縮係數在全部方向上都是一致的。一個平移,是在一個特定的方向上,將每個點都移動固定的距離的變換。一個切變是一個將一個對象正交的移動向給定的軸,同時保持軸某一側的值比另外一側更大的變換。 web

來源: (wikipedia.org, freedictionary.com)
編程

平移

下面的例子描述了一個簡單的平移。 函數

def on_draw(self, wdith, cr):
        cr.set_source_rgb(0.2, 0.3, 0.8)
        cr.rectangle(10, 10, 60, 60)
        cr.fill()
        
        cr.translate(30, 30)
        cr.set_source_rgb(0.8, 0.3, 0.2)
        cr.rectangle(0, 0, 60, 60)
        cr.fill()
        
        cr.translate(60, 60)
        cr.set_source_rgb(0.8, 0.8, 0.2)
        cr.rectangle(0, 0, 60, 60)
        cr.fill()
        
        cr.translate(70, 70)
        cr.set_source_rgb(0.3, 0.8, 0.8)
        cr.rectangle(0, 0, 60, 60)
        cr.fill()

這個例子先畫了一個矩形,而後咱們作一個平移,並屢次畫了相同的矩形。 ui

cr.translate(30, 30)

 translate()函數經過平移用戶空間原點來修改當前的平移矩陣。在咱們的例子中,咱們在兩個方向上將原點移動20個單位。 spa


Figure: Translation operation
rest

切變

在下面的例子中,咱們執行一個切變操做。切變是沿着一個特定的軸來扭曲對象的操做。並無一個方法來完成這個操做。咱們須要建立咱們本身的變換矩陣。注意,每個仿射變換均可以經過建立一個變換矩陣來執行。 code

def on_draw(self, wdith, cr):
        cr.set_source_rgb(0.6, 0.6, 0.6)
        cr.rectangle(20, 30, 80, 50)
        cr.fill()
        
        mtx = cairo.Matrix(1.0, 0.5, 0.0, 1.0, 0.0, 0.0)
        
        cr.transform(mtx)
        cr.rectangle(130, 30, 80, 50)
        cr.fill()

這段code中,咱們執行一個簡單的切變操做。 orm

mtx = cairo.Matrix(1.0, 0.5, 0.0, 1.0, 0.0, 0.0)

這個變換將y值切變爲x值的0.5倍。 對象

cr.transform(mtx)

咱們用 transform()方法來執行變換。


Figure: Shearing operation

放縮

下一個例子演示了一個放縮操做。放縮是一個將對象放大或縮小的變換操做。

def on_draw(self, wdith, cr):
        cr.set_source_rgb(0.2, 0.3, 0.8)
        cr.rectangle(10, 10, 150, 150)
        cr.fill()
        
        cr.scale(0.6, 0.6)
        cr.set_source_rgb(0.8, 0.3, 0.2)
        cr.rectangle(30, 30, 150, 150)
        cr.fill()
        
        cr.scale(0.8, 0.8)
        cr.set_source_rgb(0.8, 0.8, 0.2)
        cr.rectangle(50, 50, 150, 150)
        cr.fill()

咱們畫了三個90×90 px大小的矩形。對於它們中的2個,咱們執行了放縮操做。

cr.scale(0.6, 0.6)
        cr.set_source_rgb(0.8, 0.3, 0.2)
        cr.rectangle(30, 30, 150, 150)
        cr.fill()

咱們一致地用一個因子0.6來縮小一個矩形。

cr.scale(0.8, 0.8)
        cr.set_source_rgb(0.8, 0.8, 0.2)
        cr.rectangle(50, 50, 150, 150)
        cr.fill()

這裏咱們用因子0.8執行了另一個放縮操做。若是咱們看圖片,咱們將看到,第三個黃色矩形是最小的。即便咱們已經使用了另外一個更小的放縮因子。這是因爲變換操做是累積的。事實上,第三個矩形是用一個放縮因子0.528(0.6 ×0.8)來進行放縮的。


Figure: Scaling operation

隔離變換

變換是累積的。爲了隔離各個變換操做,咱們可使用save()restore()操做。save()方法建立一份當前的繪製上下文狀態的拷貝,並將它存進一個保存狀態的內部棧中。restore()方法將會重建上下文到保存的狀態。

def on_draw(self, wdith, cr):
        cr.set_source_rgb(0.2, 0.3, 0.8)
        cr.rectangle(10, 10, 120, 120)
        cr.fill()
        
        cr.save()
        cr.scale(0.6, 0.6)
        cr.set_source_rgb(0.8, 0.3, 0.2)
        cr.rectangle(30, 30, 120, 120)
        cr.fill()
        cr.restore()
        
        cr.save()
        cr.scale(0.8, 0.8)
        cr.set_source_rgb(0.8, 0.8, 0.2)
        cr.rectangle(50, 50, 120, 120)
        cr.fill()
        cr.restore()

這個例子中咱們放縮了兩個矩形。這一次咱們隔離了各個放縮操做。

cr.save()
        cr.scale(0.6, 0.6)
        cr.set_source_rgb(0.8, 0.3, 0.2)
        cr.rectangle(30, 30, 120, 120)
        cr.fill()
        cr.restore()

咱們經過將scale()方法放到save()restore()方法之間來隔離放縮操做。


Figure: Isolating transformations

注意,第三個黃色矩形比第二個紅色的要大。

環狀線圈

在下面的例子中,咱們經過旋轉一束橢圓形來建立一個複雜的形狀。

#!/usr/bin/python
'''
ZetCode PyCairo tutorial

This program creates a 'donut' shape in PyCairo.

author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''

import gtk
import cairo
import math

class MainWindow(gtk.Window):
    def __init__(self):
        super(self.__class__, self).__init__()
        self.init_ui()

    def init_ui(self):
        self.darea = gtk.DrawingArea()
        self.darea.connect("expose_event", self.expose)
        self.add(self.darea)
        
        self.set_title("Donut")
        self.resize(350, 250)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect("delete-event", gtk.main_quit)
        self.show_all()

    def expose(self, widget, event):
        self.context = widget.window.cairo_create()
        self.on_draw(350, self.context)
        
    def on_draw(self, wdith, cr):
        cr.set_line_width(0.5)
        w, h = self.get_size()
        
        cr.translate(w/2, h/2)
        cr.arc(0, 0, 120, 0, 2 *math.pi)
        cr.stroke()
        
        for i in range(36):
            cr.save()
            cr.rotate(i * math.pi / 36)
            cr.scale(0.3, 1)
            cr.arc(0, 0, 120, 0, 2 *math.pi)
            cr.restore()
            cr.stroke()

def main():
    window = MainWindow()
    gtk.main()
        
if __name__ == "__main__":
    main()

咱們將執行旋轉和放縮操做。咱們也將保存和恢復PyCairo上下文。

cr.translate(w/2, h/2)
        cr.arc(0, 0, 120, 0, 2 *math.pi)
        cr.stroke()

在GTK窗口的中間,咱們建立了一個圓形。這將是咱們的橢圓形的邊界圓形。

for i in range(36):
            cr.save()
            cr.rotate(i * math.pi / 36)
            cr.scale(0.3, 1)
            cr.arc(0, 0, 120, 0, 2 *math.pi)
            cr.restore()
            cr.stroke()

咱們沿着咱們的邊界圓形的路徑建立了36個橢圓形。咱們經過save()restore()方法,將各個旋轉和放縮操做隔離開。


Figure: Donut

星星

下一個例子中,顯示了一個旋轉和放縮的星星。

#!/usr/bin/python
'''
ZetCode PyCairo tutorial

This is a star example which 
demostrates scaling, translation and
rotation operations in PyCairo.

author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''

import gtk, glib
import cairo

class cv(object):
    points = (
              (0, 85), 
              (75, 75),
              (100, 10),
              (125, 75),
              (200, 85),
              (150, 125),
              (160, 190),
              (100, 150),
              (40, 190),
              (50, 125),
              (0, 85)
              )
    
    SPEED = 20
    TIMER_ID = 1

class MainWindow(gtk.Window):
    def __init__(self):
        super(self.__class__, self).__init__()
        self.init_ui()
        self.init_vars()

    def init_ui(self):
        self.darea = gtk.DrawingArea()
        self.darea.connect("expose_event", self.expose)
        self.add(self.darea)
        
        self.set_title("Star")
        self.resize(400, 300)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect("delete-event", gtk.main_quit)
        self.show_all()
        
    def init_vars(self):
        self.angle = 0
        self.scale = 1
        self.delta = 0.01
        
        glib.timeout_add(cv.SPEED, self.on_timer)
    
    def on_timer(self):
        if self.scale < 0.01 or self.scale > 0.99:
            self.delta = - self.delta
        
        self.scale += self.delta
        self.angle += 0.01
        
        self.darea.queue_draw()

        return True

    def expose(self, widget, event):
        self.context = widget.window.cairo_create()
        self.on_draw(350, self.context)
        
    def on_draw(self, wdith, cr):
        w, h = self.get_size()
        
        cr.set_source_rgb(0, 0.44, 0.7)
        cr.set_line_width(1)
        
        cr.translate(w/2, h/2)
        cr.rotate(self.angle)
        cr.scale(self.scale, self.scale)
        
        for i in range(10):
            cr.line_to(cv.points[i][0], cv.points[i][1])
        
        cr.fill()

def main():
    window = MainWindow()
    gtk.main()
        
if __name__ == "__main__":
    main()

這個例子中,咱們建立了一個星星對象。咱們將平移它,旋轉它並放縮它。

points = (
              (0, 85), 
              (75, 75),
              (100, 10),
              (125, 75),
              (200, 85),
    ...

星星對象將從這些點來建立。

def init_vars(self):
        self.angle = 0
        self.scale = 1
        self.delta = 0.01
    ...

init_vars()方法中,我麼初始化三個變量。self.angle被用於旋轉,self.scale被用於放縮星星對象。self.delta變量控制什麼時候星星不斷放大而什麼時候又不斷縮小。

glib.timeout_add(cv.SPEED, self.on_timer)
每個 cv.SPEED ms,on_timer()方法都會被調到。
if self.scale < 0.01 or self.scale > 0.99:
            self.delta = - self.delta

這幾行控制星星是逐漸變大仍是變小。

cr.translate(w/2, h/2)
        cr.rotate(self.angle)
        cr.scale(self.scale, self.scale)

咱們將星星移動到窗口的中心。旋轉並放縮它。

for i in range(10):
            cr.line_to(cv.points[i][0], cv.points[i][1])
        
        cr.fill()

咱們在這裏繪製星星對象。

Figure: Star

在PyCairo指南的這個部分,咱們討論了變換。

相關文章
相關標籤/搜索