变换
PyCairo 图形学编程指南的这个部分,我们将讨论变换。
一个仿射变换由0个或者多个线性变换(旋转,放缩或切变)和平移(移位)组成。一些线性变换可以被结合起来放进一个单一的矩阵中。一个旋转是将一个精确的对象沿着一个固定的点做移动的变换。一个放缩是将对象进行放大或缩小的变换。放缩系数在所有方向上都是一致的。一个平移,是在一个特定的方向上,将每一个点都移动固定的距离的变换。一个切变是一个将一个对象正交的移动向给定的轴,同时保持轴某一侧的值比另一侧更大的变换。
来源: (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()
这个例子先画了一个矩形,然后我们做一个平移,并多次画了相同的矩形。
cr.translate(30, 30)
translate()函数通过平移用户空间原点来修改当前的平移矩阵。在我们的例子中,我们在两个方向上将原点移动20个单位。
Figure: Translation operation
切变
在下面的例子中,我们执行一个切变操作。切变是沿着一个特定的轴来扭曲对象的操作。并没有一个方法来完成这个操作。我们需要创建我们自己的变换矩阵。注意,每一个仿射变换都可以通过创建一个变换矩阵来执行。
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中,我们执行一个简单的切变操作。
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指南的这个部分,我们讨论了变换。
来源:oschina
链接:https://my.oschina.net/u/919237/blog/133339