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指南的這個部分,咱們討論了變換。