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指南的这个部分,咱们讨论了变换。