0457d434bf4781c55c4e1f8d9eab6b0ae27f19ce
[darcs-mirror-ozeanien.git] / cairoboard.py
1 #!/usr/bin/python
2 # encoding:utf8
3
4
5 import pygtk
6 pygtk.require('2.0')
7 import gtk
8 import cairo
9 import math
10 import random
11
12 # Constants
13
14 GRIDWIDTH = 70
15 SCALE = GRIDWIDTH/2
16 GRIDPAD = 30
17
18 PILE_X = 2 * GRIDPAD + 10 * GRIDWIDTH
19 PILE_Y = 3 * GRIDWIDTH
20
21 BH_X = 2 * GRIDPAD + 10*GRIDWIDTH
22 BH_Y = 5 * GRIDWIDTH
23
24 class CairoBoard(gtk.DrawingArea):
25     def __init__(self,board):
26         assert board
27         self.board = board
28
29         self.base_pixmap = None
30         self.hand_x = 0
31         self.hand_y = 0
32         
33         self.highlighted_slot = None
34
35         gtk.DrawingArea.__init__(self)
36         self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
37                 gtk.gdk.BUTTON_RELEASE_MASK |
38                 gtk.gdk.LEAVE_NOTIFY_MASK |
39                 gtk.gdk.EXPOSURE_MASK |
40                 gtk.gdk.POINTER_MOTION_MASK)
41         self.connect_after("expose-event",self.do_expose_event)
42         self.connect("motion-notify-event",self.hover)
43
44         self.set_size_request(
45             2 * GRIDPAD + 9 * GRIDWIDTH + 2 * GRIDWIDTH,
46             2 * GRIDPAD + 6 * GRIDWIDTH
47         )
48         
49         self.reset()
50
51     def reset(self):
52         self.queue_draw()
53
54     def do_expose_event(self,widget,event):
55         gc = self.window.new_gc()
56         gc.set_clip_rectangle(event.area)
57         cr = self.window.cairo_create()
58         # set a clip region for the expose event
59         cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
60         cr.clip()
61
62         self.draw_board(self.board, self.window)
63
64     def hover(self, widget, event):
65         self.hand_x = event.x
66         self.hand_y = event.y
67         if not self.board.hand.is_empty():
68             self.queue_draw()
69
70         slot = self.event2slot(event)
71         if self.highlighted_slot and self.highlighted_slot != slot: 
72             self.highlighted_slot.highlighted = False
73             self.highlighted_slot = None
74             self.queue_draw()
75         if slot:
76             slot.highlighted = True
77             self.highlighted_slot = slot
78             self.queue_draw()
79
80
81     def pileclick(self, event):
82         return (2*abs(event.x - PILE_X) < GRIDWIDTH and
83                 2*abs(event.y - PILE_Y + 3*SCALE) < GRIDWIDTH)
84
85     def pilerotclick(self, event):
86         if   PILE_Y + GRIDWIDTH/2 <= event.y <= PILE_Y + GRIDWIDTH and\
87                 PILE_X + GRIDWIDTH/4 <= event.x <= PILE_X + GRIDWIDTH:
88             return 1
89         elif PILE_Y + GRIDWIDTH/2 <= event.y <= PILE_Y + GRIDWIDTH and\
90                 PILE_X - GRIDWIDTH/4 >= event.x >= PILE_X - GRIDWIDTH:
91             return -1
92         else:
93             return 0
94
95
96     def event2slot(self, event):
97         x = int((event.x - GRIDPAD) // GRIDWIDTH)
98         y = int((event.y - GRIDPAD) // GRIDWIDTH)
99         if 0 <= x < len(self.board.slots) and 0 <= y < len(self.board.slots[x]):
100             return self.board.slots[x][y]
101         elif (2*abs(event.x - PILE_X) < GRIDWIDTH and
102               2*abs(event.y - PILE_Y) < GRIDWIDTH):
103             return self.board.pile
104         elif (2*abs(event.x - BH_X) < GRIDWIDTH and
105               2*abs(event.y - BH_Y) < GRIDWIDTH):
106             return self.board.bh
107         else:
108             return None
109
110     def draw_board(self, board, window):
111         self.draw_base(window)
112
113         for col in board.slots:
114             for slot in col:
115                 self.draw_boardslot(slot,window)
116
117         self.draw_bh(board.bh, window)
118         self.draw_points(board.points, window)
119         self.draw_pile(board.pile, window)
120         self.draw_hand(board.hand, window, self.hand_x, self.hand_y)
121
122     
123     def draw_arrow(self, cr):
124         cr.arc(1, 1, 0.5, 0, math.pi/2)
125         cr.set_source_rgb(0, 0, 0)
126         cr.set_line_width(0.1)
127         cr.set_line_cap(cairo.LINE_CAP_ROUND)
128         cr.move_to(1,    1.5)
129         cr.line_to(0.75, 1.5)
130         cr.move_to(0.75, 1.5)
131         cr.line_to(1   , 1.75)
132         cr.move_to(0.75, 1.5)
133         cr.line_to(1   , 1.25)
134         cr.stroke()
135
136     def draw_pile(self, pile, window):
137         cr = window.cairo_create()
138         cr.translate(PILE_X, PILE_Y)
139         cr.scale(SCALE, SCALE)
140
141         cr.set_source_rgb(0.8, 0.8, 0.8)
142         cr.rectangle(-1, -1, 2, 2)
143         cr.fill()
144
145         matrix = cr.get_matrix()
146         self.draw_arrow(cr)
147         cr.scale(-1,1)
148         self.draw_arrow(cr)
149         cr.set_matrix(matrix)
150
151         cr.set_line_cap(cairo.LINE_CAP_SQUARE)
152         cr.translate(0,-3)
153         if pile.cards:
154             cr.set_source_rgb(0, 0, 0.5)
155             cr.rectangle(-1, -1, 2, 2)
156             cr.fill()
157         else:
158             cr.set_source_rgb(0, 0, 0)
159             cr.set_line_width(0.05)
160             cr.rectangle(-1, -1, 2, 2)
161             cr.stroke()
162
163         cr.set_font_size(1)
164         cr.set_source_rgb(1,1,1)
165         (_,_,w,h,_,_) = cr.text_extents(str(len(pile.cards)))
166
167         cr.move_to(-w/2, h/2)
168         cr.show_text(str(len(pile.cards)))
169         cr.fill()
170
171         self.draw_slot(pile, window, PILE_X, PILE_Y)
172
173     def draw_points(self, points, window):
174         cr = window.cairo_create()
175         cr.translate(2 * GRIDPAD + 10*GRIDWIDTH, 6 * GRIDWIDTH)
176         cr.scale(SCALE, SCALE)
177
178         txt = "%ip" % points
179
180         cr.set_font_size(1)
181         cr.set_source_rgb(0,0,0)
182         (_,_,w,h,_,_) = cr.text_extents(txt)
183
184         cr.move_to(-w/2, h/2)
185         cr.show_text(txt)
186         cr.fill()
187
188     def draw_bh(self, bh, window):
189         cr = window.cairo_create()
190         cr.translate(BH_X, BH_Y)
191         cr.scale(SCALE, SCALE)
192
193         cr.rectangle(-1, -1, 2, 2)
194         cr.clip()
195
196         cr.set_source_rgb(0,0,0)
197         cr.arc(0, 0, 0.8, 0, 2*math.pi)
198         cr.fill()
199         
200         cr.set_font_size(1)
201         cr.set_source_rgb(1,1,1)
202         (_,_,w,h,_,_) = cr.text_extents(str(bh.count))
203
204         cr.move_to(-w/2, h/2)
205         cr.show_text(str(bh.count))
206         cr.fill()
207
208     def draw_boardslot(self, slot, window):
209         self.draw_slot(slot, window,
210             GRIDPAD + (slot.x+0.5) * GRIDWIDTH,
211             GRIDPAD + (slot.y+0.5) * GRIDWIDTH)
212
213     def draw_slot(self, slot, window, x, y):
214         if slot.card or slot.ship:
215             cr = window.cairo_create()
216             cr.translate(x, y)
217             cr.scale(SCALE, SCALE)
218
219             cr.rectangle(-1, -1, 2, 2)
220             cr.clip()
221
222             matrix = cr.get_matrix()
223             if slot.card:
224                 self.draw_card(slot.card, cr)
225                 cr.set_matrix(matrix)
226             if slot.ship:
227                 self.draw_ship(slot.ship, cr)
228                 cr.set_matrix(matrix)
229
230             if slot.highlighted:
231                 cr.set_source_rgba(1, 1, 1, 0.5)
232                 cr.rectangle(-1, -1, 2, 2)
233                 cr.fill()
234
235     def draw_ship(self, ship, cr):
236         cr.move_to(0.9, 0)
237         cr.arc(0, 0, 0.3, math.pi, 0) 
238         cr.line_to(-0.9,0)
239         cr.line_to(-0.6, 0.6)
240         cr.line_to( 0.6, 0.6)
241
242         cr.set_source_rgb(1, 0, 0)
243         cr.fill()
244
245     draw_hand = draw_slot
246
247     def draw_card(self, card, cr):
248         cr.rotate(-(card.rotation * math.pi / 2))
249
250         # Meer
251         cr.set_source_rgb(0, 0, 1)
252         cr.rectangle(-1, -1, 2, 2)
253         cr.fill()
254
255         cr.set_source_rgb(0, 1, 0)
256         cr.set_line_cap(cairo.LINE_CAP_ROUND)
257         cr.set_line_join(cairo.LINE_JOIN_ROUND)
258         cr.set_line_width(0.9)
259
260         p2c = [ (1,0), (0,-1), (-1,0), (0,1) ]
261
262         # Land
263         for island in card.islands:
264             point = island[0]
265             cr.move_to( *p2c[point] )
266             for point in island[1:]:
267                 cr.line_to( *p2c[point] )
268             cr.close_path()
269             cr.stroke()
270         
271         # Border
272         cr.set_source_rgb(0, 0, 0)
273         cr.set_line_cap(cairo.LINE_CAP_ROUND)
274         cr.set_line_width(0.05)
275         cr.rectangle(-1, -1, 2, 2)
276         cr.stroke()
277
278     def draw_base(self,window):
279         if not self.base_pixmap:
280             self.base_pixmap = gtk.gdk.Pixmap(window,
281                     2*GRIDPAD + 9*GRIDWIDTH, 2*GRIDPAD + 6*GRIDWIDTH)
282
283             self.base_pixmap.draw_drawable(self.base_pixmap.new_gc(), window, 0, 0, 0, 0,
284                     2*GRIDPAD + 9*GRIDWIDTH, 2*GRIDPAD + 6*GRIDWIDTH)
285
286             cr = self.base_pixmap.cairo_create()
287
288
289             cr.translate(GRIDPAD, GRIDPAD)
290             cr.scale(GRIDWIDTH, GRIDWIDTH)
291
292             # Sea Background
293             # Unten
294             pat = cairo.LinearGradient (0, 5, 0, 5.5)
295             pat.add_color_stop_rgba(0, 0, 0, 1,1)
296             pat.add_color_stop_rgba(1, 0, 0, 1,0)
297             cr.rectangle(1, 5, 7, 0.5)
298             cr.set_source(pat)
299             cr.fill()
300
301             # Oben
302             pat = cairo.LinearGradient (0, 0, 0, -0.5)
303             pat.add_color_stop_rgba(0, 0, 0, 1,1)
304             pat.add_color_stop_rgba(1, 0, 0, 1,0)
305             cr.rectangle(1, -0.5, 7, 0.5)
306             cr.set_source(pat)
307             cr.fill()
308
309             # Links
310             pat = cairo.LinearGradient (1, 0, 0.5, 0)
311             pat.add_color_stop_rgba(0, 0, 0, 1,1)
312             pat.add_color_stop_rgba(1, 0, 0, 1,0)
313             cr.rectangle(0.5, 0, 0.5, 5)
314             cr.set_source(pat)
315             cr.fill()
316
317             # Rechts
318             pat = cairo.LinearGradient (8, 0, 8.5, 0)
319             pat.add_color_stop_rgba(0, 0, 0, 1,1)
320             pat.add_color_stop_rgba(1, 0, 0, 1,0)
321             cr.rectangle(8, 0, 0.5, 5)
322             cr.set_source(pat)
323             cr.fill()
324
325             # Oben Links
326             pat = cairo.RadialGradient(1,0,0, 1,0, 0.5)
327             pat.add_color_stop_rgba(0, 0, 0, 1, 1)
328             pat.add_color_stop_rgba(1, 0, 0, 1, 0)
329             cr.rectangle(0.5, -0.5, 0.5, 0.5)
330             cr.set_source(pat)
331             cr.fill()
332
333             # Oben Rechts
334             pat = cairo.RadialGradient(8,0,0, 8,0, 0.5)
335             pat.add_color_stop_rgba(0, 0, 0, 1, 1)
336             pat.add_color_stop_rgba(1, 0, 0, 1, 0)
337             cr.rectangle(8, -0.5, 8.5, 0.5)
338             cr.set_source(pat)
339             cr.fill()
340
341             # Unten Rechts
342             pat = cairo.RadialGradient(8,5,0, 8,5, 0.5)
343             pat.add_color_stop_rgba(0, 0, 0, 1, 1)
344             pat.add_color_stop_rgba(1, 0, 0, 1, 0)
345             cr.rectangle(8, 5, 8.5, 5.5)
346             cr.set_source(pat)
347             cr.fill()
348
349             # Unten Links
350             pat = cairo.RadialGradient(1,5,0, 1,5, 0.5)
351             pat.add_color_stop_rgba(0, 0, 0, 1, 1)
352             pat.add_color_stop_rgba(1, 0, 0, 1, 0)
353             cr.rectangle(0.5, 5, 1, 5.5)
354             cr.set_source(pat)
355             cr.fill()
356
357
358             # Frame
359             cr.set_source_rgb(1,1,1)
360             cr.set_line_width(0.05)
361             cr.set_line_cap(cairo.LINE_CAP_ROUND)
362             for x in range(0,8):
363                 cr.move_to(1+x, 0)
364                 cr.line_to(1+x, 5.5)
365                 cr.stroke()
366             for y in range(0,6):
367                 cr.move_to(0.5, y)
368                 cr.line_to(8.5, y)
369                 cr.stroke()
370
371         window.draw_drawable(window.new_gc(), self.base_pixmap, 0, 0, 0, 0,
372                 2*GRIDPAD + 9*GRIDWIDTH, 2*GRIDPAD + 6*GRIDWIDTH)
373
374 class GrassBoard(CairoBoard):
375         #Karte f├╝llen
376         def draw_card(self, card, cr):
377             cr.rotate(-(card.rotation * math.pi / 2))
378             orientation=0;
379             cr.set_source_rgb(0.6, 0.4, 0.3)
380             cr.rectangle(-1, -1, 2, 2)
381             cr.fill()
382             p2c = [ (0.7,0), (0,-0.7), (-0.7,0), (0,0.7) ]
383             matrix=cr.get_matrix()
384             self.draw_gras(card, cr, orientation)
385             for island in card.islands:
386                 for dir in island:
387                     cr.translate(*p2c[dir])
388                     self.draw_flower(card, cr)
389                     cr.set_matrix(matrix)
390             #self.draw_gras(card, cr, orientation)
391             #self.draw_flower(card, cr)
392
393         #Einen Grashalm malen
394         def draw_gras(self, card, cr, orientation):
395             #x=random.uniform(0.1,0.2)
396             #y=random.uniform(0.2,0.3)
397             matrix=cr.get_matrix()
398             if orientation == 0: 
399                 cr.scale(0.15, 0.25)
400             else:
401                 cr.scale(-x, y)
402             cr.set_source_rgb(0, 1, 0)
403             cr.move_to(-1,-1)
404             cr.curve_to(0,-1,1,0,1,1)
405             cr.curve_to(0,1,0,-1,-1,-1)
406             cr.fill_preserve()
407             cr.set_source_rgb(0, 0, 0)
408             cr.set_line_width(0.1)
409             cr.stroke()
410             cr.set_matrix(matrix)
411             
412         #Eine Blume malen
413         def draw_flower(self, card,cr):
414             matrix=cr.get_matrix()
415             cr.scale(0.25, 0.25)
416             for n in range(5):
417                 cr.set_source_rgb(1, 0, 0)
418                 cr.move_to(0,0)
419                 cr.curve_to(0,-0.8,-0.2,-1,-1,-1)
420                 cr.curve_to(-1,-0.2,-0.8,0.2,0,0)
421                 cr.fill_preserve()
422                 cr.set_source_rgb(0, 0, 0)
423                 cr.set_line_width(0.1)
424                 cr.stroke()
425                 cr.rotate(2* math.pi / 5)
426             cr.set_matrix(matrix)
427         # Meer
428         #cr.set_source_rgb(0, 0, 1)
429         #cr.rectangle(-1, -1, 2, 2)
430         #cr.fill()
431
432         #cr.set_source_rgb(0, 1, 0)
433         #cr.set_line_cap(cairo.LINE_CAP_ROUND)
434         #cr.set_line_width(0.9)
435
436         #p2c = [ (1,0), (0,-1), (-1,0), (0,1) ]
437
438         # Land
439         #for island in card.islands:
440         #    point = island[0]
441         #    cr.move_to( *p2c[point] )
442         #    for point in island[1:] + island[0:1]:
443         #        cr.line_to( *p2c[point] )
444         #    cr.stroke()
445         
446         # Border
447         #cr.set_source_rgb(0, 0, 0)
448         #cr.set_line_cap(cairo.LINE_CAP_ROUND)
449         #cr.set_line_width(0.05)
450         #cr.rectangle(-1, -1, 2, 2)
451         #cr.stroke()
452
453 # vim:ts=4:sw=4:sts=4:et