class Stator(): """ Create a sprite for a stator """ def __init__(self, sprites, path, name, x, y, w, h, svg_engine=None, calculate=None, result=None): if svg_engine is None: self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) else: self.spr = Sprite(sprites, x, y, svg_str_to_pixbuf(svg_engine().svg)) self.spr.type = name self.name = name self.calculate = calculate self.result = result def draw(self, layer=1000): self.spr.set_layer(layer) def match(self, sprite): if self.spr == sprite: return True return False def move(self, dx, dy): self.spr.move((dx, dy)) def move_relative(self, dx, dy): self.spr.move_relative((dx, dy)) def hide(self): self.spr.hide()
class Tile: def __init__(self, sprites, svg, svgs, tile_type='tile', number=0): self.highlight = [svg_str_to_pixbuf(svg)] self.spr = Sprite(sprites, 0, 0, self.highlight[0]) for s in svgs: self.highlight.append(svg_str_to_pixbuf(s)) self.paths = [] # [[N, E, S, W], [N, E, S, W]] self.shape = None self.orientation = 0 self.type = tile_type self.number = number self.value = 1 self.spr.set_label_color('#FF0000') def set_value(self, value): self.value = value def get_value(self): return self.value def set_paths(self, paths): for c in paths: self.paths.append(c) def get_paths(self): return self.paths def reset(self): self.spr.set_layer(HIDE) self.shape = None self.spr.set_shape(self.highlight[0]) while self.orientation != 0: self.rotate_clockwise() def set_shape(self, path): if self.shape is None: self.spr.set_shape(self.highlight[path + 1]) self.shape = path elif self.shape != path: self.spr.set_shape(self.highlight[-1]) def rotate_clockwise(self): """ rotate the tile and its paths """ for i in range(len(self.paths)): west = self.paths[i][WEST] self.paths[i][WEST] = self.paths[i][SOUTH] self.paths[i][SOUTH] = self.paths[i][EAST] self.paths[i][EAST] = self.paths[i][NORTH] self.paths[i][NORTH] = west for h in range(len(self.highlight)): self.highlight[h] = self.highlight[h].rotate_simple(270) self.spr.set_shape(self.highlight[0]) self.orientation += 90 self.orientation %= 360 def show_tile(self): self.spr.set_layer(CARDS) def hide(self): self.spr.move((-self.spr.get_dimensions()[0], 0))
class Slide(Stator): """ Create a sprite for a slide """ def __init__(self, sprites, path, name, x, y, w, h, svg_engine=None, function=None): if svg_engine is None: self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) else: self.spr = Sprite(sprites, x, y, svg_str_to_pixbuf(svg_engine().svg)) self.tab_dx = [0, SWIDTH - TABWIDTH] self.tab_dy = [2 * SHEIGHT, 2 * SHEIGHT] self.tabs = [] self.tabs.append(Tab(sprites, path, 'tab', x + self.tab_dx[0], y + self.tab_dy[0], TABWIDTH, SHEIGHT)) self.tabs.append(Tab(sprites, path, 'tab', x + self.tab_dx[1], y + self.tab_dy[1], TABWIDTH, SHEIGHT)) self.calculate = function self.name = name def add_textview(self, textview, i=0): self.tabs[i].textview = textview self.tabs[i].textbuffer = textview.get_buffer() def set_fixed(self, fixed): for tab in self.tabs: tab.fixed = fixed def match(self, sprite): if sprite == self.spr or sprite == self.tabs[0].spr or \ sprite == self.tabs[1].spr: return True return False def draw(self, layer=1000): self.spr.set_layer(layer) self.spr.draw() for tab in self.tabs: tab.draw() def move(self, dx, dy): self.spr.move((dx, dy)) for i, tab in enumerate(self.tabs): tab.move(dx + self.tab_dx[i], dy + self.tab_dy[i]) def move_relative(self, dx, dy): self.spr.move_relative((dx, dy)) for i, tab in enumerate(self.tabs): tab.move_relative(dx, dy) def hide(self): self.spr.hide() for tab in self.tabs: tab.hide() def label(self, label, i=0): self.tabs[i].label(label)
class Tab(): """ Create tabs for the slide; include a TextView for OSK input """ def __init__(self, sprites, path, name, x, y, w, h): self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) self.spr.label = "1.0" self.spr.type = name self.name = name self.width = w self.textview = None self.textbuffer = None self.fixed = None self.textview_y_offset = 0 def label(self, label): if self.textbuffer is not None: self.textbuffer.set_text(label) def _move_textview(self, x, y): y += self.textview_y_offset if self.textview is not None: if x > 0 and x < Gdk.Screen.width() - self.width and y > 0: self.fixed.move(self.textview, x, y) self.textview.show() else: self.textview.hide() def move(self, x, y): self.spr.move((x, y)) self._move_textview(x, y) def move_relative(self, dx, dy): self.spr.move_relative((dx, dy)) x, y = self.spr.get_xy() self._move_textview(x, y) def draw(self, layer=100): self.spr.set_layer(layer) self.spr.draw() x, y = self.spr.get_xy() self._move_textview(x, y) def hide(self): self.spr.hide()
def _test(self, easter_egg=False): ''' Test to see if we estimated correctly ''' self.timeout = None delta = self.ball.width() / 4 x = self.ball.ball_x() + self.ball.width() / 2 f = self.ball.width() / 2 + int(self.fraction * self.bar.width()) self.bar.mark.move( (int(f - self.bar.mark_width() / 2), self.bar.bar_y() - 2)) if self.challenges[self.n][2] == 0: # label the column spr = Sprite(self.sprites, 0, 0, self.blank_graphic) spr.set_label(self.label) spr.move((int(self.n * 25), 0)) spr.set_layer(-1) self.challenges[self.n][2] += 1 if x > f - delta and x < f + delta: if not easter_egg: spr = Sprite(self.sprites, 0, 0, self.smiley_graphic) self.correct += 1 gobject.idle_add(play_audio_from_file, self, self.path_to_success) else: if not easter_egg: spr = Sprite(self.sprites, 0, 0, self.frown_graphic) gobject.idle_add(play_audio_from_file, self, self.path_to_failure) if easter_egg: spr = Sprite(self.sprites, 0, 0, self.egg_graphic) spr.move((int(self.n * 25), int(self.challenges[self.n][2] * 25))) spr.set_layer(-1) # after enough correct answers, up the difficulty if self.correct == len(self.challenges) * 2: self.challenge += 1 if self.challenge < len(CHALLENGES): for challenge in CHALLENGES[self.challenge]: self.challenges.append(challenge) else: self.expert = True self.count += 1 self.dx = 0. # stop horizontal movement between bounces
def _test(self, easter_egg=False): ''' Test to see if we estimated correctly ''' self._timeout = None if self._expert: delta = self.ball.width() / 6 else: delta = self.ball.width() / 3 x = self.ball.ball_x() + self.ball.width() / 2 f = int(self._fraction * self.bar.width()) self.bar.mark.move((int(f - self.bar.mark_width() / 2), int(self.bar.bar_y() + self._mark_offset(f)))) if self._challenges[self._n][2] == 0: # label the column spr = Sprite(self._sprites, 0, 0, self.blank_graphic) spr.set_label(self._label) spr.move((int(self._n * 27), 0)) spr.set_layer(-1) self._challenges[self._n][2] += 1 if x > f - delta and x < f + delta: spr = Sprite(self._sprites, 0, 0, self.smiley_graphic) self._correct += 1 GObject.idle_add(play_audio_from_file, self, self._path_to_success) else: spr = Sprite(self._sprites, 0, 0, self.frown_graphic) GObject.idle_add(play_audio_from_file, self, self._path_to_failure) spr.move((int(self._n * 27), int(self._challenges[self._n][2] * 27))) spr.set_layer(-1) # after enough correct answers, up the difficulty if self._correct == len(self._challenges) * 2: self._challenge += 1 if self._challenge < len(CHALLENGES): for challenge in CHALLENGES[self._challenge]: self._challenges.append(challenge) else: self._expert = True self.count += 1 self._dx = 0. # stop horizontal movement between bounces
def _test(self, easter_egg=False): ''' Test to see if we estimated correctly ''' if self._expert: delta = self.ball.width() / 6 else: delta = self.ball.width() / 3 x = self.ball.ball_x() + self.ball.width() / 2 f = int(self._fraction * self.bar.width()) self.bar.mark.move((int(f - self.bar.mark_width() / 2), int(self.bar.bar_y() + self._mark_offset(f)))) if self._challenges[self._n][2] == 0: # label the column spr = Sprite(self._sprites, 0, 0, self.blank_graphic) spr.set_label(self._label) spr.move((int(self._n * 27), 0)) spr.set_layer(-1) self._challenges[self._n][2] += 1 if x > f - delta and x < f + delta: spr = Sprite(self._sprites, 0, 0, self.smiley_graphic) self._correct += 1 aplay.play(self._path_to_success) else: spr = Sprite(self._sprites, 0, 0, self.frown_graphic) aplay.play(self._path_to_failure) spr.move((int(self._n * 27), int(self._challenges[self._n][2] * 27))) spr.set_layer(-1) # after enough correct answers, up the difficulty if self._correct == len(self._challenges) * 2: self._challenge += 1 if self._challenge < len(CHALLENGES): for challenge in CHALLENGES[self._challenge]: self._challenges.append(challenge) else: self._expert = True self.count += 1 self._dx = 0. # stop horizontal movement between bounces
class Game(): ''' OLPC XO man color changer designed in memory of Nat Jacobson ''' def __init__(self, canvas, parent=None, mycolors=['#A0FFA0', '#FF8080']): self._activity = parent self.colors = [mycolors[0]] self.colors.append(mycolors[1]) self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent self._canvas.connect("draw", self.__draw_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - GRID_CELL_SIZE self._scale = self._width / 1200. self.press = None self.dragpos = [0, 0] self.startpos = [0, 0] self._dot_cache = {} self._xo_cache = {} self._radius = 22.5 self._stroke_width = 9.5 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] self._xo_man = None self._generate_bg('#FFF') # First dot, starting angle self._cxy = [self._width / 2, self._height / 2] self._xy = [ self._width / 2 + 120 * self._scale, self._height / 2 - self._radius * self._scale ] self._angle = 0 self._dot_size_plus = self._radius * 3 * self._scale self._min = -self._dot_size_plus / 3 self._max = self._height - (self._dot_size_plus / 2.2) self._zones = [] self._calc_zones() self._generate_spiral() def _calc_zones(self): for color in colors: rgb1 = _from_hex(color[0]) rgb2 = _from_hex(color[1]) dv = _contrast(rgb1, rgb2) dh = _delta_hue(rgb1, rgb2) self._zones.append(_zone(dv, dh)) def _calc_next_dot_position(self): ''' calculate spiral coordinates ''' dx = self._xy[0] - self._cxy[0] dy = self._xy[1] - self._cxy[1] r = sqrt(dx * dx + dy * dy) c = 2 * r * pi a = atan2(dy, dx) da = (self._dot_size_plus / c) * 2 * pi a += da r += self._dot_size_plus / (c / self._dot_size_plus) self._xy[0] = r * cos(a) + self._cxy[0] self._xy[1] = r * sin(a) + self._cxy[1] if self._xy[1] < self._min or self._xy[1] > self._max: self._calc_next_dot_position() def _generate_spiral(self): ''' Make a new set of dots for a sprial ''' for z in range(4): for i in range(len(colors)): if self._zones[i] == z: self._dots.append( Sprite(self._sprites, self._xy[0], self._xy[1], self._new_dot(colors[i]))) self._dots[-1].type = i self._calc_next_dot_position() if self._xo_man is None: x = 510 * self._scale y = 280 * self._scale self._xo_man = Sprite(self._sprites, x, y, self._new_xo_man(self.colors)) self._xo_man.type = None def move_dot(self, i, x, y): self._dots[i].move((x, y)) def get_dot_xy(self, i): return self._dots[i].get_xy() def move_xo_man(self, x, y): self._xo_man.move((x, y)) def get_xo_man_xy(self): return self._xo_man.get_xy() def rotate(self): x, y = self._dots[0].get_xy() for i in range(len(colors) - 1): self._dots[i].move(self._dots[i + 1].get_xy()) self._dots[-1].move((x, y)) def _generate_bg(self, color): ''' a background color ''' self._bg = Sprite(self._sprites, 0, 0, self._new_background(color)) self._bg.set_layer(0) self._bg.type = None def adj_background(self, color): ''' Change background ''' self._bg.set_image(self._new_background(color)) self._bg.set_layer(0) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) self.dragpos = [x, y] spr = self._sprites.find_sprite((x, y)) if spr == None or spr == self._bg: return self.startpos = spr.get_xy() self.press = spr def _mouse_move_cb(self, win, event): """ Drag a rule with the mouse. """ if self.press is None: self.dragpos = [0, 0] return True win.grab_focus() x, y = map(int, event.get_coords()) dx = x - self.dragpos[0] dy = y - self.dragpos[1] self.press.move_relative((dx, dy)) self.dragpos = [x, y] def _button_release_cb(self, win, event): if self.press == None: return True if _distance(self.press.get_xy(), self.startpos) < 20: if type(self.press.type) == int: self.i = self.press.type self._new_surface() self.press.move(self.startpos) self.press = None def _new_surface(self): self.colors[0] = colors[self.i][0] self.colors[1] = colors[self.i][1] self._xo_man.set_image(self._new_xo_man(colors[self.i])) self._xo_man.set_layer(100) def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def do_expose_event(self, event): ''' Handle the expose-event by drawing ''' # Restrict Cairo to the exposed area cr = self._canvas.window.cairo_create() cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self._sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def _new_dot(self, color): ''' generate a dot of a color color ''' if True: # not color in self._dot_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(60 * self._scale) self._svg_height = int(60 * self._scale) pixbuf = svg_str_to_pixbuf( self._header() + \ '<circle cx="%f" cy="%f" r="%f" stroke="%s" fill="%s" \ stroke-width="%f" visibility="visible" />' % ( 30 * self._scale, 30 * self._scale, self._radius * self._scale, self._stroke, self._fill, self._stroke_width * self._scale) + \ self._footer()) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._svg_width, self._svg_height) context = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() # self._dot_cache[color] = surface return surface # self._dot_cache[color] def _new_background(self, color): ''' Background color ''' self._svg_width = int(self._width) self._svg_height = int(self._height) string = \ self._header() + \ '<rect width="%f" height="%f" x="%f" \ y="%f" fill="%s" stroke="none" visibility="visible" />' % ( self._width, self._height, 0, 0, color) + \ self._footer() pixbuf = svg_str_to_pixbuf(string) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._svg_width, self._svg_height) context = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() return surface def _new_xo_man(self, color): ''' generate a xo-man of a color color ''' if True: # not color in self._xo_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(240. * self._scale) self._svg_height = int(260. * self._scale) string = \ self._header() + \ '<g>' + \ '<g id="XO">' + \ '<path id="Line1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Line2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Fill1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._fill, 17 * self._scale) + \ '<path id="Fill2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._fill, 17 * self._scale) + \ '<circle id="Circle" cx="%f" cy="%f" r="%f" \ fill="%s" stroke="%s" stroke-width="%f" visibility="visible" />' % ( 120 * self._scale, 61.5 * self._scale, 27.5 * self._scale, self._fill, self._stroke, 11 * self._scale) + \ '</g></g>' + \ self._footer() pixbuf = svg_str_to_pixbuf(string) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._svg_width, self._svg_height) context = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() # self._xo_cache[color] = surface return surface # self._xo_cache[color] def _header(self): return '<svg\n' + 'xmlns:svg="http:#www.w3.org/2000/svg"\n' + \ 'xmlns="http://www.w3.org/2000/svg"\n' + \ 'xmlns:xlink="http://www.w3.org/1999/xlink"\n' + \ 'version="1.1"\n' + 'width="' + str(self._svg_width) + '"\n' + \ 'height="' + str(self._svg_height) + '">\n' def _footer(self): return '</svg>\n'
class Ball(): ''' The Bounce class is used to define the ball and the user interaction. ''' def __init__(self, sprites, filename): self._current_frame = 0 self._frames = [] # Easter Egg animation self._sprites = sprites self.ball = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf(svg_from_file(filename))) self.ball.set_layer(3) self.ball.set_label_attributes(24, vert_align='top') ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames.append( Sprite( self._sprites, 0, 0, svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer()))) for frame in self._frames: frame.set_layer(3) frame.move((0, -SIZE[1])) # move animation frames off screen def new_ball(self, filename): ''' Create a ball object and Easter Egg animation from an SVG file. ''' self.ball.set_shape(svg_str_to_pixbuf(svg_from_file(filename))) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames[i].set_shape( svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer())) def new_ball_from_image(self, filename, save_path): ''' Just create a ball object from an image file ''' if filename == '': _logger.debug('Image file not found.') return try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) if pixbuf.get_width() > pixbuf.get_height(): size = pixbuf.get_height() x = int((pixbuf.get_width() - size) / 2) else: size = pixbuf.get_width() x = int((pixbuf.get_height() - size) / 2) crop = GdkPixbuf.Pixbuf.new(0, True, 8, size, size) pixbuf.copy_area(x, 0, size, size, crop, 0, 0) scale = crop.scale_simple(85, 85, GdkPixbuf.InterpType.BILINEAR) scale.savev(save_path, 'png', [], []) self.ball.set_shape(svg_str_to_pixbuf( generate_ball_svg(save_path))) except Exception as e: _logger.error('Could not load image from %s: %s' % (filename, e)) def new_ball_from_fraction(self, fraction): ''' Create a ball with a section of size fraction. ''' r = SIZE[0] / 2.0 self.ball.set_shape( svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + svg_sector(r, r + BOX[1], r - 1, 1.999 * pi, COLORS[0], COLORS[1]) + svg_sector(r, r + BOX[1], r - 1, fraction * 2 * pi, COLORS[1], COLORS[0]) + svg_rect(BOX[0], BOX[1], 4, 4, 0, 0, '#FFFFFF', 'none') + svg_footer())) def ball_x(self): return self.ball.get_xy()[0] def ball_y(self): return self.ball.get_xy()[1] def frame_x(self, i): return self._frames[i].get_xy()[0] def frame_y(self, i): return self._frames[i].get_xy()[1] def width(self): return self.ball.rect[2] def height(self): return self.ball.rect[3] def move_ball(self, pos): self.ball.move(pos) def move_ball_relative(self, pos): self.ball.move_relative(pos) def move_frame(self, i, pos): self._frames[i].move(pos) def move_frame_relative(self, i, pos): self._frames[i].move_relative(pos) def hide_frames(self): for frame in self._frames: frame.move((0, -SIZE[1])) # hide the animation frames def next_frame(self, frame_counter): if frame_counter in ANIMATION: self._switch_frames(ANIMATION[frame_counter]) return self._current_frame def _switch_frames(self, frames): ''' Switch between frames in the animation ''' self.move_frame(frames[1], (self.frame_x(frames[0]), self.frame_y(frames[0]))) self.move_frame(frames[0], ((0, -SIZE[1]))) # hide the frame self._current_frame = frames[1]
class Game(): def __init__(self, canvas, parent=None, path=None, root=None, mode='array', colors=['#A0FFA0', '#FF8080']): self._canvas = canvas self._parent = parent self._path = path self._root = root self._mode = mode self.current_image = 0 self.playing = False self._timeout_id = None self._prev_mouse_pos = (0, 0) self._start_time = 0 self._colors = ['#FFFFFF'] self._colors.append(colors[0]) self._colors.append(colors[1]) self._canvas.add_events( Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.BUTTON_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.TOUCH_MASK) self._canvas.connect('draw', self.__draw_cb) self._canvas.connect('event', self.__event_cb) self.configure(move=False) self.we_are_sharing = False self._start_time = 0 self._timeout_id = None # Find the image files self._PATHS = glob.glob(os.path.join(self._path, 'images', '*.svg')) # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) a = max(Gdk.Screen.width(), Gdk.Screen.height()) b = min(Gdk.Screen.width(), Gdk.Screen.height()) self._bg_pixbufs = [] if self._parent.tablet_mode: # text on top # landscape self._bg_pixbufs.append(svg_str_to_pixbuf(genhole( a, a, 3 * style.GRID_CELL_SIZE, style.DEFAULT_SPACING, a - 3 * style.GRID_CELL_SIZE, style.GRID_CELL_SIZE * 3 + style.DEFAULT_SPACING))) # portrait self._bg_pixbufs.append(svg_str_to_pixbuf(genhole( a, a, 3 * style.GRID_CELL_SIZE, style.DEFAULT_SPACING, b - 3 * style.GRID_CELL_SIZE, style.GRID_CELL_SIZE * 3 + style.DEFAULT_SPACING))) else: # text on bottom # landscape self._bg_pixbufs.append(svg_str_to_pixbuf(genhole( a, a, 3 * style.GRID_CELL_SIZE, b - style.GRID_CELL_SIZE * 4 - style.DEFAULT_SPACING, a - 3 * style.GRID_CELL_SIZE, b - style.GRID_CELL_SIZE - style.DEFAULT_SPACING))) # portrait self._bg_pixbufs.append(svg_str_to_pixbuf(genhole( a, a, 3 * style.GRID_CELL_SIZE, a - style.GRID_CELL_SIZE * 4 - style.DEFAULT_SPACING, b - 3 * style.GRID_CELL_SIZE, a - style.GRID_CELL_SIZE - style.DEFAULT_SPACING))) if Gdk.Screen.width() > Gdk.Screen.height(): self._bg = Sprite(self._sprites, 0, 0, self._bg_pixbufs[0]) else: self._bg = Sprite(self._sprites, 0, 0, self._bg_pixbufs[1]) self._bg.set_layer(-2) self._bg.type = 'background' size = 3 * self._dot_size + 4 * self._space x = int((Gdk.Screen.width() - size) / 2.) self._dots = [] self._Dots = [] # larger dots for linear mode X = int((Gdk.Screen.width() - self._dot_size * 3) / 2.) Y = style.GRID_CELL_SIZE + self._yoff if self._parent.tablet_mode: yoffset = self._space * 2 + self._yoff else: yoffset = self._yoff for y in range(3): for x in range(3): xoffset = int((self._width - 3 * self._dot_size - 2 * self._space) / 2.) self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space) + yoffset, self._new_dot_surface(color=self._colors[0]))) self._dots[-1].type = -1 # No image self._dots[-1].set_label_attributes(72) self._dots[-1].set_label('?') self._Dots.append( Sprite( self._sprites, X, Y, self._new_dot_surface(color=self._colors[0], large=True))) self._Dots[-1].type = -1 # No image self._Dots[-1].set_label_attributes(72 * 3) self._Dots[-1].set_label('?') self.number_of_images = len(self._PATHS) if USE_ART4APPS: self._art4apps = Art4Apps() self.number_of_images = len(self._art4apps.get_words()) self._record_pixbufs = [] for icon in ['media-audio', 'media-audio-recording']: self._record_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) self._play_pixbufs = [] for icon in ['play-inactive', 'play']: self._play_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) self._speak_pixbufs = [] for icon in ['speak-inactive', 'speak']: self._speak_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) left = style.GRID_CELL_SIZE right = Gdk.Screen.width() - 2 * style.GRID_CELL_SIZE y0 = style.DEFAULT_SPACING + style.DEFAULT_PADDING y1 = y0 + style.GRID_CELL_SIZE y2 = y1 + style.GRID_CELL_SIZE if not self._parent.tablet_mode: dy = Gdk.Screen.height() - 4 * style.GRID_CELL_SIZE - \ 2 * style.DEFAULT_SPACING y0 += dy y1 += dy y2 += dy y3 = int((Gdk.Screen.height() - 2 * style.GRID_CELL_SIZE) / 2) self._record = Sprite( self._sprites, right, y0, self._record_pixbufs[RECORD_OFF]) self._record.set_layer(1) self._record.type = 'record' self._play = Sprite( self._sprites, right, y1, self._play_pixbufs[PLAY_OFF]) self._play.set_layer(1) self._play.type = 'play-inactive' self._speak = Sprite( self._sprites, right, y2, self._speak_pixbufs[SPEAK_OFF]) self._speak.set_layer(1) self._speak.type = 'speak-inactive' self._next_prev_pixbufs = [] for icon in ['go-previous', 'go-next', 'go-previous-inactive', 'go-next-inactive']: self._next_prev_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) self._prev = Sprite( self._sprites, left, y3, self._next_prev_pixbufs[PREV_INACTIVE]) self._prev.set_layer(1) self._prev.type = 'prev' if self._mode == 'array': self._prev.hide() self._next = Sprite( self._sprites, right, y3, self._next_prev_pixbufs[NEXT]) self._next.set_layer(1) self._next.type = 'next' if self._mode == 'array': self._next.hide() def configure(self, move=True): self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE if not move: if self._height < self._width: self._scale = self._height / (3 * DOT_SIZE * 1.2) else: self._scale = self._width / (3 * DOT_SIZE * 1.2) self._scale /= 1.5 self._dot_size = int(DOT_SIZE * self._scale) if self._parent.tablet_mode: # text on top self._yoff = style.GRID_CELL_SIZE * 3 + style.DEFAULT_SPACING else: self._yoff = style.DEFAULT_SPACING self._space = int(self._dot_size / 5.) return left = style.GRID_CELL_SIZE right = Gdk.Screen.width() - 2 * style.GRID_CELL_SIZE y0 = style.DEFAULT_SPACING + style.DEFAULT_PADDING y1 = y0 + style.GRID_CELL_SIZE y2 = y1 + style.GRID_CELL_SIZE if not self._parent.tablet_mode: dy = Gdk.Screen.height() - 4 * style.GRID_CELL_SIZE - \ 2 * style.DEFAULT_SPACING y0 += dy y1 += dy y2 += dy y3 = int((Gdk.Screen.height() - 2 * style.GRID_CELL_SIZE) / 2) self._record.move((right, y0)) self._play.move((right, y1)) self._speak.move((right, y2)) self._prev.move((left, y3)) self._next.move((right, y3)) # Move the dots X = int((Gdk.Screen.width() - self._dot_size * 3) / 2.) Y = style.GRID_CELL_SIZE + self._yoff if self._parent.tablet_mode: yoffset = self._space * 2 + self._yoff else: yoffset = self._yoff for y in range(3): for x in range(3): xoffset = int((self._width - 3 * self._dot_size - 2 * self._space) / 2.) self._dots[x + y * 3].move( (xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space) + yoffset)) self._Dots[x + y * 3].move((X, Y)) # switch orientation the bg sprite if Gdk.Screen.width() > Gdk.Screen.height(): self._bg.set_image(self._bg_pixbufs[0]) else: self._bg.set_image(self._bg_pixbufs[1]) self._bg.set_layer(-2) def set_speak_icon_state(self, state): if state: self._speak.set_image(self._speak_pixbufs[SPEAK_ON]) self._speak.type = 'speak' else: self._speak.set_image(self._speak_pixbufs[SPEAK_OFF]) self._speak.type = 'speak-inactive' self._speak.set_layer(1) def set_record_icon_state(self, state): if state: self._record.set_image(self._record_pixbufs[RECORD_ON]) else: self._record.set_image(self._record_pixbufs[RECORD_OFF]) self._record.set_layer(1) def set_play_icon_state(self, state): if state: self._play.set_image(self._play_pixbufs[PLAY_ON]) self._play.type = 'play' else: self._play.set_image(self._play_pixbufs[PLAY_OFF]) self._play.type = 'play-inactive' self._play.set_layer(1) def autoplay(self): self.set_mode('linear') # forces current image to 0 self.playing = True self._autonext(next=False) def stop(self): self.playing = False if self._parent.audio_process is not None: self._parent.audio_process.terminate() self._parent.audio_process = None if self._timeout_id is not None: GObject.source_remove(self._timeout_id) self._timeout_id = None self._parent.autoplay_button.set_icon_name('media-playback-start') self._parent.autoplay_button.set_tooltip(_('Play')) self._parent.array_button.set_sensitive(True) def _autonext(self, next=True): self._timeout_id = None if not self.playing: return if next: self._Dots[self.current_image].hide() self.current_image += 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 8: self._next.set_image( self._next_prev_pixbufs[NEXT_INACTIVE]) self._next.set_layer(1) self._prev.set_image(self._next_prev_pixbufs[PREV]) self._prev.set_layer(1) self._parent.check_audio_status() self._parent.check_text_status() GObject.idle_add(self._play_sound) def _poll_audio(self): if self._parent.audio_process is None: # Already stopped? return if self._parent.audio_process.poll() is None: GObject.timeout_add(200, self._poll_audio) else: self._parent.audio_process = None self._next_image() def _play_sound(self): self._start_time = time.time() # Either play back a recording or speak the text if self._play.type == 'play': self._parent.playback_recording_cb() self._poll_audio() elif self._speak.type == 'speak': bounds = self._parent.text_buffer.get_bounds() text = self._parent.text_buffer.get_text( bounds[0], bounds[1], True) speak(text) self._next_image() def _next_image(self): accumulated_time = int(time.time() - self._start_time) if accumulated_time < 5: pause = 5 - accumulated_time else: pause = 1 if self.playing and self.current_image < 8: self._timeout_id = GObject.timeout_add(pause * 1000, self._autonext) else: self.stop() def __event_cb(self, win, event): ''' The mouse button was pressed. Is it on a sprite? or there was a gesture. ''' left = right = False if event.type in (Gdk.EventType.TOUCH_BEGIN, Gdk.EventType.TOUCH_CANCEL, Gdk.EventType.TOUCH_END, Gdk.EventType.BUTTON_PRESS, Gdk.EventType.BUTTON_RELEASE): x = int(event.get_coords()[1]) y = int(event.get_coords()[2]) if event.type in (Gdk.EventType.TOUCH_BEGIN, Gdk.EventType.BUTTON_PRESS): self._prev_mouse_pos = (x, y) elif event.type in (Gdk.EventType.TOUCH_END, Gdk.EventType.BUTTON_RELEASE): if self._parent.audio_process is not None: self._parent.audio_process.terminate() self._parent.audio_process = None terminated_audio = True else: terminated_audio = False if self.playing: self.stop() new_mouse_pos = (x, y) mouse_movement = (new_mouse_pos[0] - self._prev_mouse_pos[0], new_mouse_pos[1] - self._prev_mouse_pos[1]) # horizontal gestures only if (abs(mouse_movement[0]) / 5) > abs(mouse_movement[1]): if abs(mouse_movement[0]) > abs(mouse_movement[1]): if mouse_movement[0] < 0: right = True else: left = True if event.type in (Gdk.EventType.TOUCH_END, Gdk.EventType.BUTTON_RELEASE): spr = self._sprites.find_sprite((x, y)) if left or right or spr is not None: if spr.type in ['record', 'play', 'play-inactive', 'speak', 'speak-inactive']: if spr.type == 'record': self._parent.record_cb() elif spr.type == 'play' and not terminated_audio: self._parent.playback_recording_cb() elif spr.type == 'speak': bounds = self._parent.text_buffer.get_bounds() text = self._parent.text_buffer.get_text( bounds[0], bounds[1], True) speak(text) return elif self._mode == 'array': return self._parent.speak_text_cb() if self._parent.recording: self._parent.record_cb() if (left or spr.type == 'prev') and self.current_image > 0: self._Dots[self.current_image].hide() self.current_image -= 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 0: self._prev.set_image( self._next_prev_pixbufs[PREV_INACTIVE]) self._next.set_image(self._next_prev_pixbufs[NEXT]) elif (right or spr.type == 'next') and self.current_image < 8: self._Dots[self.current_image].hide() self.current_image += 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 8: self._next.set_image( self._next_prev_pixbufs[NEXT_INACTIVE]) self._prev.set_image(self._next_prev_pixbufs[PREV]) elif spr.type not in ['prev', 'background'] and \ self.current_image < 8: self._Dots[self.current_image].hide() self.current_image += 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 8: self._next.set_image( self._next_prev_pixbufs[NEXT_INACTIVE]) self._prev.set_image(self._next_prev_pixbufs[PREV]) self._parent.check_audio_status() self._parent.check_text_status() self._prev.set_layer(1) self._next.set_layer(1) return False def get_mode(self): return self._mode def set_mode(self, mode): self.current_image = 0 self._prev.set_image(self._next_prev_pixbufs[PREV_INACTIVE]) self._next.set_image(self._next_prev_pixbufs[NEXT]) if mode == 'array': self._mode = 'array' self._prev.hide() self._next.hide() else: self._mode = 'linear' self._prev.set_layer(1) self._next.set_layer(1) for i in range(9): if self._mode == 'array': self._dots[i].set_layer(100) self._Dots[i].hide() else: self._dots[i].hide() if self.current_image == i: self._Dots[i].set_layer(100) else: self._Dots[i].hide() def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' if self._timeout_id is not None: GObject.source_remove(self._timeout_id) self.set_mode(self._mode) if self._mode == 'array': for dot in self._dots: if dot.type != -1: dot.type = -1 dot.set_shape(self._new_dot_surface( self._colors[abs(dot.type)])) dot.set_label('?') else: for dot in self._Dots: if dot.type != -1: dot.type = -1 dot.set_shape(self._new_dot_surface( self._colors[abs(dot.type)], large=True)) dot.set_label('?') self._dance_counter = 0 self._dance_step() def _dance_step(self): ''' Short animation before loading new game ''' if self._mode == 'array': for dot in self._dots: dot.set_shape(self._new_dot_surface( self._colors[int(uniform(0, 3))])) else: self._Dots[0].set_shape(self._new_dot_surface( self._colors[int(uniform(0, 3))], large=True)) self._dance_counter += 1 if self._dance_counter < 10: self._timeout_id = GObject.timeout_add(500, self._dance_step) else: self._new_images() def new_game(self): ''' Start a new game. ''' self._all_clear() def _new_images(self): ''' Select pictures at random ''' used_images = [0] * self.number_of_images for i in range(9): random_selection = int(uniform(0, self.number_of_images)) while used_images[random_selection] != 0: random_selection = int(uniform(0, self.number_of_images)) used_images[random_selection] = 1 self._dots[i].set_label('') self._dots[i].type = random_selection self._dots[i].set_shape(self._new_dot_surface( image=self._dots[i].type)) self._Dots[i].set_label('') self._Dots[i].type = self._dots[i].type self._Dots[i].set_shape(self._new_dot_surface( image=self._Dots[i].type, large=True)) if self._mode == 'array': self._dots[i].set_layer(100) self._Dots[i].hide() else: if self.current_image == i: self._Dots[i].set_layer(100) else: self._Dots[i].hide() self._dots[i].hide() if self.we_are_sharing: self._parent.send_new_images() def restore_game(self, dot_list): ''' Restore a game from the Journal or share ''' self.set_mode(self._mode) for i, dot in enumerate(dot_list): self._dots[i].type = dot self._dots[i].set_shape(self._new_dot_surface( image=self._dots[i].type)) self._dots[i].set_label('') self._Dots[i].type = dot self._Dots[i].set_shape(self._new_dot_surface( image=self._Dots[i].type, large=True)) self._Dots[i].set_label('') if self._mode == 'array': self._dots[i].set_layer(100) self._Dots[i].hide() else: if self.current_image == i: self._Dots[i].set_layer(100) else: self._Dots[i].hide() self._dots[i].hide() def save_game(self): ''' Return dot list for saving to Journal or sharing ''' dot_list = [] for dot in self._dots: dot_list.append(dot.type) return dot_list def set_sharing(self, share=True): self.we_are_sharing = share def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * 3 def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % 3, int(dot / 3)] def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def __expose_cb(self, win, event): ''' Callback to handle window expose events ''' self.do_expose_event(event) return True # Handle the expose-event by drawing def do_expose_event(self, event): # Create the cairo context cr = self._canvas.window.cairo_create() # Restrict Cairo to the exposed area; avoid extra work cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list if cr is not None: self._sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def export(self): ''' Write dot to cairo surface. ''' if self._mode == 'array': w = h = int(4 * self._space + 3 * self._dot_size) png_surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h) cr = cairo.Context(png_surface) cr.set_source_rgb(192, 192, 192) cr.rectangle(0, 0, w, h) cr.fill() for i in range(9): y = self._space + int(i / 3.) * (self._dot_size + self._space) x = self._space + (i % 3) * (self._dot_size + self._space) cr.save() cr.set_source_surface(self._dots[i].images[0], x, y) cr.rectangle(x, y, self._dot_size, self._dot_size) cr.fill() cr.restore() else: w = h = int(2 * self._space + 3 * self._dot_size) png_surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h) cr = cairo.Context(png_surface) cr.set_source_rgb(192, 192, 192) cr.rectangle(0, 0, w, h) cr.fill() y = self._space x = self._space cr.save() cr.set_source_surface(self._Dots[self.current_image].images[0], x, y) cr.rectangle(x, y, 3 * self._dot_size, 3 * self._dot_size) cr.fill() cr.restore() return png_surface def _new_dot_surface(self, color='#000000', image=None, large=False): ''' generate a dot of a color color ''' if large: size = self._dot_size * 3 else: size = self._dot_size self._svg_width = size self._svg_height = size if image is None: # color dot self._stroke = color self._fill = color pixbuf = svg_str_to_pixbuf( self._header() + self._circle(size / 2., size / 2., size / 2.) + self._footer()) else: if USE_ART4APPS: word = self._art4apps.get_words()[image] try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._art4apps.get_image_filename(word), size, size) except Exception, e: _logger.error('new dot surface %s %s: %s' % (image, word, e)) word = 'zebra' # default in case image is not found pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._art4apps.get_image_filename(word), size, size) else:
class Game(): ''' OLPC XO man color changer designed in memory of Nat Jacobson ''' def __init__(self, canvas, parent=None, mycolors=['#A0FFA0', '#FF8080']): self._activity = parent self.colors = [mycolors[0]] self.colors.append(mycolors[1]) self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent self._canvas.connect("draw", self.__draw_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - GRID_CELL_SIZE self._scale = self._width / 1200. self.press = None self.dragpos = [0, 0] self.startpos = [0, 0] self._dot_cache = {} self._xo_cache = {} self._radius = 22.5 self._stroke_width = 9.5 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._sprites.set_delay(True) self._dots = [] self._xo_man = None self._generate_bg('#FFF') # First dot, starting angle self._cxy = [self._width / 2, self._height / 2] self._xy = [self._width / 2 + 120 * self._scale, self._height / 2 - self._radius * self._scale] self._angle = 0 self._dot_size_plus = self._radius * 3 * self._scale self._min = -self._dot_size_plus / 3 self._max = self._height - (self._dot_size_plus / 2.2) self._zones = [] self._calc_zones() self._generate_spiral() self._sprites.draw_all() def _calc_zones(self): for color in colors: rgb1 = _from_hex(color[0]) rgb2 = _from_hex(color[1]) dv = _contrast(rgb1, rgb2) dh = _delta_hue(rgb1, rgb2) self._zones.append(_zone(dv, dh)) def _calc_next_dot_position(self): ''' calculate spiral coordinates ''' dx = self._xy[0] - self._cxy[0] dy = self._xy[1] - self._cxy[1] r = sqrt(dx * dx + dy * dy) c = 2 * r * pi a = atan2(dy, dx) da = (self._dot_size_plus / c) * 2 * pi a += da r += self._dot_size_plus / (c / self._dot_size_plus) self._xy[0] = r * cos(a) + self._cxy[0] self._xy[1] = r * sin(a) + self._cxy[1] if self._xy[1] < self._min or self._xy[1] > self._max: self._calc_next_dot_position() def _generate_spiral(self): ''' Make a new set of dots for a sprial ''' for z in range(4): for i in range(len(colors)): if self._zones[i] == z: self._dots.append( Sprite(self._sprites, self._xy[0], self._xy[1], self._new_dot(colors[i]))) self._dots[-1].type = i self._calc_next_dot_position() if self._xo_man is None: x = 510 * self._scale y = 280 * self._scale self._xo_man = Sprite(self._sprites, x, y, self._new_xo_man(self.colors)) self._xo_man.type = None def move_dot(self, i, x, y): self._dots[i].move((x, y)) def get_dot_xy(self, i): return self._dots[i].get_xy() def move_xo_man(self, x, y): self._xo_man.move((x, y)) def get_xo_man_xy(self): return self._xo_man.get_xy() def rotate(self): x, y = self._dots[0].get_xy() for i in range(len(colors) - 1): self._dots[i].move(self._dots[i + 1].get_xy()) self._dots[-1].move((x, y)) def _generate_bg(self, color): ''' a background color ''' self._bg = Sprite(self._sprites, 0, 0, self._new_background(color)) self._bg.set_layer(0) self._bg.type = None def adj_background(self, color): ''' Change background ''' self._bg.set_image(self._new_background(color)) self._bg.set_layer(0) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) self.dragpos = [x, y] spr = self._sprites.find_sprite((x, y)) if spr == None or spr == self._bg: return self.startpos = spr.get_xy() self.press = spr def _mouse_move_cb(self, win, event): """ Drag a rule with the mouse. """ if self.press is None: self.dragpos = [0, 0] return True win.grab_focus() x, y = map(int, event.get_coords()) dx = x - self.dragpos[0] dy = y - self.dragpos[1] self.press.move_relative((dx, dy)) self.dragpos = [x, y] def _button_release_cb(self, win, event): if self.press == None: return True if _distance(self.press.get_xy(), self.startpos) < 20: if type(self.press.type) == int: self.i = self.press.type self._new_surface() self.press.move(self.startpos) self.press = None def _new_surface(self): self.colors[0] = colors[self.i][0] self.colors[1] = colors[self.i][1] self._xo_man.set_image(self._new_xo_man(colors[self.i])) self._xo_man.set_layer(100) def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def do_expose_event(self, event): ''' Handle the expose-event by drawing ''' # Restrict Cairo to the exposed area cr = self._canvas.window.cairo_create() cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self._sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def _new_dot(self, color): ''' generate a dot of a color color ''' if True: # not color in self._dot_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(60 * self._scale) self._svg_height = int(60 * self._scale) pixbuf = svg_str_to_pixbuf( self._header() + \ '<circle cx="%f" cy="%f" r="%f" stroke="%s" fill="%s" \ stroke-width="%f" visibility="visible" />' % ( 30 * self._scale, 30 * self._scale, self._radius * self._scale, self._stroke, self._fill, self._stroke_width * self._scale) + \ self._footer()) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._svg_width, self._svg_height) context = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() # self._dot_cache[color] = surface return surface # self._dot_cache[color] def _new_background(self, color): ''' Background color ''' self._svg_width = int(self._width) self._svg_height = int(self._height) string = \ self._header() + \ '<rect width="%f" height="%f" x="%f" \ y="%f" fill="%s" stroke="none" visibility="visible" />' % ( self._width, self._height, 0, 0, color) + \ self._footer() pixbuf = svg_str_to_pixbuf(string) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._svg_width, self._svg_height) context = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() return surface def _new_xo_man(self, color): ''' generate a xo-man of a color color ''' if True: # not color in self._xo_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(240. * self._scale) self._svg_height = int(260. * self._scale) string = \ self._header() + \ '<g>' + \ '<g id="XO">' + \ '<path id="Line1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Line2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Fill1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._fill, 17 * self._scale) + \ '<path id="Fill2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._fill, 17 * self._scale) + \ '<circle id="Circle" cx="%f" cy="%f" r="%f" \ fill="%s" stroke="%s" stroke-width="%f" visibility="visible" />' % ( 120 * self._scale, 61.5 * self._scale, 27.5 * self._scale, self._fill, self._stroke, 11 * self._scale) + \ '</g></g>' + \ self._footer() pixbuf = svg_str_to_pixbuf(string) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._svg_width, self._svg_height) context = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() # self._xo_cache[color] = surface return surface # self._xo_cache[color] def _header(self): return '<svg\n' + 'xmlns:svg="http:#www.w3.org/2000/svg"\n' + \ 'xmlns="http://www.w3.org/2000/svg"\n' + \ 'xmlns:xlink="http://www.w3.org/1999/xlink"\n' + \ 'version="1.1"\n' + 'width="' + str(self._svg_width) + '"\n' + \ 'height="' + str(self._svg_height) + '">\n' def _footer(self): return '</svg>\n'
class Turtle: def __init__(self, turtles, key, turtle_colors=None): """ The turtle is not a block, just a sprite with an orientation """ self.x = 0.0 self.y = 0.0 self.hidden = False self.shapes = [] self.custom_shapes = False self.type = 'turtle' self.name = key self.heading = 0.0 self.pen_shade = 50 self.pen_color = 0 self.pen_gray = 100 self.pen_size = 5 self.pen_state = True self.label_block = None self._prep_shapes(key, turtles, turtle_colors) # Choose a random angle from which to attach the turtle label. if turtles.sprite_list is not None: self.spr = Sprite(turtles.sprite_list, 0, 0, self.shapes[0]) angle = uniform(0, pi * 4 / 3.0) # 240 degrees w = self.shapes[0].get_width() r = w * 0.67 # Restrict angle the the sides 30-150; 210-330 if angle > pi * 2 / 3.0: angle += pi / 2.0 # + 90 self.label_xy = [int(r * sin(angle)), int(r * cos(angle) + w / 2.0)] else: angle += pi / 6.0 # + 30 self.label_xy = [int(r * sin(angle) + w / 2.0), int(r * cos(angle) + w / 2.0)] else: self.spr = None turtles.add_to_dict(key, self) def _prep_shapes(self, name, turtles=None, turtle_colors=None): # If the turtle name is an int, we'll use a palette color as the # turtle color try: int_key = int(name) use_color_table = True except ValueError: use_color_table = False if turtle_colors is not None: self.colors = turtle_colors[:] self.shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = ['#%06x' % (COLOR_TABLE[fill]), '#%06x' % (COLOR_TABLE[stroke])] self.shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: self.colors = DEFAULT_TURTLE_COLORS self.shapes = turtles.get_pixbufs() def set_turtle_colors(self, turtle_colors): ''' reset the colors of a preloaded turtle ''' if turtle_colors is not None: self.colors = turtle_colors[:] self.shapes = generate_turtle_pixbufs(self.colors) self.set_heading(self.heading) def set_shapes(self, shapes, i=0): """ Reskin the turtle """ n = len(shapes) if n == 1 and i > 0: # set shape[i] if i < len(self.shapes): self.shapes[i] = shapes[0] elif n == SHAPES: # all shapes have been precomputed self.shapes = shapes[:] else: # rotate shapes if n != 1: debug_output("%d images passed to set_shapes: ignoring" % (n), self.tw.running_sugar) if self.heading == 0.0: # rotate the shapes images = [] w, h = shapes[0].get_width(), shapes[0].get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(SHAPES): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context = gtk.gdk.CairoContext(context) context.translate(nw / 2., nh / 2.) context.rotate(i * 10 * pi / 180.) context.translate(-nw / 2., -nh / 2.) context.set_source_pixbuf(shapes[0], (nw - w) / 2., (nh - h) / 2.) context.rectangle(0, 0, nw, nh) context.fill() images.append(surface) self.shapes = images[:] else: # associate shape with image at current heading j = int(self.heading + 5) % 360 / (360 / SHAPES) self.shapes[j] = shapes[0] self.custom_shapes = True self.show() def reset_shapes(self): """ Reset the shapes to the standard turtle """ if self.custom_shapes: self.shapes = generate_turtle_pixbufs(self.colors) self.custom_shapes = False def set_heading(self, heading): """ Set the turtle heading (one shape per 360/SHAPES degrees) """ self.heading = heading i = (int(self.heading + 5) % 360) / (360 / SHAPES) if not self.hidden and self.spr is not None: try: self.spr.set_shape(self.shapes[i]) except IndexError: self.spr.set_shape(self.shapes[0]) def set_color(self, color): """ Set the pen color for this turtle. """ self.pen_color = color def set_gray(self, gray): """ Set the pen gray level for this turtle. """ self.pen_gray = gray def set_shade(self, shade): """ Set the pen shade for this turtle. """ self.pen_shade = shade def set_pen_size(self, pen_size): """ Set the pen size for this turtle. """ self.pen_size = pen_size def set_pen_state(self, pen_state): """ Set the pen state (down==True) for this turtle. """ self.pen_state = pen_state def hide(self): """ Hide the turtle. """ if self.spr is not None: self.spr.hide() if self.label_block is not None: self.label_block.spr.hide() self.hidden = True def show(self): """ Show the turtle. """ if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) self.hidden = False self.move((self.x, self.y)) self.set_heading(self.heading) if self.label_block is not None: self.label_block.spr.set_layer(TURTLE_LAYER + 1) def move(self, pos): """ Move the turtle. """ self.x, self.y = pos[0], pos[1] if not self.hidden and self.spr is not None: self.spr.move((int(pos[0]), int(pos[1]))) if self.label_block is not None: self.label_block.spr.move((int(pos[0] + self.label_xy[0]), int(pos[1] + self.label_xy[1]))) return(self.x, self.y) def get_name(self): ''' return turtle name (key) ''' return self.name def get_xy(self): """ Return the turtle's x, y coordinates. """ return(self.x, self.y) def get_heading(self): """ Return the turtle's heading. """ return(self.heading) def get_color(self): """ Return the turtle's color. """ return(self.pen_color) def get_gray(self): """ Return the turtle's gray level. """ return(self.pen_gray) def get_shade(self): """ Return the turtle's shade. """ return(self.pen_shade) def get_pen_size(self): """ Return the turtle's pen size. """ return(self.pen_size) def get_pen_state(self): """ Return the turtle's pen state. """ return(self.pen_state)
class Turtle: def __init__(self, turtles, turtle_name, turtle_colors=None): ''' The turtle is not a block, just a sprite with an orientation ''' self.spr = None self.label_block = None self._turtles = turtles self._shapes = [] self._custom_shapes = False self._name = turtle_name self._hidden = False self._remote = False self._x = 0.0 self._y = 0.0 self._heading = 0.0 self._half_width = 0 self._half_height = 0 self._drag_radius = None self._pen_shade = 50 self._pen_color = 0 self._pen_gray = 100 if self._turtles.turtle_window.coord_scale == 1: self._pen_size = 5 else: self._pen_size = 1 self._pen_state = True self._pen_fill = False self._poly_points = [] self._prep_shapes(turtle_name, self._turtles, turtle_colors) # Create a sprite for the turtle in interactive mode. if turtles.sprite_list is not None: self.spr = Sprite(self._turtles.sprite_list, 0, 0, self._shapes[0]) self._calculate_sizes() # Choose a random angle from which to attach the turtle # label to be used when sharing. angle = uniform(0, pi * 4 / 3.0) # 240 degrees width = self._shapes[0].get_width() radius = width * 0.67 # Restrict the angle to the sides: 30-150; 210-330 if angle > pi * 2 / 3.0: angle += pi / 2.0 # + 90 self.label_xy = [int(radius * sin(angle)), int(radius * cos(angle) + width / 2.0)] else: angle += pi / 6.0 # + 30 self.label_xy = [int(radius * sin(angle) + width / 2.0), int(radius * cos(angle) + width / 2.0)] self._turtles.add_to_dict(turtle_name, self) def _calculate_sizes(self): self._half_width = int(self.spr.rect.width / 2.0) self._half_height = int(self.spr.rect.height / 2.0) self._drag_radius = ((self._half_width * self._half_width) + (self._half_height * self._half_height)) / 6 def set_remote(self): self._remote = True def get_remote(self): return self._remote def _prep_shapes(self, name, turtles=None, turtle_colors=None): # If the turtle name is an int, we'll use a palette color as the # turtle color try: int_key = int(name) use_color_table = True except ValueError: use_color_table = False if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = ['#%06x' % (COLOR_TABLE[fill]), '#%06x' % (COLOR_TABLE[stroke])] self._shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: self.colors = DEFAULT_TURTLE_COLORS self._shapes = turtles.get_pixbufs() def set_turtle_colors(self, turtle_colors): ''' reset the colors of a preloaded turtle ''' if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) self.set_heading(self._heading, share=False) def set_shapes(self, shapes, i=0): ''' Reskin the turtle ''' n = len(shapes) if n == 1 and i > 0: # set shape[i] if i < len(self._shapes): self._shapes[i] = shapes[0] elif n == SHAPES: # all shapes have been precomputed self._shapes = shapes[:] else: # rotate shapes if n != 1: debug_output("%d images passed to set_shapes: ignoring" % (n), self._turtles.turtle_window.running_sugar) if self._heading == 0.0: # rotate the shapes images = [] w, h = shapes[0].get_width(), shapes[0].get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(SHAPES): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context = gtk.gdk.CairoContext(context) context.translate(nw / 2.0, nh / 2.0) context.rotate(i * 10 * pi / 180.) context.translate(-nw / 2.0, -nh / 2.0) context.set_source_pixbuf(shapes[0], (nw - w) / 2.0, (nh - h) / 2.0) context.rectangle(0, 0, nw, nh) context.fill() images.append(surface) self._shapes = images[:] else: # associate shape with image at current heading j = int(self._heading + 5) % 360 / (360 / SHAPES) self._shapes[j] = shapes[0] self._custom_shapes = True self.show() self._calculate_sizes() def reset_shapes(self): ''' Reset the shapes to the standard turtle ''' if self._custom_shapes: self._shapes = generate_turtle_pixbufs(self.colors) self._custom_shapes = False self._calculate_sizes() def set_heading(self, heading, share=True): ''' Set the turtle heading (one shape per 360/SHAPES degrees) ''' try: self._heading = heading except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._heading %= 360 self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def _update_sprite_heading(self): ''' Update the sprite to reflect the current heading ''' i = (int(self._heading + 5) % 360) / (360 / SHAPES) if not self._hidden and self.spr is not None: try: self.spr.set_shape(self._shapes[i]) except IndexError: self.spr.set_shape(self._shapes[0]) def set_color(self, color=None, share=True): ''' Set the pen color for this turtle. ''' # Special case for color blocks if color is not None and color in COLORDICT: self.set_shade(COLORDICT[color][1], share) self.set_gray(COLORDICT[color][2], share) if COLORDICT[color][0] is not None: self.set_color(COLORDICT[color][0], share) color = COLORDICT[color][0] else: color = self._pen_color elif color is None: color = self._pen_color try: self._pen_color = color except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'c|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_color)])) self._turtles.turtle_window.send_event(event) def set_gray(self, gray=None, share=True): ''' Set the pen gray level for this turtle. ''' if gray is not None: try: self._pen_gray = gray except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return if self._pen_gray < 0: self._pen_gray = 0 if self._pen_gray > 100: self._pen_gray = 100 self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'g|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_gray)])) self._turtles.turtle_window.send_event(event) def set_shade(self, shade=None, share=True): ''' Set the pen shade for this turtle. ''' if shade is not None: try: self._pen_shade = shade except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 's|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_shade)])) self._turtles.turtle_window.send_event(event) def set_pen_size(self, pen_size=None, share=True): ''' Set the pen size for this turtle. ''' if pen_size is not None: try: self._pen_size = max(0, pen_size) except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._turtles.turtle_window.canvas.set_pen_size( self._pen_size * self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'w|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_size)])) self._turtles.turtle_window.send_event(event) def set_pen_state(self, pen_state=None, share=True): ''' Set the pen state (down==True) for this turtle. ''' if pen_state is not None: self._pen_state = pen_state if self._turtles.turtle_window.sharing() and share: event = 'p|%s' % (data_to_string([self._turtles.turtle_window.nick, self._pen_state])) self._turtles.turtle_window.send_event(event) def set_fill(self, state=False): self._pen_fill = state if not self._pen_fill: self._poly_points = [] def set_poly_points(self, poly_points=None): if poly_points is not None: self._poly_points = poly_points[:] def start_fill(self): self._pen_fill = True self._poly_points = [] def stop_fill(self, share=True): self._pen_fill = False if len(self._poly_points) == 0: return self._turtles.turtle_window.canvas.fill_polygon(self._poly_points) if self._turtles.turtle_window.sharing() and share: shared_poly_points = [] for p in self._poly_points: x, y = self._turtles.turtle_to_screen_coordinates( (p[1], p[2])) if p[0] in ['move', 'line']: shared_poly_points.append((p[0], x, y)) elif p[0] in ['rarc', 'larc']: shared_poly_points.append((p[0], x, y, p[3], p[4], p[5])) event = 'F|%s' % (data_to_string( [self._turtles.turtle_window.nick, shared_poly_points])) self._turtles.turtle_window.send_event(event) self._poly_points = [] def hide(self): if self.spr is not None: self.spr.hide() if self.label_block is not None: self.label_block.spr.hide() self._hidden = True def show(self): if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) self._hidden = False self.move_turtle_spr((self._x, self._y)) self.set_heading(self._heading, share=False) if self.label_block is not None: self.label_block.spr.set_layer(TURTLE_LAYER + 1) def move_turtle(self, pos=None): ''' Move the turtle's position ''' if pos is None: pos = self.get_xy() self._x, self._y = pos[0], pos[1] if self.spr is not None: self.move_turtle_spr(pos) def move_turtle_spr(self, pos): ''' Move the turtle's sprite ''' pos = self._turtles.turtle_to_screen_coordinates(pos) pos[0] -= self._half_width pos[1] -= self._half_height if not self._hidden and self.spr is not None: self.spr.move(pos) if self.label_block is not None: self.label_block.spr.move((pos[0] + self.label_xy[0], pos[1] + self.label_xy[1])) def right(self, degrees, share=True): ''' Rotate turtle clockwise ''' try: self._heading += degrees except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._heading %= 360 self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def _draw_line(self, old, new, pendown): if self._pen_state and pendown: self._turtles.turtle_window.canvas.set_source_rgb() pos1 = self._turtles.turtle_to_screen_coordinates(old) pos2 = self._turtles.turtle_to_screen_coordinates(new) self._turtles.turtle_window.canvas.draw_line(pos1[0], pos1[1], pos2[0], pos2[1]) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', pos1[0], pos1[1])) self._poly_points.append(('line', pos2[0], pos2[1])) def forward(self, distance, share=True): scaled_distance = distance * self._turtles.turtle_window.coord_scale old = self.get_xy() try: xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR) ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR) except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._draw_line(old, (xcor, ycor), True) self.move_turtle((xcor, ycor)) if self._turtles.turtle_window.sharing() and share: event = 'f|%s' % (data_to_string([self._turtles.turtle_window.nick, int(distance)])) self._turtles.turtle_window.send_event(event) def set_xy(self, x, y, share=True, pendown=True, dragging=False): old = self.get_xy() try: if dragging: xcor = x ycor = y else: xcor = x * self._turtles.turtle_window.coord_scale ycor = y * self._turtles.turtle_window.coord_scale except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._draw_line(old, (xcor, ycor), pendown) self.move_turtle((xcor, ycor)) if self._turtles.turtle_window.sharing() and share: event = 'x|%s' % (data_to_string([self._turtles.turtle_window.nick, [round_int(xcor), round_int(ycor)]])) self._turtles.turtle_window.send_event(event) def arc(self, a, r, share=True): ''' Draw an arc ''' if self._pen_state: self._turtles.turtle_window.canvas.set_source_rgb() try: if a < 0: pos = self.larc(-a, r) else: pos = self.rarc(a, r) except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self.move_turtle(pos) if self._turtles.turtle_window.sharing() and share: event = 'a|%s' % (data_to_string([self._turtles.turtle_window.nick, [round_int(a), round_int(r)]])) self._turtles.turtle_window.send_event(event) def rarc(self, a, r): ''' draw a clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] + r * cos(self._heading * DEGTOR) cy = pos[1] - r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.rarc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append(('rarc', npos[0], npos[1], r, (self._heading - 180) * DEGTOR, (self._heading - 180 + a) * DEGTOR)) self.right(a, False) return [cx - r * cos(self._heading * DEGTOR), cy + r * sin(self._heading * DEGTOR)] def larc(self, a, r): ''' draw a counter-clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] - r * cos(self._heading * DEGTOR) cy = pos[1] + r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.larc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append(('larc', npos[0], npos[1], r, (self._heading) * DEGTOR, (self._heading - a) * DEGTOR)) self.right(-a, False) return [cx + r * cos(self._heading * DEGTOR), cy - r * sin(self._heading * DEGTOR)] def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True): ''' Draw a pixbuf ''' self._turtles.turtle_window.canvas.draw_pixbuf( pixbuf, a, b, x, y, w, h, self._heading) if self._turtles.turtle_window.sharing() and share: if self._turtles.turtle_window.running_sugar: tmp_path = get_path(self._turtles.turtle_window.activity, 'instance') else: tmp_path = '/tmp' tmp_file = os.path.join( get_path(self._turtles.turtle_window.activity, 'instance'), 'tmpfile.png') pixbuf.save(tmp_file, 'png', {'quality': '100'}) data = image_to_base64(tmp_file, tmp_path) height = pixbuf.get_height() width = pixbuf.get_width() pos = self._turtles.screen_to_turtle_coordinates((x, y)) event = 'P|%s' % (data_to_string([self._turtles.turtle_window.nick, [round_int(a), round_int(b), round_int(pos[0]), round_int(pos[1]), round_int(w), round_int(h), round_int(width), round_int(height), data]])) gobject.idle_add(self._turtles.turtle_window.send_event, event) os.remove(tmp_file) def draw_text(self, label, x, y, size, w, share=True): ''' Draw text ''' self._turtles.turtle_window.canvas.draw_text( label, x, y, size, w, self._heading, self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'W|%s' % (data_to_string([self._turtles.turtle_window.nick, [label, round_int(x), round_int(y), round_int(size), round_int(w)]])) self._turtles.turtle_window.send_event(event) def get_name(self): return self._name def get_xy(self): return [self._x, self._y] def get_x(self): return self._x def get_y(self): return self._y def get_heading(self): return self._heading def get_color(self): return self._pen_color def get_gray(self): return self._pen_gray def get_shade(self): return self._pen_shade def get_pen_size(self): return self._pen_size def get_pen_state(self): return self._pen_state def get_fill(self): return self._pen_fill def get_poly_points(self): return self._poly_points def get_pixel(self): pos = self._turtles.turtle_to_screen_coordinates(self.get_xy()) return self._turtles.turtle_window.canvas.get_pixel(pos[0], pos[1]) def get_drag_radius(self): if self._drag_radius is None: self._calculate_sizes() return self._drag_radius
class Ball(): ''' The Bounce class is used to define the ball and the user interaction. ''' def __init__(self, sprites, filename): self._current_frame = 0 self._frames = [] # Easter Egg animation self._sprites = sprites self.ball = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf( svg_from_file(filename))) self.ball.set_layer(3) self.ball.set_label_attributes(24, vert_align='top') ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames.append(Sprite( self._sprites, 0, 0, svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer()))) for frame in self._frames: frame.set_layer(3) frame.move((0, -SIZE[1])) # move animation frames off screen def new_ball(self, filename): ''' Create a ball object and Easter Egg animation from an SVG file. ''' self.ball.set_shape(svg_str_to_pixbuf(svg_from_file(filename))) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames[i].set_shape(svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer())) def new_ball_from_image(self, filename, save_path): ''' Just create a ball object from an image file ''' if filename == '': _logger.debug('Image file not found.') return try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) if pixbuf.get_width() > pixbuf.get_height(): size = pixbuf.get_height() x = int((pixbuf.get_width() - size) / 2) else: size = pixbuf.get_width() x = int((pixbuf.get_height() - size) / 2) crop = GdkPixbuf.Pixbuf.new(0, True, 8, size, size) pixbuf.copy_area(x, 0, size, size, crop, 0, 0) scale = crop.scale_simple(85, 85, GdkPixbuf.InterpType.BILINEAR) scale.savev(save_path, 'png', [], []) self.ball.set_shape( svg_str_to_pixbuf(generate_ball_svg(save_path))) except Exception as e: _logger.error('Could not load image from %s: %s' % (filename, e)) def new_ball_from_fraction(self, fraction): ''' Create a ball with a section of size fraction. ''' r = SIZE[0] / 2.0 self.ball.set_shape(svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + svg_sector(r, r + BOX[1], r - 1, 1.999 * pi, COLORS[0], COLORS[1]) + svg_sector(r, r + BOX[1], r - 1, fraction * 2 * pi, COLORS[1], COLORS[0]) + svg_rect(BOX[0], BOX[1], 4, 4, 0, 0, '#FFFFFF', 'none') + svg_footer())) def ball_x(self): return self.ball.get_xy()[0] def ball_y(self): return self.ball.get_xy()[1] def frame_x(self, i): return self._frames[i].get_xy()[0] def frame_y(self, i): return self._frames[i].get_xy()[1] def width(self): return self.ball.rect[2] def height(self): return self.ball.rect[3] def move_ball(self, pos): self.ball.move(pos) def move_ball_relative(self, pos): self.ball.move_relative(pos) def move_frame(self, i, pos): self._frames[i].move(pos) def move_frame_relative(self, i, pos): self._frames[i].move_relative(pos) def hide_frames(self): for frame in self._frames: frame.move((0, -SIZE[1])) # hide the animation frames def next_frame(self, frame_counter): if frame_counter in ANIMATION: self._switch_frames(ANIMATION[frame_counter]) return self._current_frame def _switch_frames(self, frames): ''' Switch between frames in the animation ''' self.move_frame(frames[1], (self.frame_x(frames[0]), self.frame_y(frames[0]))) self.move_frame(frames[0], ((0, -SIZE[1]))) # hide the frame self._current_frame = frames[1]
class Turtle: def __init__(self, turtles, turtle_name, turtle_colors=None): #print 'class Turtle taturtle.py: def __init__' ''' The turtle is not a block, just a sprite with an orientation ''' self.spr = None self.label_block = None self._turtles = turtles self._shapes = [] self._custom_shapes = False self._name = turtle_name self._hidden = False self._remote = False self._x = 0.0 self._y = 0.0 self._3Dz = 0.0 self._3Dx = 0.0 self._3Dy = 0.0 self._heading = 0.0 self._roll = 0.0 self._pitch = 0.0 self._direction = [0.0, 1.0, 0.0] self._points = [[0., 0., 0.]] self._points_penstate = [1] self._half_width = 0 self._half_height = 0 self._drag_radius = None self._pen_shade = 50 self._pen_color = 0 self._pen_gray = 100 if self._turtles.turtle_window.coord_scale == 1: self._pen_size = 5 else: self._pen_size = 1 self._pen_state = True self._pen_fill = False self._poly_points = [] self._prep_shapes(turtle_name, self._turtles, turtle_colors) # Create a sprite for the turtle in interactive mode. if turtles.sprite_list is not None: self.spr = Sprite(self._turtles.sprite_list, 0, 0, self._shapes[0]) self._calculate_sizes() # Choose a random angle from which to attach the turtle # label to be used when sharing. angle = uniform(0, pi * 4 / 3.0) # 240 degrees width = self._shapes[0].get_width() radius = width * 0.67 # Restrict the angle to the sides: 30-150; 210-330 if angle > pi * 2 / 3.0: angle += pi / 2.0 # + 90 self.label_xy = [ int(radius * sin(angle)), int(radius * cos(angle) + width / 2.0) ] else: angle += pi / 6.0 # + 30 self.label_xy = [ int(radius * sin(angle) + width / 2.0), int(radius * cos(angle) + width / 2.0) ] self._turtles.add_to_dict(turtle_name, self) def _calculate_sizes(self): #print 'taturtle.py: def _calculate_sizes' self._half_width = int(self.spr.rect.width / 2.0) self._half_height = int(self.spr.rect.height / 2.0) self._drag_radius = ((self._half_width * self._half_width) + (self._half_height * self._half_height)) / 6 def set_remote(self): #print 'taturtle.py: def set_remote' self._remote = True def get_remote(self): #print 'taturtle.py: def get_remote' return self._remote def _prep_shapes(self, name, turtles=None, turtle_colors=None): #print 'taturtle.py: def _prep_shapes' # If the turtle name is an int, we'll use a palette color as the # turtle color try: int_key = int(name) use_color_table = True except ValueError: use_color_table = False if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = [ '#%06x' % (COLOR_TABLE[fill]), '#%06x' % (COLOR_TABLE[stroke]) ] self._shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: self.colors = DEFAULT_TURTLE_COLORS self._shapes = turtles.get_pixbufs() def set_turtle_colors(self, turtle_colors): #print 'taturtle.py: def set_turtle_colors' ''' reset the colors of a preloaded turtle ''' if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) self.set_heading(self._heading, share=False) def set_shapes(self, shapes, i=0): #print 'taturtle.py: def set_shapes' ''' Reskin the turtle ''' n = len(shapes) if n == 1 and i > 0: # set shape[i] if i < len(self._shapes): self._shapes[i] = shapes[0] elif n == SHAPES: # all shapes have been precomputed self._shapes = shapes[:] else: # rotate shapes if n != 1: debug_output("%d images passed to set_shapes: ignoring" % (n), self._turtles.turtle_window.running_sugar) if self._heading == 0.0: # rotate the shapes images = [] w, h = shapes[0].get_width(), shapes[0].get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(SHAPES): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context = gtk.gdk.CairoContext(context) context.translate(nw / 2.0, nh / 2.0) context.rotate(i * 10 * pi / 180.) context.translate(-nw / 2.0, -nh / 2.0) context.set_source_pixbuf(shapes[0], (nw - w) / 2.0, (nh - h) / 2.0) context.rectangle(0, 0, nw, nh) context.fill() images.append(surface) self._shapes = images[:] else: # associate shape with image at current heading j = int(self._heading + 5) % 360 / (360 / SHAPES) self._shapes[j] = shapes[0] self._custom_shapes = True self.show() self._calculate_sizes() def reset_shapes(self): #print 'taturtle.py: def reset_shapes' ''' Reset the shapes to the standard turtle ''' if self._custom_shapes: self._shapes = generate_turtle_pixbufs(self.colors) self._custom_shapes = False self._calculate_sizes() def _apply_rotations(self): self._direction = [0., 1., 0.] angle = self._heading * DEGTOR * -1.0 temp = [] temp.append((self._direction[0] * cos(angle)) - (self._direction[1] * sin(angle))) temp.append((self._direction[0] * sin(angle)) + (self._direction[1] * cos(angle))) temp.append(self._direction[2] * 1.0) self._direction = temp[:] angle = self._roll * DEGTOR * -1.0 temp = [] temp.append(self._direction[0] * 1.0) temp.append((self._direction[1] * cos(angle)) - (self._direction[2] * sin(angle))) temp.append((self._direction[1] * sin(angle)) + (self._direction[2] * cos(angle))) self._direction = temp[:] angle = self._pitch * DEGTOR * -1.0 temp = [] temp.append((self._direction[0] * cos(angle)) + (self._direction[2] * sin(angle))) temp.append(self._direction[1] * 1.0) temp.append((self._direction[0] * -1.0 * sin(angle)) + (self._direction[2] * cos(angle))) self._direction = temp[:] def set_heading(self, heading, share=True): #print 'taturtle.py: def set_heading' ''' Set the turtle heading (one shape per 360/SHAPES degrees) ''' self._heading = heading self._heading %= 360 self._apply_rotations() self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def set_roll(self, roll): ''' Set the turtle roll ''' self._roll = roll self._roll %= 360 self._apply_rotations() def set_pitch(self, pitch): ''' Set the turtle pitch ''' self._pitch = pitch self._pitch %= 360 self._apply_rotations() def _update_sprite_heading(self): #print 'taturtle.py: def _update_sprite_heading' ''' Update the sprite to reflect the current heading ''' i = (int(self._heading + 5) % 360) / (360 / SHAPES) if not self._hidden and self.spr is not None: try: self.spr.set_shape(self._shapes[i]) except IndexError: self.spr.set_shape(self._shapes[0]) def set_color(self, color=None, share=True): #print 'taturtle.py: def set_color' ''' Set the pen color for this turtle. ''' if isinstance(color, ColorObj): # See comment in tatype.py TYPE_BOX -> TYPE_COLOR color = color.color if color is None: color = self._pen_color # Special case for color blocks from CONSTANTS elif isinstance(color, Color): self.set_shade(color.shade, share) self.set_gray(color.gray, share) if color.color is not None: color = color.color else: color = self._pen_color self._pen_color = color self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'c|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_color)])) self._turtles.turtle_window.send_event(event) def set_gray(self, gray=None, share=True): #print 'taturtle.py: def set_gray' ''' Set the pen gray level for this turtle. ''' if gray is not None: self._pen_gray = gray if self._pen_gray < 0: self._pen_gray = 0 if self._pen_gray > 100: self._pen_gray = 100 self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'g|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_gray)])) self._turtles.turtle_window.send_event(event) def set_shade(self, shade=None, share=True): #print 'taturtle.py: def set_shade' ''' Set the pen shade for this turtle. ''' if shade is not None: self._pen_shade = shade self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 's|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_shade)])) self._turtles.turtle_window.send_event(event) def set_pen_size(self, pen_size=None, share=True): #print 'taturtle.py: def set_pen_size' ''' Set the pen size for this turtle. ''' if pen_size is not None: self._pen_size = max(0, pen_size) self._turtles.turtle_window.canvas.set_pen_size( self._pen_size * self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'w|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_size)])) self._turtles.turtle_window.send_event(event) def set_pen_state(self, pen_state=None, share=True): #print 'taturtle.py: def set_pen_state' ''' Set the pen state (down==True) for this turtle. ''' if pen_state is not None: self._pen_state = pen_state if self._turtles.turtle_window.sharing() and share: event = 'p|%s' % (data_to_string( [self._turtles.turtle_window.nick, self._pen_state])) self._turtles.turtle_window.send_event(event) def set_fill(self, state=False): #print 'taturtle.py: def set_fill' self._pen_fill = state if not self._pen_fill: self._poly_points = [] def set_poly_points(self, poly_points=None): #print 'taturtle.py: def set_poly_points' if poly_points is not None: self._poly_points = poly_points[:] def start_fill(self): #print 'taturtle.py: def start_fill' self._pen_fill = True self._poly_points = [] def stop_fill(self, share=True): #print 'taturtle.py: def stop_fill' self._pen_fill = False if len(self._poly_points) == 0: return self._turtles.turtle_window.canvas.fill_polygon(self._poly_points) if self._turtles.turtle_window.sharing() and share: shared_poly_points = [] for p in self._poly_points: x, y = self._turtles.turtle_to_screen_coordinates((p[1], p[2])) if p[0] in ['move', 'line']: shared_poly_points.append((p[0], x, y)) elif p[0] in ['rarc', 'larc']: shared_poly_points.append((p[0], x, y, p[3], p[4], p[5])) event = 'F|%s' % (data_to_string( [self._turtles.turtle_window.nick, shared_poly_points])) self._turtles.turtle_window.send_event(event) self._poly_points = [] def hide(self): #print 'taturtle.py: def hide' if self.spr is not None: self.spr.hide() if self.label_block is not None: self.label_block.spr.hide() self._hidden = True def show(self): #print 'taturtle.py: def show' if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) self._hidden = False self.move_turtle_spr((self._x, self._y)) self.set_heading(self._heading, share=False) if self.label_block is not None: self.label_block.spr.set_layer(TURTLE_LAYER + 1) def move_turtle(self, pos=None): #print 'taturtle.py: def move_turtle' ''' Move the turtle's position ''' if pos is None: pos = self.get_xy() self._x, self._y = pos[0], pos[1] if self.spr is not None: self.move_turtle_spr(pos) def move_turtle_spr(self, pos): #print 'taturtle.py: def move_turtle_spr' ''' Move the turtle's sprite ''' pos = self._turtles.turtle_to_screen_coordinates(pos) pos[0] -= self._half_width pos[1] -= self._half_height if not self._hidden and self.spr is not None: self.spr.move(pos) if self.label_block is not None: self.label_block.spr.move( (pos[0] + self.label_xy[0], pos[1] + self.label_xy[1])) def reset_3D(self): self._3Dx, self._3Dy, self._3Dz = 0.0, 0.0, 0.0 self._direction = [0.0, 1.0, 0.0] self._roll, self._pitch = 0.0, 0.0 self._points = [[0., 0., 0.]] self._points_penstate = [1] def right(self, degrees, share=True): #print 'taturtle.py: def right' ''' Rotate turtle clockwise ''' self._heading += degrees self._heading %= 360 self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def left(self, degrees, share=True): #print 'taturtle.py: def left' degrees = 0 - degrees self.right(degrees, share) def _draw_line(self, old, new, pendown): #print 'taturtle.py: def _draw_line' if self._pen_state and pendown: self._turtles.turtle_window.canvas.set_source_rgb() pos1 = self._turtles.turtle_to_screen_coordinates(old) pos2 = self._turtles.turtle_to_screen_coordinates(new) self._turtles.turtle_window.canvas.draw_line( pos1[0], pos1[1], pos2[0], pos2[1]) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', pos1[0], pos1[1])) self._poly_points.append(('line', pos2[0], pos2[1])) def draw_obj(self, file_name): vertices = [] lines = [] file_handle = open(file_name, 'r') for line in file_handle: temp = line.split() if temp[0] == 'v': vertices.append( [float(temp[1]), float(temp[2]), float(temp[3])]) if temp[0] == 'l': lines.append([int(temp[1]), int(temp[2])]) width = self._turtles.turtle_window.width height = self._turtles.turtle_window.height for line in lines: source = vertices[line[0] - 1] dest = vertices[line[1] - 1] source_point = Point3D(source[0], source[1], source[2]) p1 = source_point.project(width, height, 512, 512) pair1 = [p1.x, p1.y] pos1 = self._turtles.screen_to_turtle_coordinates(pair1) dest_point = Point3D(dest[0], dest[1], dest[2]) p2 = dest_point.project(width, height, 512, 512) pair2 = [p2.x, p2.y] pos2 = self._turtles.screen_to_turtle_coordinates(pair2) self._draw_line(pos1, pos2, True) self.move_turtle((pos2[0], pos2[1])) return vertices, lines def forward(self, distance, share=True): #print 'taturtle.py: def forward' scaled_distance = distance * self._turtles.turtle_window.coord_scale old = self.get_xy() #Projected Point old_3D = self.get_3Dpoint() #Actual Point #xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR) #ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR) xcor = old_3D[0] + scaled_distance * self._direction[0] ycor = old_3D[1] + scaled_distance * self._direction[1] zcor = old_3D[2] + scaled_distance * self._direction[2] width = self._turtles.turtle_window.width height = self._turtles.turtle_window.height old_point = Point3D(old_3D[0], old_3D[1], old_3D[2]) # Old point as Point3D object p = old_point.project(width, height, 512, 512) # Projected Old Point new_x, new_y = p.x, p.y pair1 = [new_x, new_y] pos1 = self._turtles.screen_to_turtle_coordinates(pair1) ''' for i, val in enumerate(old_3D): if (abs(val) < 0.0001): old_3D[i] = 0. old_3D[i] = round(old_3D[i], 2) self._points.append([old_3D[0], old_3D[1], old_3D[2]]) if (self._pen_state): self._points_penstate.append(1) else: self._points_penstate.append(0) ''' self._3Dx, self._3Dy, self._3Dz = xcor, ycor, zcor self.store_data() new_point = Point3D(xcor, ycor, zcor) # New point as 3D object p = new_point.project(width, height, 512, 512) # Projected New Point new_x, new_y = p.x, p.y pair2 = [new_x, new_y] pos2 = self._turtles.screen_to_turtle_coordinates(pair2) #print 'new = ', new_point.x, new_point.y, new_point.z self._draw_line(pos1, pos2, True) #self.move_turtle((xcor, ycor)) self.move_turtle((pos2[0], pos2[1])) if self._turtles.turtle_window.sharing() and share: event = 'f|%s' % (data_to_string( [self._turtles.turtle_window.nick, int(distance)])) self._turtles.turtle_window.send_event(event) def backward(self, distance, share=True): #print 'taturtle.py: def backward' distance = 0 - distance self.forward(distance, share) def set_xy(self, x, y, share=True, pendown=True, dragging=False): #print 'taturtle.py: def set_xy' old = self.get_xy() if dragging: xcor = x ycor = y else: xcor = x * self._turtles.turtle_window.coord_scale ycor = y * self._turtles.turtle_window.coord_scale self._draw_line(old, (xcor, ycor), pendown) self.move_turtle((xcor, ycor)) if self._turtles.turtle_window.sharing() and share: event = 'x|%s' % (data_to_string([ self._turtles.turtle_window.nick, [round_int(xcor), round_int(ycor)] ])) self._turtles.turtle_window.send_event(event) def set_xyz(self, x, y, z): ''' Set the x, y and z coordinates ''' self._3Dx, self._3Dy, self._3Dz = x, y, z self.store_data() point_3D = Point3D(x, y, z) width = self._turtles.turtle_window.width height = self._turtles.turtle_window.height p = point_3D.project(width, height, 512, 512) new_x, new_y = p.x, p.y pair = [new_x, new_y] pos = self._turtles.screen_to_turtle_coordinates(pair) self.set_xy(pos[0], pos[1]) def store_data(self): if (abs(self._3Dx) < 0.0001): self._3Dx = 0. if (abs(self._3Dy) < 0.0001): self._3Dy = 0. if (abs(self._3Dz) < 0.0001): self._3Dz = 0. self._3Dx = round(self._3Dx, 2) self._3Dy = round(self._3Dy, 2) self._3Dz = round(self._3Dz, 2) self._points.append([self._3Dx, self._3Dy, self._3Dz]) if (self._pen_state): self._points_penstate.append(1) else: self._points_penstate.append(0) def arc(self, a, r, share=True): #print 'taturtle.py: def arc' ''' Draw an arc ''' if self._pen_state: self._turtles.turtle_window.canvas.set_source_rgb() if a < 0: pos = self.larc(-a, r) else: pos = self.rarc(a, r) self.move_turtle(pos) if self._turtles.turtle_window.sharing() and share: event = 'a|%s' % (data_to_string([ self._turtles.turtle_window.nick, [round_int(a), round_int(r)] ])) self._turtles.turtle_window.send_event(event) def rarc(self, a, r): #print 'taturtle.py: def rarc' ''' draw a clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] + r * cos(self._heading * DEGTOR) cy = pos[1] - r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.rarc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append(('rarc', npos[0], npos[1], r, (self._heading - 180) * DEGTOR, (self._heading - 180 + a) * DEGTOR)) self.right(a, False) return [ cx - r * cos(self._heading * DEGTOR), cy + r * sin(self._heading * DEGTOR) ] def larc(self, a, r): #print 'taturtle.py: def larc' ''' draw a counter-clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] - r * cos(self._heading * DEGTOR) cy = pos[1] + r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.larc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append( ('larc', npos[0], npos[1], r, (self._heading) * DEGTOR, (self._heading - a) * DEGTOR)) self.right(-a, False) return [ cx + r * cos(self._heading * DEGTOR), cy - r * sin(self._heading * DEGTOR) ] def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True): #print 'taturtle.py: def draw_pixbuf' ''' Draw a pixbuf ''' self._turtles.turtle_window.canvas.draw_pixbuf(pixbuf, a, b, x, y, w, h, self._heading) if self._turtles.turtle_window.sharing() and share: if self._turtles.turtle_window.running_sugar: tmp_path = get_path(self._turtles.turtle_window.activity, 'instance') else: tmp_path = '/tmp' tmp_file = os.path.join( get_path(self._turtles.turtle_window.activity, 'instance'), 'tmpfile.png') pixbuf.save(tmp_file, 'png', {'quality': '100'}) data = image_to_base64(tmp_file, tmp_path) height = pixbuf.get_height() width = pixbuf.get_width() pos = self._turtles.screen_to_turtle_coordinates((x, y)) event = 'P|%s' % (data_to_string([ self._turtles.turtle_window.nick, [ round_int(a), round_int(b), round_int(pos[0]), round_int(pos[1]), round_int(w), round_int(h), round_int(width), round_int(height), data ] ])) gobject.idle_add(self._turtles.turtle_window.send_event, event) os.remove(tmp_file) def draw_text(self, label, x, y, size, w, share=True): #print 'taturtle.py: def draw_text' ''' Draw text ''' self._turtles.turtle_window.canvas.draw_text( label, x, y, size, w, self._heading, self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'W|%s' % (data_to_string([ self._turtles.turtle_window.nick, [ label, round_int(x), round_int(y), round_int(size), round_int(w) ] ])) self._turtles.turtle_window.send_event(event) def read_pixel(self): #print 'taturtle.py: def read_pixel' """ Read r, g, b, a from the canvas and push b, g, r to the stack """ r, g, b, a = self.get_pixel() self._turtles.turtle_window.lc.heap.append(b) self._turtles.turtle_window.lc.heap.append(g) self._turtles.turtle_window.lc.heap.append(r) def get_color_index(self): #print 'taturtle.py: def get_color_index' r, g, b, a = self.get_pixel() color_index = self._turtles.turtle_window.canvas.get_color_index( r, g, b) return color_index def get_name(self): #print 'taturtle.py: def get_name' return self._name def get_xy(self): #print 'taturtle.py: def get_xy' return [self._x, self._y] def get_3Dpoint(self): return [self._3Dx, self._3Dy, self._3Dz] def get_x(self): #print 'taturtle.py: def get_x' return self._3Dx def get_y(self): #print 'taturtle.py: def get_y' return self._3Dy def get_z(self): return self._3Dz def get_heading(self): #print 'taturtle.py: def get_heading' return self._heading def get_roll(self): return self._roll def get_pitch(self): return self._pitch def get_color(self): #print 'taturtle.py: def get_color' return self._pen_color def get_gray(self): #print 'taturtle.py: def get_gray' return self._pen_gray def get_shade(self): #print 'taturtle.py: def get_shade' return self._pen_shade def get_pen_size(self): #print 'taturtle.py: def get_pen_size' return self._pen_size def get_pen_state(self): #print 'taturtle.py: def get_pen_state' return self._pen_state def get_fill(self): #print 'taturtle.py: def get_fill' return self._pen_fill def get_poly_points(self): #print 'taturtle.py: def get_poly_points' return self._poly_points def get_pixel(self): #print 'taturtle.py: def get_pixel' pos = self._turtles.turtle_to_screen_coordinates(self.get_xy()) return self._turtles.turtle_window.canvas.get_pixel(pos[0], pos[1]) def get_drag_radius(self): #print 'taturtle.py: def get_drag_radius' if self._drag_radius is None: self._calculate_sizes() return self._drag_radius
class Turtle: def __init__(self, turtles, turtle_name, turtle_colors=None): #print 'class Turtle taturtle.py: def __init__' ''' The turtle is not a block, just a sprite with an orientation ''' self.spr = None self.label_block = None self._turtles = turtles self._shapes = [] self._custom_shapes = False self._name = turtle_name self._hidden = False self._remote = False self._x = 0.0 self._y = 0.0 self._3Dz = 0.0 self._3Dx = 0.0 self._3Dy = 0.0 self._heading = 0.0 self._roll = 0.0 self._pitch = 0.0 self._direction = [0.0, 1.0, 0.0] self._points = [[0., 0., 0.]] self._points_penstate = [1] self._half_width = 0 self._half_height = 0 self._drag_radius = None self._pen_shade = 50 self._pen_color = 0 self._pen_gray = 100 if self._turtles.turtle_window.coord_scale == 1: self._pen_size = 5 else: self._pen_size = 1 self._pen_state = True self._pen_fill = False self._poly_points = [] self._prep_shapes(turtle_name, self._turtles, turtle_colors) # Create a sprite for the turtle in interactive mode. if turtles.sprite_list is not None: self.spr = Sprite(self._turtles.sprite_list, 0, 0, self._shapes[0]) self._calculate_sizes() # Choose a random angle from which to attach the turtle # label to be used when sharing. angle = uniform(0, pi * 4 / 3.0) # 240 degrees width = self._shapes[0].get_width() radius = width * 0.67 # Restrict the angle to the sides: 30-150; 210-330 if angle > pi * 2 / 3.0: angle += pi / 2.0 # + 90 self.label_xy = [int(radius * sin(angle)), int(radius * cos(angle) + width / 2.0)] else: angle += pi / 6.0 # + 30 self.label_xy = [int(radius * sin(angle) + width / 2.0), int(radius * cos(angle) + width / 2.0)] self._turtles.add_to_dict(turtle_name, self) def _calculate_sizes(self): #print 'taturtle.py: def _calculate_sizes' self._half_width = int(self.spr.rect.width / 2.0) self._half_height = int(self.spr.rect.height / 2.0) self._drag_radius = ((self._half_width * self._half_width) + (self._half_height * self._half_height)) / 6 def set_remote(self): #print 'taturtle.py: def set_remote' self._remote = True def get_remote(self): #print 'taturtle.py: def get_remote' return self._remote def _prep_shapes(self, name, turtles=None, turtle_colors=None): #print 'taturtle.py: def _prep_shapes' # If the turtle name is an int, we'll use a palette color as the # turtle color try: int_key = int(name) use_color_table = True except ValueError: use_color_table = False if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = ['#%06x' % (COLOR_TABLE[fill]), '#%06x' % (COLOR_TABLE[stroke])] self._shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: self.colors = DEFAULT_TURTLE_COLORS self._shapes = turtles.get_pixbufs() def set_turtle_colors(self, turtle_colors): #print 'taturtle.py: def set_turtle_colors' ''' reset the colors of a preloaded turtle ''' if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) self.set_heading(self._heading, share=False) def set_shapes(self, shapes, i=0): #print 'taturtle.py: def set_shapes' ''' Reskin the turtle ''' n = len(shapes) if n == 1 and i > 0: # set shape[i] if i < len(self._shapes): self._shapes[i] = shapes[0] elif n == SHAPES: # all shapes have been precomputed self._shapes = shapes[:] else: # rotate shapes if n != 1: debug_output("%d images passed to set_shapes: ignoring" % (n), self._turtles.turtle_window.running_sugar) if self._heading == 0.0: # rotate the shapes images = [] w, h = shapes[0].get_width(), shapes[0].get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(SHAPES): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context = gtk.gdk.CairoContext(context) context.translate(nw / 2.0, nh / 2.0) context.rotate(i * 10 * pi / 180.) context.translate(-nw / 2.0, -nh / 2.0) context.set_source_pixbuf(shapes[0], (nw - w) / 2.0, (nh - h) / 2.0) context.rectangle(0, 0, nw, nh) context.fill() images.append(surface) self._shapes = images[:] else: # associate shape with image at current heading j = int(self._heading + 5) % 360 / (360 / SHAPES) self._shapes[j] = shapes[0] self._custom_shapes = True self.show() self._calculate_sizes() def reset_shapes(self): #print 'taturtle.py: def reset_shapes' ''' Reset the shapes to the standard turtle ''' if self._custom_shapes: self._shapes = generate_turtle_pixbufs(self.colors) self._custom_shapes = False self._calculate_sizes() def _apply_rotations(self): self._direction = [0., 1., 0.] angle = self._heading * DEGTOR * -1.0 temp = [] temp.append((self._direction[0] * cos(angle)) - (self._direction[1] * sin(angle))) temp.append((self._direction[0] * sin(angle)) + (self._direction[1] * cos(angle))) temp.append(self._direction[2] * 1.0) self._direction = temp[:] angle = self._roll * DEGTOR * -1.0 temp = [] temp.append(self._direction[0] * 1.0) temp.append((self._direction[1] * cos(angle)) - (self._direction[2] * sin(angle))) temp.append((self._direction[1] * sin(angle)) + (self._direction[2] * cos(angle))) self._direction = temp[:] angle = self._pitch * DEGTOR * -1.0 temp = [] temp.append((self._direction[0] * cos(angle)) + (self._direction[2] * sin(angle))) temp.append(self._direction[1] * 1.0) temp.append((self._direction[0] * -1.0 * sin(angle)) + (self._direction[2] * cos(angle))) self._direction = temp[:] def set_heading(self, heading, share=True): #print 'taturtle.py: def set_heading' ''' Set the turtle heading (one shape per 360/SHAPES degrees) ''' self._heading = heading self._heading %= 360 self._apply_rotations() self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def set_roll(self, roll): ''' Set the turtle roll ''' self._roll = roll self._roll %= 360 self._apply_rotations() def set_pitch(self, pitch): ''' Set the turtle pitch ''' self._pitch = pitch self._pitch %= 360 self._apply_rotations() def _update_sprite_heading(self): #print 'taturtle.py: def _update_sprite_heading' ''' Update the sprite to reflect the current heading ''' i = (int(self._heading + 5) % 360) / (360 / SHAPES) if not self._hidden and self.spr is not None: try: self.spr.set_shape(self._shapes[i]) except IndexError: self.spr.set_shape(self._shapes[0]) def set_color(self, color=None, share=True): #print 'taturtle.py: def set_color' ''' Set the pen color for this turtle. ''' if isinstance(color, ColorObj): # See comment in tatype.py TYPE_BOX -> TYPE_COLOR color = color.color if color is None: color = self._pen_color # Special case for color blocks from CONSTANTS elif isinstance(color, Color): self.set_shade(color.shade, share) self.set_gray(color.gray, share) if color.color is not None: color = color.color else: color = self._pen_color self._pen_color = color self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'c|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_color)])) self._turtles.turtle_window.send_event(event) def set_gray(self, gray=None, share=True): #print 'taturtle.py: def set_gray' ''' Set the pen gray level for this turtle. ''' if gray is not None: self._pen_gray = gray if self._pen_gray < 0: self._pen_gray = 0 if self._pen_gray > 100: self._pen_gray = 100 self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'g|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_gray)])) self._turtles.turtle_window.send_event(event) def set_shade(self, shade=None, share=True): #print 'taturtle.py: def set_shade' ''' Set the pen shade for this turtle. ''' if shade is not None: self._pen_shade = shade self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 's|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_shade)])) self._turtles.turtle_window.send_event(event) def set_pen_size(self, pen_size=None, share=True): #print 'taturtle.py: def set_pen_size' ''' Set the pen size for this turtle. ''' if pen_size is not None: self._pen_size = max(0, pen_size) self._turtles.turtle_window.canvas.set_pen_size( self._pen_size * self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'w|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._pen_size)])) self._turtles.turtle_window.send_event(event) def set_pen_state(self, pen_state=None, share=True): #print 'taturtle.py: def set_pen_state' ''' Set the pen state (down==True) for this turtle. ''' if pen_state is not None: self._pen_state = pen_state if self._turtles.turtle_window.sharing() and share: event = 'p|%s' % (data_to_string([self._turtles.turtle_window.nick, self._pen_state])) self._turtles.turtle_window.send_event(event) def set_fill(self, state=False): #print 'taturtle.py: def set_fill' self._pen_fill = state if not self._pen_fill: self._poly_points = [] def set_poly_points(self, poly_points=None): #print 'taturtle.py: def set_poly_points' if poly_points is not None: self._poly_points = poly_points[:] def start_fill(self): #print 'taturtle.py: def start_fill' self._pen_fill = True self._poly_points = [] def stop_fill(self, share=True): #print 'taturtle.py: def stop_fill' self._pen_fill = False if len(self._poly_points) == 0: return self._turtles.turtle_window.canvas.fill_polygon(self._poly_points) if self._turtles.turtle_window.sharing() and share: shared_poly_points = [] for p in self._poly_points: x, y = self._turtles.turtle_to_screen_coordinates( (p[1], p[2])) if p[0] in ['move', 'line']: shared_poly_points.append((p[0], x, y)) elif p[0] in ['rarc', 'larc']: shared_poly_points.append((p[0], x, y, p[3], p[4], p[5])) event = 'F|%s' % (data_to_string( [self._turtles.turtle_window.nick, shared_poly_points])) self._turtles.turtle_window.send_event(event) self._poly_points = [] def hide(self): #print 'taturtle.py: def hide' if self.spr is not None: self.spr.hide() if self.label_block is not None: self.label_block.spr.hide() self._hidden = True def show(self): #print 'taturtle.py: def show' if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) self._hidden = False self.move_turtle_spr((self._x, self._y)) self.set_heading(self._heading, share=False) if self.label_block is not None: self.label_block.spr.set_layer(TURTLE_LAYER + 1) def move_turtle(self, pos=None): #print 'taturtle.py: def move_turtle' ''' Move the turtle's position ''' if pos is None: pos = self.get_xy() self._x, self._y = pos[0], pos[1] if self.spr is not None: self.move_turtle_spr(pos) def move_turtle_spr(self, pos): #print 'taturtle.py: def move_turtle_spr' ''' Move the turtle's sprite ''' pos = self._turtles.turtle_to_screen_coordinates(pos) pos[0] -= self._half_width pos[1] -= self._half_height if not self._hidden and self.spr is not None: self.spr.move(pos) if self.label_block is not None: self.label_block.spr.move((pos[0] + self.label_xy[0], pos[1] + self.label_xy[1])) def reset_3D(self): self._3Dx, self._3Dy, self._3Dz = 0.0, 0.0, 0.0 self._direction = [0.0, 1.0, 0.0] self._roll, self._pitch = 0.0, 0.0 self._points = [[0., 0., 0.]] self._points_penstate = [1] def right(self, degrees, share=True): #print 'taturtle.py: def right' ''' Rotate turtle clockwise ''' self._heading += degrees self._heading %= 360 self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def left(self, degrees, share=True): #print 'taturtle.py: def left' degrees = 0 - degrees self.right(degrees, share) def _draw_line(self, old, new, pendown): #print 'taturtle.py: def _draw_line' if self._pen_state and pendown: self._turtles.turtle_window.canvas.set_source_rgb() pos1 = self._turtles.turtle_to_screen_coordinates(old) pos2 = self._turtles.turtle_to_screen_coordinates(new) self._turtles.turtle_window.canvas.draw_line(pos1[0], pos1[1], pos2[0], pos2[1]) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', pos1[0], pos1[1])) self._poly_points.append(('line', pos2[0], pos2[1])) def draw_obj(self, file_name): vertices = [] lines = [] file_handle = open(file_name, 'r') for line in file_handle: temp = line.split() if temp[0] == 'v': vertices.append([float(temp[1]), float(temp[2]), float(temp[3])]) if temp[0] == 'l': lines.append([int(temp[1]), int(temp[2])]) width = self._turtles.turtle_window.width height = self._turtles.turtle_window.height for line in lines: source = vertices[line[0] - 1] dest = vertices[line[1] - 1] source_point = Point3D(source[0], source[1], source[2]) p1 = source_point.project(width, height, 512, 512) pair1 = [p1.x, p1.y] pos1 = self._turtles.screen_to_turtle_coordinates(pair1) dest_point = Point3D(dest[0], dest[1], dest[2]) p2 = dest_point.project(width, height, 512, 512) pair2 = [p2.x, p2.y] pos2 = self._turtles.screen_to_turtle_coordinates(pair2) self._draw_line(pos1, pos2, True) self.move_turtle((pos2[0], pos2[1])) return vertices, lines def forward(self, distance, share=True): #print 'taturtle.py: def forward' scaled_distance = distance * self._turtles.turtle_window.coord_scale old = self.get_xy() #Projected Point old_3D = self.get_3Dpoint() #Actual Point #xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR) #ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR) xcor = old_3D[0] + scaled_distance * self._direction[0] ycor = old_3D[1] + scaled_distance * self._direction[1] zcor = old_3D[2] + scaled_distance * self._direction[2] width = self._turtles.turtle_window.width height = self._turtles.turtle_window.height old_point = Point3D(old_3D[0], old_3D[1], old_3D[2]) # Old point as Point3D object p = old_point.project(width, height, 512, 512) # Projected Old Point new_x, new_y = p.x, p.y pair1 = [new_x, new_y] pos1 = self._turtles.screen_to_turtle_coordinates(pair1) ''' for i, val in enumerate(old_3D): if (abs(val) < 0.0001): old_3D[i] = 0. old_3D[i] = round(old_3D[i], 2) self._points.append([old_3D[0], old_3D[1], old_3D[2]]) if (self._pen_state): self._points_penstate.append(1) else: self._points_penstate.append(0) ''' self._3Dx, self._3Dy, self._3Dz = xcor, ycor, zcor self.store_data() new_point = Point3D(xcor, ycor, zcor) # New point as 3D object p = new_point.project(width, height, 512, 512) # Projected New Point new_x, new_y = p.x, p.y pair2 = [new_x, new_y] pos2 = self._turtles.screen_to_turtle_coordinates(pair2) #print 'new = ', new_point.x, new_point.y, new_point.z self._draw_line(pos1, pos2, True) #self.move_turtle((xcor, ycor)) self.move_turtle((pos2[0], pos2[1])) if self._turtles.turtle_window.sharing() and share: event = 'f|%s' % (data_to_string([self._turtles.turtle_window.nick, int(distance)])) self._turtles.turtle_window.send_event(event) def backward(self, distance, share=True): #print 'taturtle.py: def backward' distance = 0 - distance self.forward(distance, share) def set_xy(self, x, y, share=True, pendown=True, dragging=False): #print 'taturtle.py: def set_xy' old = self.get_xy() if dragging: xcor = x ycor = y else: xcor = x * self._turtles.turtle_window.coord_scale ycor = y * self._turtles.turtle_window.coord_scale self._draw_line(old, (xcor, ycor), pendown) self.move_turtle((xcor, ycor)) if self._turtles.turtle_window.sharing() and share: event = 'x|%s' % (data_to_string([self._turtles.turtle_window.nick, [round_int(xcor), round_int(ycor)]])) self._turtles.turtle_window.send_event(event) def set_xyz(self, x, y, z): ''' Set the x, y and z coordinates ''' self._3Dx, self._3Dy, self._3Dz = x, y, z self.store_data() point_3D = Point3D(x, y, z) width = self._turtles.turtle_window.width height = self._turtles.turtle_window.height p = point_3D.project(width, height, 512, 512) new_x, new_y = p.x, p.y pair = [new_x, new_y] pos = self._turtles.screen_to_turtle_coordinates(pair) self.set_xy(pos[0], pos[1]) def store_data(self): if(abs(self._3Dx) < 0.0001): self._3Dx = 0. if(abs(self._3Dy) < 0.0001): self._3Dy = 0. if(abs(self._3Dz) < 0.0001): self._3Dz = 0. self._3Dx = round(self._3Dx, 2) self._3Dy = round(self._3Dy, 2) self._3Dz = round(self._3Dz, 2) self._points.append([self._3Dx, self._3Dy, self._3Dz]) if (self._pen_state): self._points_penstate.append(1) else: self._points_penstate.append(0) def arc(self, a, r, share=True): #print 'taturtle.py: def arc' ''' Draw an arc ''' if self._pen_state: self._turtles.turtle_window.canvas.set_source_rgb() if a < 0: pos = self.larc(-a, r) else: pos = self.rarc(a, r) self.move_turtle(pos) if self._turtles.turtle_window.sharing() and share: event = 'a|%s' % (data_to_string([self._turtles.turtle_window.nick, [round_int(a), round_int(r)]])) self._turtles.turtle_window.send_event(event) def rarc(self, a, r): #print 'taturtle.py: def rarc' ''' draw a clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] + r * cos(self._heading * DEGTOR) cy = pos[1] - r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.rarc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append(('rarc', npos[0], npos[1], r, (self._heading - 180) * DEGTOR, (self._heading - 180 + a) * DEGTOR)) self.right(a, False) return [cx - r * cos(self._heading * DEGTOR), cy + r * sin(self._heading * DEGTOR)] def larc(self, a, r): #print 'taturtle.py: def larc' ''' draw a counter-clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] - r * cos(self._heading * DEGTOR) cy = pos[1] + r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.larc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append(('larc', npos[0], npos[1], r, (self._heading) * DEGTOR, (self._heading - a) * DEGTOR)) self.right(-a, False) return [cx + r * cos(self._heading * DEGTOR), cy - r * sin(self._heading * DEGTOR)] def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True): #print 'taturtle.py: def draw_pixbuf' ''' Draw a pixbuf ''' self._turtles.turtle_window.canvas.draw_pixbuf( pixbuf, a, b, x, y, w, h, self._heading) if self._turtles.turtle_window.sharing() and share: if self._turtles.turtle_window.running_sugar: tmp_path = get_path(self._turtles.turtle_window.activity, 'instance') else: tmp_path = '/tmp' tmp_file = os.path.join( get_path(self._turtles.turtle_window.activity, 'instance'), 'tmpfile.png') pixbuf.save(tmp_file, 'png', {'quality': '100'}) data = image_to_base64(tmp_file, tmp_path) height = pixbuf.get_height() width = pixbuf.get_width() pos = self._turtles.screen_to_turtle_coordinates((x, y)) event = 'P|%s' % (data_to_string([self._turtles.turtle_window.nick, [round_int(a), round_int(b), round_int(pos[0]), round_int(pos[1]), round_int(w), round_int(h), round_int(width), round_int(height), data]])) gobject.idle_add(self._turtles.turtle_window.send_event, event) os.remove(tmp_file) def draw_text(self, label, x, y, size, w, share=True): #print 'taturtle.py: def draw_text' ''' Draw text ''' self._turtles.turtle_window.canvas.draw_text( label, x, y, size, w, self._heading, self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'W|%s' % (data_to_string([self._turtles.turtle_window.nick, [label, round_int(x), round_int(y), round_int(size), round_int(w)]])) self._turtles.turtle_window.send_event(event) def read_pixel(self): #print 'taturtle.py: def read_pixel' """ Read r, g, b, a from the canvas and push b, g, r to the stack """ r, g, b, a = self.get_pixel() self._turtles.turtle_window.lc.heap.append(b) self._turtles.turtle_window.lc.heap.append(g) self._turtles.turtle_window.lc.heap.append(r) def get_color_index(self): #print 'taturtle.py: def get_color_index' r, g, b, a = self.get_pixel() color_index = self._turtles.turtle_window.canvas.get_color_index( r, g, b) return color_index def get_name(self): #print 'taturtle.py: def get_name' return self._name def get_xy(self): #print 'taturtle.py: def get_xy' return [self._x, self._y] def get_3Dpoint(self): return [self._3Dx, self._3Dy, self._3Dz] def get_x(self): #print 'taturtle.py: def get_x' return self._3Dx def get_y(self): #print 'taturtle.py: def get_y' return self._3Dy def get_z(self): return self._3Dz def get_heading(self): #print 'taturtle.py: def get_heading' return self._heading def get_roll(self): return self._roll def get_pitch(self): return self._pitch def get_color(self): #print 'taturtle.py: def get_color' return self._pen_color def get_gray(self): #print 'taturtle.py: def get_gray' return self._pen_gray def get_shade(self): #print 'taturtle.py: def get_shade' return self._pen_shade def get_pen_size(self): #print 'taturtle.py: def get_pen_size' return self._pen_size def get_pen_state(self): #print 'taturtle.py: def get_pen_state' return self._pen_state def get_fill(self): #print 'taturtle.py: def get_fill' return self._pen_fill def get_poly_points(self): #print 'taturtle.py: def get_poly_points' return self._poly_points def get_pixel(self): #print 'taturtle.py: def get_pixel' pos = self._turtles.turtle_to_screen_coordinates(self.get_xy()) return self._turtles.turtle_window.canvas.get_pixel(pos[0], pos[1]) def get_drag_radius(self): #print 'taturtle.py: def get_drag_radius' if self._drag_radius is None: self._calculate_sizes() return self._drag_radius
class Turtle: def __init__(self, turtles, turtle_name, turtle_colors=None): ''' The turtle is not a block, just a sprite with an orientation ''' self.spr = None self.label_block = None self._turtles = turtles self._shapes = [] self._custom_shapes = False self._name = turtle_name self._hidden = False self._remote = False self._x = 0.0 self._y = 0.0 self._heading = 0.0 self._half_width = 0 self._half_height = 0 self._drag_radius = None self._pen_shade = 50 self._pen_color = 0 self._pen_gray = 100 if self._turtles.turtle_window.coord_scale == 1: self._pen_size = 5 else: self._pen_size = 1 self._pen_state = True self._pen_fill = False self._poly_points = [] self._prep_shapes(turtle_name, self._turtles, turtle_colors) # Create a sprite for the turtle in interactive mode. if turtles.sprite_list is not None: self.spr = Sprite(self._turtles.sprite_list, 0, 0, self._shapes[0]) self._calculate_sizes() # Choose a random angle from which to attach the turtle # label to be used when sharing. angle = uniform(0, pi * 4 / 3.0) # 240 degrees width = self._shapes[0].get_width() radius = width * 0.67 # Restrict the angle to the sides: 30-150; 210-330 if angle > pi * 2 / 3.0: angle += pi / 2.0 # + 90 self.label_xy = [ int(radius * sin(angle)), int(radius * cos(angle) + width / 2.0) ] else: angle += pi / 6.0 # + 30 self.label_xy = [ int(radius * sin(angle) + width / 2.0), int(radius * cos(angle) + width / 2.0) ] self._turtles.add_to_dict(turtle_name, self) def _calculate_sizes(self): self._half_width = int(self.spr.rect.width / 2.0) self._half_height = int(self.spr.rect.height / 2.0) self._drag_radius = ((self._half_width * self._half_width) + (self._half_height * self._half_height)) / 6 def set_remote(self): self._remote = True def get_remote(self): return self._remote def _prep_shapes(self, name, turtles=None, turtle_colors=None): # If the turtle name is an int, we'll use a palette color as the # turtle color try: int_key = int(name) use_color_table = True except ValueError: use_color_table = False if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = [ '#%06x' % (COLOR_TABLE[fill]), '#%06x' % (COLOR_TABLE[stroke]) ] self._shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: self.colors = DEFAULT_TURTLE_COLORS self._shapes = turtles.get_pixbufs() def set_turtle_colors(self, turtle_colors): ''' reset the colors of a preloaded turtle ''' if turtle_colors is not None: self.colors = turtle_colors[:] self._shapes = generate_turtle_pixbufs(self.colors) self.set_heading(self._heading, share=False) def set_shapes(self, shapes, i=0): ''' Reskin the turtle ''' n = len(shapes) if n == 1 and i > 0: # set shape[i] if i < len(self._shapes): self._shapes[i] = shapes[0] elif n == SHAPES: # all shapes have been precomputed self._shapes = shapes[:] else: # rotate shapes if n != 1: debug_output("%d images passed to set_shapes: ignoring" % (n), self._turtles.turtle_window.running_sugar) if self._heading == 0.0: # rotate the shapes images = [] w, h = shapes[0].get_width(), shapes[0].get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(SHAPES): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context = gtk.gdk.CairoContext(context) context.translate(nw / 2.0, nh / 2.0) context.rotate(i * 10 * pi / 180.) context.translate(-nw / 2.0, -nh / 2.0) context.set_source_pixbuf(shapes[0], (nw - w) / 2.0, (nh - h) / 2.0) context.rectangle(0, 0, nw, nh) context.fill() images.append(surface) self._shapes = images[:] else: # associate shape with image at current heading j = int(self._heading + 5) % 360 / (360 / SHAPES) self._shapes[j] = shapes[0] self._custom_shapes = True self.show() self._calculate_sizes() def reset_shapes(self): ''' Reset the shapes to the standard turtle ''' if self._custom_shapes: self._shapes = generate_turtle_pixbufs(self.colors) self._custom_shapes = False self._calculate_sizes() def set_heading(self, heading, share=True): ''' Set the turtle heading (one shape per 360/SHAPES degrees) ''' try: self._heading = heading except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._heading %= 360 self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def _update_sprite_heading(self): ''' Update the sprite to reflect the current heading ''' i = (int(self._heading + 5) % 360) / (360 / SHAPES) if not self._hidden and self.spr is not None: try: self.spr.set_shape(self._shapes[i]) except IndexError: self.spr.set_shape(self._shapes[0]) def set_color(self, color=None, share=True): ''' Set the pen color for this turtle. ''' # Special case for color blocks if color is not None and color in COLORDICT: self.set_shade(COLORDICT[color][1], share) self.set_gray(COLORDICT[color][2], share) if COLORDICT[color][0] is not None: self.set_color(COLORDICT[color][0], share) color = COLORDICT[color][0] else: color = self._pen_color elif color is None: color = self._pen_color try: self._pen_color = color except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'c|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_color)])) self._turtles.turtle_window.send_event(event) def set_gray(self, gray=None, share=True): ''' Set the pen gray level for this turtle. ''' if gray is not None: try: self._pen_gray = gray except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return if self._pen_gray < 0: self._pen_gray = 0 if self._pen_gray > 100: self._pen_gray = 100 self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 'g|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_gray)])) self._turtles.turtle_window.send_event(event) def set_shade(self, shade=None, share=True): ''' Set the pen shade for this turtle. ''' if shade is not None: try: self._pen_shade = shade except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, gray=self._pen_gray, color=self._pen_color) if self._turtles.turtle_window.sharing() and share: event = 's|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_shade)])) self._turtles.turtle_window.send_event(event) def set_pen_size(self, pen_size=None, share=True): ''' Set the pen size for this turtle. ''' if pen_size is not None: try: self._pen_size = max(0, pen_size) except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._turtles.turtle_window.canvas.set_pen_size( self._pen_size * self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'w|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._pen_size)])) self._turtles.turtle_window.send_event(event) def set_pen_state(self, pen_state=None, share=True): ''' Set the pen state (down==True) for this turtle. ''' if pen_state is not None: self._pen_state = pen_state if self._turtles.turtle_window.sharing() and share: event = 'p|%s' % (data_to_string( [self._turtles.turtle_window.nick, self._pen_state])) self._turtles.turtle_window.send_event(event) def set_fill(self, state=False): self._pen_fill = state if not self._pen_fill: self._poly_points = [] def set_poly_points(self, poly_points=None): if poly_points is not None: self._poly_points = poly_points[:] def start_fill(self): self._pen_fill = True self._poly_points = [] def stop_fill(self, share=True): self._pen_fill = False if len(self._poly_points) == 0: return self._turtles.turtle_window.canvas.fill_polygon(self._poly_points) if self._turtles.turtle_window.sharing() and share: shared_poly_points = [] for p in self._poly_points: x, y = self._turtles.turtle_to_screen_coordinates((p[1], p[2])) if p[0] in ['move', 'line']: shared_poly_points.append((p[0], x, y)) elif p[0] in ['rarc', 'larc']: shared_poly_points.append((p[0], x, y, p[3], p[4], p[5])) event = 'F|%s' % (data_to_string( [self._turtles.turtle_window.nick, shared_poly_points])) self._turtles.turtle_window.send_event(event) self._poly_points = [] def hide(self): if self.spr is not None: self.spr.hide() if self.label_block is not None: self.label_block.spr.hide() self._hidden = True def show(self): if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) self._hidden = False self.move_turtle_spr((self._x, self._y)) self.set_heading(self._heading, share=False) if self.label_block is not None: self.label_block.spr.set_layer(TURTLE_LAYER + 1) def move_turtle(self, pos=None): ''' Move the turtle's position ''' if pos is None: pos = self.get_xy() self._x, self._y = pos[0], pos[1] if self.spr is not None: self.move_turtle_spr(pos) def move_turtle_spr(self, pos): ''' Move the turtle's sprite ''' pos = self._turtles.turtle_to_screen_coordinates(pos) pos[0] -= self._half_width pos[1] -= self._half_height if not self._hidden and self.spr is not None: self.spr.move(pos) if self.label_block is not None: self.label_block.spr.move( (pos[0] + self.label_xy[0], pos[1] + self.label_xy[1])) def right(self, degrees, share=True): ''' Rotate turtle clockwise ''' try: self._heading += degrees except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._heading %= 360 self._update_sprite_heading() if self._turtles.turtle_window.sharing() and share: event = 'r|%s' % (data_to_string( [self._turtles.turtle_window.nick, round_int(self._heading)])) self._turtles.turtle_window.send_event(event) def _draw_line(self, old, new, pendown): if self._pen_state and pendown: self._turtles.turtle_window.canvas.set_source_rgb() pos1 = self._turtles.turtle_to_screen_coordinates(old) pos2 = self._turtles.turtle_to_screen_coordinates(new) self._turtles.turtle_window.canvas.draw_line( pos1[0], pos1[1], pos2[0], pos2[1]) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', pos1[0], pos1[1])) self._poly_points.append(('line', pos2[0], pos2[1])) def forward(self, distance, share=True): scaled_distance = distance * self._turtles.turtle_window.coord_scale old = self.get_xy() try: xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR) ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR) except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._draw_line(old, (xcor, ycor), True) self.move_turtle((xcor, ycor)) if self._turtles.turtle_window.sharing() and share: event = 'f|%s' % (data_to_string( [self._turtles.turtle_window.nick, int(distance)])) self._turtles.turtle_window.send_event(event) def set_xy(self, x, y, share=True, pendown=True, dragging=False): old = self.get_xy() try: if dragging: xcor = x ycor = y else: xcor = x * self._turtles.turtle_window.coord_scale ycor = y * self._turtles.turtle_window.coord_scale except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self._draw_line(old, (xcor, ycor), pendown) self.move_turtle((xcor, ycor)) if self._turtles.turtle_window.sharing() and share: event = 'x|%s' % (data_to_string([ self._turtles.turtle_window.nick, [round_int(xcor), round_int(ycor)] ])) self._turtles.turtle_window.send_event(event) def arc(self, a, r, share=True): ''' Draw an arc ''' if self._pen_state: self._turtles.turtle_window.canvas.set_source_rgb() try: if a < 0: pos = self.larc(-a, r) else: pos = self.rarc(a, r) except (TypeError, ValueError): debug_output('bad value sent to %s' % (__name__), self._turtles.turtle_window.running_sugar) return self.move_turtle(pos) if self._turtles.turtle_window.sharing() and share: event = 'a|%s' % (data_to_string([ self._turtles.turtle_window.nick, [round_int(a), round_int(r)] ])) self._turtles.turtle_window.send_event(event) def rarc(self, a, r): ''' draw a clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] + r * cos(self._heading * DEGTOR) cy = pos[1] - r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.rarc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append( ('rarc', npos[0], npos[1], r, (self._heading - 180) * DEGTOR, (self._heading - 180 + a) * DEGTOR)) self.right(a, False) return [ cx - r * cos(self._heading * DEGTOR), cy + r * sin(self._heading * DEGTOR) ] def larc(self, a, r): ''' draw a counter-clockwise arc ''' r *= self._turtles.turtle_window.coord_scale if r < 0: r = -r a = -a pos = self.get_xy() cx = pos[0] - r * cos(self._heading * DEGTOR) cy = pos[1] + r * sin(self._heading * DEGTOR) if self._pen_state: npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) self._turtles.turtle_window.canvas.larc(npos[0], npos[1], r, a, self._heading) if self._pen_fill: if self._poly_points == []: self._poly_points.append(('move', npos[0], npos[1])) self._poly_points.append( ('larc', npos[0], npos[1], r, (self._heading) * DEGTOR, (self._heading - a) * DEGTOR)) self.right(-a, False) return [ cx + r * cos(self._heading * DEGTOR), cy - r * sin(self._heading * DEGTOR) ] def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True): ''' Draw a pixbuf ''' self._turtles.turtle_window.canvas.draw_pixbuf(pixbuf, a, b, x, y, w, h, self._heading) if self._turtles.turtle_window.sharing() and share: if self._turtles.turtle_window.running_sugar: tmp_path = get_path(self._turtles.turtle_window.activity, 'instance') else: tmp_path = '/tmp' tmp_file = os.path.join( get_path(self._turtles.turtle_window.activity, 'instance'), 'tmpfile.png') pixbuf.save(tmp_file, 'png', {'quality': '100'}) data = image_to_base64(tmp_file, tmp_path) height = pixbuf.get_height() width = pixbuf.get_width() pos = self._turtles.screen_to_turtle_coordinates((x, y)) event = 'P|%s' % (data_to_string([ self._turtles.turtle_window.nick, [ round_int(a), round_int(b), round_int(pos[0]), round_int(pos[1]), round_int(w), round_int(h), round_int(width), round_int(height), data ] ])) gobject.idle_add(self._turtles.turtle_window.send_event, event) os.remove(tmp_file) def draw_text(self, label, x, y, size, w, share=True): ''' Draw text ''' self._turtles.turtle_window.canvas.draw_text( label, x, y, size, w, self._heading, self._turtles.turtle_window.coord_scale) if self._turtles.turtle_window.sharing() and share: event = 'W|%s' % (data_to_string([ self._turtles.turtle_window.nick, [ label, round_int(x), round_int(y), round_int(size), round_int(w) ] ])) self._turtles.turtle_window.send_event(event) def get_name(self): return self._name def get_xy(self): return [self._x, self._y] def get_x(self): return self._x def get_y(self): return self._y def get_heading(self): return self._heading def get_color(self): return self._pen_color def get_gray(self): return self._pen_gray def get_shade(self): return self._pen_shade def get_pen_size(self): return self._pen_size def get_pen_state(self): return self._pen_state def get_fill(self): return self._pen_fill def get_poly_points(self): return self._poly_points def get_pixel(self): pos = self._turtles.turtle_to_screen_coordinates(self.get_xy()) return self._turtles.turtle_window.canvas.get_pixel(pos[0], pos[1]) def get_drag_radius(self): if self._drag_radius is None: self._calculate_sizes() return self._drag_radius
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = colors self._canvas = canvas parent.show_all() self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) self._scale = self._height / (14.0 * DOT_SIZE * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._turtle_offset = 0 self._space = int(self._dot_size / 5.) self._orientation = 0 self.level = 0 self.custom_strategy = None self.strategies = [BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY, self.custom_strategy] self.strategy = self.strategies[self.level] self._timeout_id = None # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] for y in range(THIRTEEN): for x in range(THIRTEEN): xoffset = int((self._width - THIRTEEN * (self._dot_size + \ self._space) - self._space) / 2.) if y % 2 == 1: xoffset += int((self._dot_size + self._space) / 2.) if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot('#B0B0B0'))) else: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[FILL]))) self._dots[-1].type = False # not set # Put a turtle at the center of the screen... self._turtle_images = [] self._rotate_turtle(self._new_turtle()) self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0]) self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) # ...and initialize. self._all_clear() def _move_turtle(self, pos): ''' Move turtle and add its offset ''' self._turtle.move(pos) self._turtle.move_relative( (-self._turtle_offset, -self._turtle_offset)) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' # Clear dots for dot in self._dots: if dot.type: dot.type = False dot.set_shape(self._new_dot(self._colors[FILL])) dot.set_label('') # Recenter the turtle self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) self._turtle.set_shape(self._turtle_images[0]) self._set_label('') if self._timeout_id is not None: GObject.source_remove(self._timeout_id) def new_game(self, saved_state=None): ''' Start a new game. ''' self._all_clear() # Fill in a few dots to start for i in range(15): n = int(uniform(0, THIRTEEN * THIRTEEN)) if self._dots[n].type is not None: self._dots[n].type = True self._dots[n].set_shape(self._new_dot(self._colors[STROKE])) # Calculate the distances to the edge self._initialize_weights() self.strategy = self.strategies[self.level] def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._activity.status.set_label(string) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) spr = self._sprites.find_sprite((x, y), inverse=True) if spr == None: return if spr.type is not None and not spr.type: spr.type = True spr.set_shape(self._new_dot(self._colors[STROKE])) self._weights[self._dots.index(spr)] = 1000 self._test_game_over(self._move_the_turtle()) return True def _find_the_turtle(self): turtle_pos = self._turtle.get_xy() turtle_dot = None for dot in self._dots: pos = dot.get_xy() # Turtle is offset if pos[0] == turtle_pos[0] + self._turtle_offset and \ pos[1] == turtle_pos[1] + self._turtle_offset: turtle_dot = self._dots.index(dot) break if turtle_dot is None: _logger.debug('Cannot find the turtle...') return None return turtle_dot def _move_the_turtle(self): ''' Move the turtle after each click ''' self._turtle_dot = self._find_the_turtle() if self._turtle_dot is None: return # Given the col and row of the turtle, do something new_dot = self._grid_to_dot( self._my_strategy_import(self.strategy, self._dot_to_grid(self._turtle_dot))) self._move_turtle(self._dots[new_dot].get_xy()) # And set the orientation self._turtle.set_shape(self._turtle_images[self._orientation]) return new_dot def _test_game_over(self, new_dot): ''' Check to see if game is over ''' if new_dot is None: return if self._dots[new_dot].type is None: # Game-over feedback self._once_around = False self._happy_turtle_dance() return True c = int(self._turtle_dot / THIRTEEN) % 2 if self._dots[ new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \ self._dots[ new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \ self._dots[ new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \ self._dots[ new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \ self._dots[ new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \ self._dots[ new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type: # Game-over feedback for dot in self._dots: dot.set_label(':)') return True return False def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * THIRTEEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % THIRTEEN, int(dot / THIRTEEN)] def _happy_turtle_dance(self): ''' Turtle dances along the edge ''' i = self._find_the_turtle() if i == 0: if self._once_around: return else: self._once_around = True _logger.debug(i) x, y = self._dot_to_grid(i) if y == 0: x += 1 if x == 0: y -= 1 if x == THIRTEEN - 1: y += 1 if y == THIRTEEN - 1: x -= 1 i = self._grid_to_dot((x, y)) self._dots[i].set_label(':)') self._move_turtle(self._dots[i].get_xy()) self._orientation += 1 self._orientation %= 6 self._turtle.set_shape(self._turtle_images[self._orientation]) self._timeout_id = GObject.timeout_add(250, self._happy_turtle_dance) def _ordered_weights(self, pos): ''' Returns the list of surrounding points sorted by their distance to the edge ''' dots = self._surrounding_dots(pos) dots_and_weights = [] for dot in dots: dots_and_weights.append((dot, self._weights[dot])) sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1]) for i in range(6): dots[i] = sorted_dots[i][0] return dots def _daylight_ahead(self, pos): ''' Returns true if there is a straight path to the edge from the current position/orientation ''' dots = self._surrounding_dots(pos) while True: dot_type = self._dots[dots[self._orientation]].type if dot_type is None: return True elif dot_type: return False else: # keep looking pos = self._dot_to_grid(dots[self._orientation]) dots = self._surrounding_dots(pos) def _surrounding_dots(self, pos): ''' Returns dots surrounding a position in the grid ''' dots = [] evenodd = pos[1] % 2 for i in range(6): col = pos[0] + CIRCLE[evenodd][i][0] row = pos[1] + CIRCLE[evenodd][i][1] dots.append(self._grid_to_dot((col, row))) return dots def _initialize_weights(self): ''' How many steps to an edge? ''' self._weights = [] for d, dot in enumerate(self._dots): if dot.type is None: self._weights.append(0) elif dot.type: self._weights.append(1000) else: pos = self._dot_to_grid(d) pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1]) self._weights.append(min(min(pos[0], pos2[0]), min(pos[1], pos2[1]))) def _my_strategy_import(self, f, arg): ''' Run Python code passed as argument ''' userdefined = {} try: exec f in globals(), userdefined return userdefined['_turtle_strategy'](self, arg) except ZeroDivisionError, e: self._set_label('Python zero-divide error: %s' % (str(e))) except ValueError, e: self._set_label('Python value error: %s' % (str(e)))
class Slide(Stator): """ Create a sprite for a slide """ def __init__(self, sprites, path, name, x, y, w, h, svg_engine=None, function=None): if svg_engine is None: self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) else: self.spr = Sprite(sprites, x, y, svg_str_to_pixbuf(svg_engine().svg)) self.tab_dx = [0, SWIDTH - TABWIDTH] self.tab_dy = [2 * SHEIGHT, 2 * SHEIGHT] self.tabs = [] self.tabs.append( Tab(sprites, path, 'tab', x + self.tab_dx[0], y + self.tab_dy[0], TABWIDTH, SHEIGHT)) self.tabs.append( Tab(sprites, path, 'tab', x + self.tab_dx[1], y + self.tab_dy[1], TABWIDTH, SHEIGHT)) self.calculate = function self.name = name def add_textview(self, textview, i=0): self.tabs[i].textview = textview self.tabs[i].textbuffer = textview.get_buffer() def set_fixed(self, fixed): for tab in self.tabs: tab.fixed = fixed def match(self, sprite): if sprite == self.spr or sprite == self.tabs[0].spr or \ sprite == self.tabs[1].spr: return True return False def draw(self, layer=1000): self.spr.set_layer(layer) self.spr.draw() for tab in self.tabs: tab.draw() def move(self, dx, dy): self.spr.move((dx, dy)) for i, tab in enumerate(self.tabs): tab.move(dx + self.tab_dx[i], dy + self.tab_dy[i]) def move_relative(self, dx, dy): self.spr.move_relative((dx, dy)) for i, tab in enumerate(self.tabs): tab.move_relative(dx, dy) def hide(self): self.spr.hide() for tab in self.tabs: tab.hide() def label(self, label, i=0): self.tabs[i].label(label)
class Spirolaterals: def __init__(self, canvas, colors, parent, score=0, delay=500, pattern=1, last=None): self._canvas = canvas self._colors = colors self._parent = parent self.delay = delay self.score = score self.pattern = pattern self.last_pattern = last self._running = False self._turtle_canvas = None self._user_numbers = [1, 1, 1, 3, 2] self._active_index = 0 self._sprites = Sprites(self._canvas) self._sprites.set_delay(True) size = max(Gdk.Screen.width(), Gdk.Screen.height()) cr = self._canvas.get_property('window').cairo_create() self._turtle_canvas = cr.get_target().create_similar( cairo.CONTENT_COLOR, size, size) self._canvas.connect('draw', self.__draw_cb) self._cr = cairo.Context(self._turtle_canvas) self._cr.set_line_cap(1) # Set the line cap to be round self._sprites.set_cairo_context(self._cr) self._canvas.set_can_focus(True) self._canvas.grab_focus() self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect('button-press-event', self._button_press_cb) self._canvas.connect('key_press_event', self._keypress_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE if self._width < self._height: self.i = 1 else: self.i = 0 self._calculate_scale_and_offset() self._numbers = [] self._glownumbers = [] self._create_number_sprites() self._create_turtle_sprites() self._create_results_sprites() self._set_color(colors[0]) self._set_pen_size(4) self.reset_level() def _calculate_scale_and_offset(self): self.offset = 0 if self.i == 0: self.scale = self._height / (900. - style.GRID_CELL_SIZE) * 1.25 self.offset = ( self._width - (self.sx(X1[self.i] + X2[self.i]) + self.ss(BS[self.i]))) / 2. else: self.scale = self._width / 900. self.offset = (self._width - (self.sx(X1[self.i]) + self.ss(BS[self.i]))) / 2. def reset_level(self): self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE if self._width < self._height: self.i = 1 else: self.i = 0 self._calculate_scale_and_offset() self._show_background_graphics() self._show_user_numbers() self._get_goal() self._draw_goal() self._reset_sprites() if self.score > 0: self._parent.update_score(int(self.score)) def _reset_sprites(self): x = self.sx(TX[self.i] - TS[self.i] / 2) y = self.sy(TY[self.i]) self._target_turtle.move((x, y)) x = self.sx(UX[self.i] - US[self.i] / 2) y = self.sy(UY[self.i]) self._user_turtles[0].move((x, y)) for i in range(5): for j in range(5): if self.i == 0: x = self.sx( NX[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) y = self.sy(NY[self.i]) else: x = self.sx(NX[self.i]) y = self.sy( NY[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) self._numbers[i][j].move((x, y)) self._glownumbers[i][j].move((x, y)) x = 0 y = self.sy(GY[self.i]) self._success.move((x, y)) self._success.hide() self._failure.move((x, y)) self._failure.hide() self._splot.hide() if self.last_pattern == self.pattern: self._parent.cyan.set_sensitive(True) def _keypress_cb(self, area, event): ''' Keypress: moving the slides with the arrow keys ''' k = Gdk.keyval_name(event.keyval) if k in ['1', '2', '3', '4', '5']: self.do_stop() i = self._active_index j = int(k) - 1 self._numbers[i][self._user_numbers[i] - 1].set_layer(HIDDEN_LAYER) self._numbers[i][j].set_layer(NUMBER_LAYER) self._user_numbers[i] = j + 1 self.inval(self._numbers[i][j].rect) elif k in ['KP_Up', 'j', 'Up']: self.do_stop() i = self._active_index j = self._user_numbers[i] if j < 5: j += 1 self._numbers[i][self._user_numbers[i] - 1].set_layer(HIDDEN_LAYER) self._numbers[i][j - 1].set_layer(NUMBER_LAYER) self._user_numbers[i] = j self.inval(self._numbers[i][j].rect) elif k in ['KP_Down', 'k', 'Down']: self.do_stop() i = self._active_index j = self._user_numbers[i] if j > 0: j -= 1 self._numbers[i][self._user_numbers[i] - 1].set_layer(HIDDEN_LAYER) self._numbers[i][j - 1].set_layer(NUMBER_LAYER) self._user_numbers[i] = j self.inval(self._numbers[i][j].rect) elif k in ['KP_Left', 'h', 'Left']: self.do_stop() self._active_index -= 1 self._active_index %= 5 elif k in ['KP_Right', 'l', 'Right']: self.do_stop() self._active_index += 1 self._active_index %= 5 elif k in ['Return', 'KP_Page_Up', 'KP_End']: self.do_run() elif k in ['space', 'Esc', 'KP_Page_Down', 'KP_Home']: self.do_stop() else: logging.debug(k) self._canvas.grab_focus() def _button_press_cb(self, win, event): ''' Callback to handle the button presses ''' win.grab_focus() x, y = map(int, event.get_coords()) self.press = self._sprites.find_sprite((x, y)) if self.press is not None and self.press.type == 'number': self.do_stop() i = int(self.press.name.split(',')[0]) self._active_index = i j = int(self.press.name.split(',')[1]) j1 = (j + 1) % 5 self._numbers[i][j1].set_layer(NUMBER_LAYER) self._numbers[i][j].set_layer(HIDDEN_LAYER) self._user_numbers[i] = j1 + 1 self.inval(self._numbers[i][j].rect) def _create_results_sprites(self): x = 0 y = self.sy(GY[self.i]) self._success = Sprite(self._sprites, x, y, self._parent.good_job_pixbuf()) self._success.hide() self._failure = Sprite(self._sprites, x, y, self._parent.try_again_pixbuf()) self._failure.hide() def _create_turtle_sprites(self): x = self.sx(TX[self.i] - TS[self.i] / 2) y = self.sy(TY[self.i]) pixbuf = self._parent.turtle_pixbuf() self._target_turtle = Sprite(self._sprites, x, y, pixbuf) self._user_turtles = [] x = self.sx(UX[self.i] - US[self.i] / 2) y = self.sy(UY[self.i]) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) pixbuf = pixbuf.rotate_simple(270) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) pixbuf = pixbuf.rotate_simple(270) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) pixbuf = pixbuf.rotate_simple(270) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) self._show_turtle(0) self._splot = Sprite(self._sprites, 0, 0, self._parent.splot_pixbuf()) self._splot.hide() def _show_splot(self, x, y, dd, h): for i in range(4): self._user_turtles[i].hide() if h == 0: self._splot.move((x - int(dd / 2), y)) elif h == 1: self._splot.move((x - dd, y - int(dd / 2))) elif h == 2: self._splot.move((x - int(dd / 2), y - dd)) elif h == 3: self._splot.move((x, y - int(dd / 2))) self._splot.set_layer(SUCCESS_LAYER) self._failure.set_layer(SUCCESS_LAYER) def _show_turtle(self, t): for i in range(4): if i == t: self._user_turtles[i].set_layer(TURTLE_LAYER) else: self._user_turtles[i].hide() def _reset_user_turtle(self): x = self.sx(UX[self.i] - US[self.i] / 2) y = self.sy(UY[self.i]) self._user_turtles[0].move((x, y)) self._show_turtle(0) def _create_number_sprites(self): for i in range(5): self._numbers.append([]) self._glownumbers.append([]) for j in range(5): if self.i == 0: x = self.sx( NX[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) y = self.sy(NY[self.i]) else: x = self.sx(NX[self.i]) y = self.sy( NY[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) number = Sprite( self._sprites, x, y, self._parent.number_pixbuf(self.ss(NS[self.i]), j + 1, self._parent.sugarcolors[1])) number.type = 'number' number.name = '%d,%d' % (i, j) self._numbers[i].append(number) number = Sprite( self._sprites, x, y, self._parent.number_pixbuf(self.ss(NS[self.i]), j + 1, '#FFFFFF')) number.type = 'number' number.name = '%d,%d' % (i, j) self._glownumbers[i].append(number) def _show_user_numbers(self): # Hide the numbers for i in range(5): for j in range(5): self._numbers[i][j].set_layer(HIDDEN_LAYER) self._glownumbers[i][j].set_layer(HIDDEN_LAYER) # Show user numbers self._numbers[0][self._user_numbers[0] - 1].set_layer(NUMBER_LAYER) self._numbers[1][self._user_numbers[1] - 1].set_layer(NUMBER_LAYER) self._numbers[2][self._user_numbers[2] - 1].set_layer(NUMBER_LAYER) self._numbers[3][self._user_numbers[3] - 1].set_layer(NUMBER_LAYER) self._numbers[4][self._user_numbers[4] - 1].set_layer(NUMBER_LAYER) def _show_background_graphics(self): self._draw_pixbuf(self._parent.background_pixbuf(), 0, 0, self._width, self._height) self._draw_pixbuf(self._parent.box_pixbuf(self.ss(BS[self.i])), self.sx(X1[self.i]), self.sy(Y1[self.i]), self.ss(BS[self.i]), self.ss(BS[self.i])) self._draw_pixbuf(self._parent.box_pixbuf(self.ss(BS[self.i])), self.sx(X2[self.i]), self.sy(Y2[self.i]), self.ss(BS[self.i]), self.ss(BS[self.i])) self._draw_text(self.pattern, self.sx(X1[self.i]), self.sy(Y1[self.i]), self.ss(LS[self.i])) def _set_pen_size(self, ps): self._cr.set_line_width(ps) def _set_color(self, color): r = color[0] / 255. g = color[1] / 255. b = color[2] / 255. self._cr.set_source_rgb(r, g, b) def _draw_line(self, x1, y1, x2, y2): self._cr.move_to(x1, y1) self._cr.line_to(x2, y2) self._cr.stroke() def ss(self, f): # scale size function return int(f * self.scale) def sx(self, f): # scale x function return int(f * self.scale + self.offset) def sy(self, f): # scale y function return int(f * self.scale) def _draw_pixbuf(self, pixbuf, x, y, w, h): self._cr.save() self._cr.translate(x + w / 2., y + h / 2.) self._cr.translate(-x - w / 2., -y - h / 2.) Gdk.cairo_set_source_pixbuf(self._cr, pixbuf, x, y) self._cr.rectangle(x, y, w, h) self._cr.fill() self._cr.restore() def _draw_text(self, label, x, y, size): pl = PangoCairo.create_layout(self._cr) fd = Pango.FontDescription('Sans') fd.set_size(int(size) * Pango.SCALE) pl.set_font_description(fd) if type(label) == str or type(label) == unicode: pl.set_text(label.replace('\0', ' '), -1) elif type(label) == float or type(label) == int: pl.set_text(str(label), -1) else: pl.set_text(str(label), -1) self._cr.save() self._cr.translate(x, y) self._cr.set_source_rgb(1, 1, 1) PangoCairo.update_layout(self._cr, pl) PangoCairo.show_layout(self._cr, pl) self._cr.restore() def inval(self, r): self._canvas.queue_draw_area(r[0], r[1], r[2], r[3]) def inval_all(self): self._canvas.queue_draw_area(0, 0, self._width, self._height) def __draw_cb(self, canvas, cr): cr.set_source_surface(self._turtle_canvas) cr.paint() self._sprites.redraw_sprites(cr=cr) def do_stop(self): self._parent.green.set_sensitive(True) self._running = False def do_run(self): self._show_background_graphics() # TODO: Add turtle graphics self._success.hide() self._failure.hide() self._splot.hide() self._get_goal() self._draw_goal() self.inval_all() self._running = True self.loop = 0 self._active_index = 0 self.step = 0 self._set_pen_size(4) self._set_color(self._colors[0]) x1 = self.sx(UX[self.i]) y1 = self.sy(UY[self.i]) dd = self.ss(US[self.i]) self._numbers[0][self._user_numbers[0] - 1].set_layer(HIDDEN_LAYER) self._glownumbers[0][self._user_numbers[0] - 1].set_layer(NUMBER_LAYER) self._user_turtles[0].move((int(x1 - dd / 2), y1)) self._show_turtle(0) if self._running: GObject.timeout_add(self.delay, self._do_step, x1, y1, dd, 0) def _do_step(self, x1, y1, dd, h): if not self._running: return if self.loop > 3: return if h == 0: # up x2 = x1 y2 = y1 - dd self._user_turtles[h].move((int(x2 - dd / 2), int(y2 - dd))) elif h == 1: # right x2 = x1 + dd y2 = y1 self._user_turtles[h].move((int(x2), int(y2 - dd / 2))) elif h == 2: # down x2 = x1 y2 = y1 + dd self._user_turtles[h].move((int(x2 - dd / 2), int(y2))) elif h == 3: # left x2 = x1 - dd y2 = y1 self._user_turtles[h].move((int(x2 - dd), int(y2 - dd / 2))) self._show_turtle(h) if x2 < self.sx(X2[self.i]) or \ x2 > self.sx(X2[self.i] + BS[self.i]) or \ y2 < self.sy(Y2[self.i]) or \ y2 > self.sy(Y2[self.i] + BS[self.i]): self.do_stop() self._show_splot(x2, y2, dd, h) self._draw_line(x1, y1, x2, y2) self.inval_all() self.step += 1 i = self._active_index if self.step == self._user_numbers[i]: number = self._user_numbers[i] - 1 self._numbers[i][number].set_layer(NUMBER_LAYER) self._glownumbers[i][number].set_layer(HIDDEN_LAYER) h += 1 h %= 4 self.step = 0 self._active_index += 1 if self._active_index == 5: self.loop += 1 self._active_index = 0 else: i = self._active_index number = self._user_numbers[i] - 1 self._numbers[i][number].set_layer(HIDDEN_LAYER) self._glownumbers[i][number].set_layer(NUMBER_LAYER) if self.loop < 4 and self._running: GObject.timeout_add(self.delay, self._do_step, x2, y2, dd, h) elif self.loop == 4: # Test to see if we win self._running = False self._parent.green.set_sensitive(True) self._reset_user_turtle() self._show_user_numbers() self._test_level() def _test_level(self): success = True for i in range(5): if self._user_numbers[i] != self._goal[i]: success = False break if success: self._do_success() else: self._do_fail() def _do_success(self): self._success.set_layer(SUCCESS_LAYER) self._parent.cyan.set_sensitive(True) if self.last_pattern != self.pattern: self.score += 6 self.last_pattern = self.pattern self._parent.update_score(int(self.score)) def _do_fail(self): self._failure.set_layer(SUCCESS_LAYER) self._parent.cyan.set_sensitive(False) def do_slider(self, value): self.delay = int(value) def do_button(self, bu): self._success.hide() self._failure.hide() if bu == 'cyan': # Next level self.do_stop() self._splot.hide() self.pattern += 1 if self.pattern == 123: self.pattern = 1 self._get_goal() self._show_background_graphics() self._draw_goal() self._reset_user_turtle() self.inval_all() self._parent.cyan.set_sensitive(False) elif bu == 'green': # Run level self._parent.green.set_sensitive(False) self.do_run() elif bu == 'red': # Stop level self.do_stop() def _draw_goal(self): # draws the left hand pattern x1 = self.sx(TX[self.i]) y1 = self.sy(TY[self.i]) dd = self.ss(TS[self.i]) dx = 0 dy = -dd for i in range(4): for j in self._goal: for k in range(j): x2 = x1 + dx y2 = y1 + dy self._set_pen_size(4) self._set_color(self._colors[0]) self._draw_line(x1, y1, x2, y2) x1 = x2 y1 = y2 if dy == -dd: dx = dd dy = 0 elif dx == dd: dx = 0 dy = dd elif dy == dd: dx = -dd dy = 0 else: dx = 0 dy = -dd def _get_goal(self): fname = os.path.join('data', 'patterns.dat') try: f = open(fname, 'r') for n in range(0, self.pattern): s = f.readline() s = s[0:5] except: s = 11132 self.pattern = 1 f.close l = [int(c) for c in str(s)] self._goal = l
class Turtle: def __init__(self, turtles, key, turtle_colors=None): """ The turtle is not a block, just a sprite with an orientation """ self.x = 0 self.y = 0 self.hidden = False self.shapes = [] self.custom_shapes = False self.type = 'turtle' self.name = key self.heading = 0 self.pen_shade = 50 self.pen_color = 0 self.pen_gray = 100 self.pen_size = 5 self.pen_state = True self.label_block = None self._prep_shapes(key, turtles, turtle_colors) # Choose a random angle from which to attach the turtle label if turtles.sprite_list is not None: self.spr = Sprite(turtles.sprite_list, 0, 0, self.shapes[0]) angle = uniform(0, pi * 4 / 3.0) # 240 degrees w = self.shapes[0].get_width() r = w * 0.67 # Restrict angle the the sides 30-150; 210-330 if angle > pi * 2 / 3.0: angle += pi / 2.0 # + 90 self.label_xy = [int(r * sin(angle)), int(r * cos(angle) + w / 2.0)] else: angle += pi / 6.0 # + 30 self.label_xy = [int(r * sin(angle) + w / 2.0), int(r * cos(angle) + w / 2.0)] else: self.spr = None turtles.add_to_dict(key, self) def _prep_shapes(self, name, turtles=None, turtle_colors=None): # If the turtle name is an int, we'll use a palette color as the # turtle color try: int_key = int(name) use_color_table = True except ValueError: use_color_table = False if turtle_colors is not None: self.colors = turtle_colors[:] self.shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = ['#%06x' % (COLOR_TABLE[fill]), '#%06x' % (COLOR_TABLE[stroke])] self.shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: self.colors = DEFAULT_TURTLE_COLORS self.shapes = turtles.get_pixbufs() def set_turtle_colors(self, turtle_colors): ''' reset the colors of a preloaded turtle ''' if turtle_colors is not None: self.colors = turtle_colors[:] self.shapes = generate_turtle_pixbufs(self.colors) self.set_heading(self.heading) def set_shapes(self, shapes, i=0): """ Reskin the turtle """ n = len(shapes) if n == 1 and i > 0: # set shape[i] if i < len(self.shapes): self.shapes[i] = shapes[0] elif n == SHAPES: # all shapes have been precomputed self.shapes = shapes[:] else: # rotate shapes if n != 1: debug_output("%d images passed to set_shapes: ignoring" % (n), self.tw.running_sugar) if self.heading == 0: # rotate the shapes images = [] w, h = shapes[0].get_width(), shapes[0].get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(SHAPES): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context = gtk.gdk.CairoContext(context) context.translate(nw / 2., nh / 2.) context.rotate(i * 10 * pi / 180.) context.translate(-nw / 2., -nh / 2.) context.set_source_pixbuf(shapes[0], (nw - w) / 2., (nh - h) / 2.) context.rectangle(0, 0, nw, nh) context.fill() images.append(surface) self.shapes = images[:] else: # associate shape with image at current heading j = int(self.heading + 5) % 360 / (360 / SHAPES) self.shapes[j] = shapes[0] self.custom_shapes = True self.show() def reset_shapes(self): """ Reset the shapes to the standard turtle """ if self.custom_shapes: self.shapes = generate_turtle_pixbufs(self.colors) self.custom_shapes = False def set_heading(self, heading): """ Set the turtle heading (one shape per 360/SHAPES degrees) """ self.heading = heading i = (int(self.heading + 5) % 360) / (360 / SHAPES) if not self.hidden and self.spr is not None: try: self.spr.set_shape(self.shapes[i]) except IndexError: self.spr.set_shape(self.shapes[0]) def set_color(self, color): """ Set the pen color for this turtle. """ self.pen_color = color def set_gray(self, gray): """ Set the pen gray level for this turtle. """ self.pen_gray = gray def set_shade(self, shade): """ Set the pen shade for this turtle. """ self.pen_shade = shade def set_pen_size(self, pen_size): """ Set the pen size for this turtle. """ self.pen_size = pen_size def set_pen_state(self, pen_state): """ Set the pen state (down==True) for this turtle. """ self.pen_state = pen_state def hide(self): """ Hide the turtle. """ if self.spr is not None: self.spr.hide() if self.label_block is not None: self.label_block.spr.hide() self.hidden = True def show(self): """ Show the turtle. """ if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) self.hidden = False self.move((self.x, self.y)) self.set_heading(self.heading) if self.label_block is not None: self.label_block.spr.move((self.x + self.label_xy[0], self.y + self.label_xy[1])) self.label_block.spr.set_layer(TURTLE_LAYER + 1) def move(self, pos): """ Move the turtle. """ self.x, self.y = int(pos[0]), int(pos[1]) if not self.hidden and self.spr is not None: self.spr.move(pos) if self.label_block is not None: self.label_block.spr.move((pos[0] + self.label_xy[0], pos[1] + self.label_xy[1])) return(self.x, self.y) def get_name(self): ''' return turtle name (key) ''' return self.name def get_xy(self): """ Return the turtle's x, y coordinates. """ return(self.x, self.y) def get_heading(self): """ Return the turtle's heading. """ return(self.heading) def get_color(self): """ Return the turtle's color. """ return(self.pen_color) def get_gray(self): """ Return the turtle's gray level. """ return(self.pen_gray) def get_shade(self): """ Return the turtle's shade. """ return(self.pen_shade) def get_pen_size(self): """ Return the turtle's pen size. """ return(self.pen_size) def get_pen_state(self): """ Return the turtle's pen state. """ return(self.pen_state)
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = colors self._canvas = canvas parent.show_all() self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) self._scale = self._height / (14.0 * DOT_SIZE * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._turtle_offset = 0 self._space = int(self._dot_size / 5.) self._orientation = 0 self.level = 0 self.custom_strategy = None self.strategies = [ BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY, self.custom_strategy ] self.strategy = self.strategies[self.level] self._timeout_id = None # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] for y in range(THIRTEEN): for x in range(THIRTEEN): xoffset = int((self._width - THIRTEEN * (self._dot_size + \ self._space) - self._space) / 2.) if y % 2 == 1: xoffset += int((self._dot_size + self._space) / 2.) if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot('#B0B0B0'))) else: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[FILL]))) self._dots[-1].type = False # not set # Put a turtle at the center of the screen... self._turtle_images = [] self._rotate_turtle(self._new_turtle()) self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0]) self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) # ...and initialize. self._all_clear() def _move_turtle(self, pos): ''' Move turtle and add its offset ''' self._turtle.move(pos) self._turtle.move_relative( (-self._turtle_offset, -self._turtle_offset)) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' # Clear dots for dot in self._dots: if dot.type: dot.type = False dot.set_shape(self._new_dot(self._colors[FILL])) dot.set_label('') # Recenter the turtle self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) self._turtle.set_shape(self._turtle_images[0]) self._set_label('') if self._timeout_id is not None: GObject.source_remove(self._timeout_id) def new_game(self, saved_state=None): ''' Start a new game. ''' self._all_clear() # Fill in a few dots to start for i in range(15): n = int(uniform(0, THIRTEEN * THIRTEEN)) if self._dots[n].type is not None: self._dots[n].type = True self._dots[n].set_shape(self._new_dot(self._colors[STROKE])) # Calculate the distances to the edge self._initialize_weights() self.strategy = self.strategies[self.level] def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._activity.status.set_label(string) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) spr = self._sprites.find_sprite((x, y), inverse=True) if spr == None: return if spr.type is not None and not spr.type: spr.type = True spr.set_shape(self._new_dot(self._colors[STROKE])) self._weights[self._dots.index(spr)] = 1000 self._test_game_over(self._move_the_turtle()) return True def _find_the_turtle(self): turtle_pos = self._turtle.get_xy() turtle_dot = None for dot in self._dots: pos = dot.get_xy() # Turtle is offset if pos[0] == turtle_pos[0] + self._turtle_offset and \ pos[1] == turtle_pos[1] + self._turtle_offset: turtle_dot = self._dots.index(dot) break if turtle_dot is None: _logger.debug('Cannot find the turtle...') return None return turtle_dot def _move_the_turtle(self): ''' Move the turtle after each click ''' self._turtle_dot = self._find_the_turtle() if self._turtle_dot is None: return # Given the col and row of the turtle, do something new_dot = self._grid_to_dot( self._my_strategy_import(self.strategy, self._dot_to_grid(self._turtle_dot))) self._move_turtle(self._dots[new_dot].get_xy()) # And set the orientation self._turtle.set_shape(self._turtle_images[self._orientation]) return new_dot def _test_game_over(self, new_dot): ''' Check to see if game is over ''' if new_dot is None: return if self._dots[new_dot].type is None: # Game-over feedback self._once_around = False self._happy_turtle_dance() return True c = int(self._turtle_dot / THIRTEEN) % 2 if self._dots[ new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \ self._dots[ new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \ self._dots[ new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \ self._dots[ new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \ self._dots[ new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \ self._dots[ new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type: # Game-over feedback for dot in self._dots: dot.set_label(':)') return True return False def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * THIRTEEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % THIRTEEN, int(dot / THIRTEEN)] def _happy_turtle_dance(self): ''' Turtle dances along the edge ''' i = self._find_the_turtle() if i == 0: if self._once_around: return else: self._once_around = True _logger.debug(i) x, y = self._dot_to_grid(i) if y == 0: x += 1 if x == 0: y -= 1 if x == THIRTEEN - 1: y += 1 if y == THIRTEEN - 1: x -= 1 i = self._grid_to_dot((x, y)) self._dots[i].set_label(':)') self._move_turtle(self._dots[i].get_xy()) self._orientation += 1 self._orientation %= 6 self._turtle.set_shape(self._turtle_images[self._orientation]) self._timeout_id = GObject.timeout_add(250, self._happy_turtle_dance) def _ordered_weights(self, pos): ''' Returns the list of surrounding points sorted by their distance to the edge ''' dots = self._surrounding_dots(pos) dots_and_weights = [] for dot in dots: dots_and_weights.append((dot, self._weights[dot])) sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1]) for i in range(6): dots[i] = sorted_dots[i][0] return dots def _daylight_ahead(self, pos): ''' Returns true if there is a straight path to the edge from the current position/orientation ''' dots = self._surrounding_dots(pos) while True: dot_type = self._dots[dots[self._orientation]].type if dot_type is None: return True elif dot_type: return False else: # keep looking pos = self._dot_to_grid(dots[self._orientation]) dots = self._surrounding_dots(pos) def _surrounding_dots(self, pos): ''' Returns dots surrounding a position in the grid ''' dots = [] evenodd = pos[1] % 2 for i in range(6): col = pos[0] + CIRCLE[evenodd][i][0] row = pos[1] + CIRCLE[evenodd][i][1] dots.append(self._grid_to_dot((col, row))) return dots def _initialize_weights(self): ''' How many steps to an edge? ''' self._weights = [] for d, dot in enumerate(self._dots): if dot.type is None: self._weights.append(0) elif dot.type: self._weights.append(1000) else: pos = self._dot_to_grid(d) pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1]) self._weights.append( min(min(pos[0], pos2[0]), min(pos[1], pos2[1]))) def _my_strategy_import(self, f, arg): ''' Run Python code passed as argument ''' userdefined = {} try: exec f in globals(), userdefined return userdefined['_turtle_strategy'](self, arg) except ZeroDivisionError, e: self._set_label('Python zero-divide error: %s' % (str(e))) except ValueError, e: self._set_label('Python value error: %s' % (str(e)))
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = colors self._canvas = canvas parent.show_all() self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) self._scale = self._height / (14.0 * DOT_SIZE * 1.2) self._scale_gameover = self._height / (4.0 * DOT_SIZE_GAMEOVER * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._dot_size_gameover = int(DOT_SIZE_GAMEOVER * self._scale) self._turtle_offset = 0 self._space = int(self._dot_size / 5.) self._space_gameover = int(self._dot_size_gameover / 5.) self._orientation = 0 self.level = 0 self.custom_strategy = None self.strategies = [ BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY, self.custom_strategy ] self.strategy = self.strategies[self.level] self._timeout_id = None self.best_time = self.load_best_time() # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] self._gameover = [] self._your_time = [] self._best_time = [] self._win_lose = [] for y in range(THIRTEEN): for x in range(THIRTEEN): offset_x = int((self._width - THIRTEEN * (self._dot_size + \ self._space) - self._space) / 2.) if y % 2 == 1: offset_x += int((self._dot_size + self._space) / 2.) if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1: self._dots.append( Sprite(self._sprites, offset_x + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot('#B0B0B0', self._dot_size))) else: self._dots.append( Sprite( self._sprites, offset_x + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[FILL], self._dot_size))) self._dots[-1].type = False # not set # Put a turtle at the center of the screen... self._turtle_images = [] self._rotate_turtle(self._new_turtle()) self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0]) self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) # ...and initialize. self._all_clear() def _move_turtle(self, pos): ''' Move turtle and add its offset ''' self._turtle.move(pos) self._turtle.move_relative( (-self._turtle_offset, -self._turtle_offset)) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' # Clear dots for gameover_shape in self._gameover: gameover_shape.hide() for win_lose_shape in self._win_lose: win_lose_shape.hide() for your_time_shape in self._your_time: your_time_shape.hide() for highscore_shape in self._best_time: highscore_shape.hide() for dot in self._dots: if dot.type: dot.type = False dot.set_shape(self._new_dot(self._colors[FILL], self._dot_size)) dot.set_label('') dot.set_layer(100) self._turtle.set_layer(100) # Recenter the turtle self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) self._turtle.set_shape(self._turtle_images[0]) self._set_label('') if self._timeout_id is not None: GLib.source_remove(self._timeout_id) self._timeout_id = None def new_game(self, saved_state=None): ''' Start a new game. ''' self.gameover_flag = False self.game_lost = False self._all_clear() # Fill in a few dots to start for i in range(15): n = int(uniform(0, THIRTEEN * THIRTEEN)) if self._dots[n].type is not None: self._dots[n].type = True self._dots[n].set_shape( self._new_dot(self._colors[STROKE], self._dot_size)) # Calculate the distances to the edge self._initialize_weights() self.game_start_time = time.time() self.strategy = self.strategies[self.level] self._timeout_id = None def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._activity.status.set_label(string) def _button_press_cb(self, win, event): win.grab_focus() x, y = list(map(int, event.get_coords())) spr = self._sprites.find_sprite((x, y), inverse=True) if spr == None: return if spr.type is not None and not spr.type: spr.type = True spr.set_shape(self._new_dot(self._colors[STROKE], self._dot_size)) self._weights[self._dots.index(spr)] = 1000 self._test_game_over(self._move_the_turtle()) return True def _find_the_turtle(self): turtle_pos = self._turtle.get_xy() turtle_dot = None for dot in self._dots: pos = dot.get_xy() # Turtle is offset if pos[0] == turtle_pos[0] + self._turtle_offset and \ pos[1] == turtle_pos[1] + self._turtle_offset: turtle_dot = self._dots.index(dot) break if turtle_dot is None: _logger.debug('Cannot find the turtle...') return None return turtle_dot def _move_the_turtle(self): ''' Move the turtle after each click ''' self._turtle_dot = self._find_the_turtle() if self._turtle_dot is None: return # Given the col and row of the turtle, do something new_dot = self._grid_to_dot( self._my_strategy_import(self.strategy, self._dot_to_grid(self._turtle_dot))) self._move_turtle(self._dots[new_dot].get_xy()) # And set the orientation self._turtle.set_shape(self._turtle_images[self._orientation]) return new_dot def _test_game_over(self, new_dot): ''' Check to see if game is over ''' if new_dot is None: return if self._dots[new_dot].type is None: # Game-over feedback self._once_around = False self.game_stop_time = time.time() self.gameover_flag = True self._happy_turtle_dance() self._timeout_id = GLib.timeout_add(10000, self._game_over) return True c = int(self._turtle_dot / THIRTEEN) % 2 if self._dots[ new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \ self._dots[ new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \ self._dots[ new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \ self._dots[ new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \ self._dots[ new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \ self._dots[ new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type: # Game-over feedback for dot in self._dots: dot.set_label(':)') self.game_stop_time = time.time() self.gameover_flag = True self._timeout_id = GLib.timeout_add(4000, self._game_over) return True return False def _game_over(self): best_seconds = self.best_time % 60 best_minutes = self.best_time // 60 self.elapsed_time = int(self.game_stop_time - self.game_start_time) second = self.elapsed_time % 60 minute = self.elapsed_time // 60 for dot in self._dots: dot.hide() self._turtle.hide() offset_y = int(self._space_gameover / 4.) offset_x = int((self._width - 6 * self._dot_size_gameover - 5 * self._space_gameover) / 2.) y = 1.5 for x in range(2, 6): self._gameover.append( Sprite( self._sprites, offset_x + (x - 0.50) * self._dot_size_gameover, y * (self._dot_size + self._space) + offset_y, self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._gameover[-1].type = -1 # No image self._gameover[-1].set_label_attributes(72) text = ["☻", " Game ", " Over ", "☻"] self.rings(len(text), text, self._gameover) y = 4.5 for x in range(2, 6): self._win_lose.append( Sprite( self._sprites, offset_x + (x - 0.50) * self._dot_size_gameover, y * (self._dot_size + self._space) + offset_y, self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._win_lose[-1].type = -1 # No image self._win_lose[-1].set_label_attributes(72) text_win_best_time = ["☻", " YOU ", " WON ", "☻"] text_lose = ["☹", " YOU ", " LOST ", "☹"] text_win = ["☻", " GOOD ", " JOB ", "☻"] if self.game_lost: self.rings(len(text_lose), text_lose, self._win_lose) elif self.elapsed_time <= self.best_time: self.rings(len(text_win_best_time), text_win_best_time, self._win_lose) else: self.rings(len(text_win), text_win, self._win_lose) y = 7.5 for x in range(2, 5): self._your_time.append( Sprite( self._sprites, offset_x + x * self._dot_size_gameover, y * (self._dot_size + self._space), self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._your_time[-1].type = -1 # No image self._your_time[-1].set_label_attributes(72) text = [ " your ", " time: ", (' {:02d}:{:02d} '.format(minute, second)) ] self.rings(len(text), text, self._your_time) y = 10.5 for x in range(2, 5): self._best_time.append( Sprite( self._sprites, offset_x + x * self._dot_size_gameover, y * (self._dot_size + self._space), self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._best_time[-1].type = -1 # No image self._best_time[-1].set_label_attributes(72) if self.elapsed_time <= self.best_time and not self.game_lost: self.best_time = self.elapsed_time best_seconds = second best_minutes = minute text = [ " best ", " time: ", (' {:02d}:{:02d} '.format(best_minutes, best_seconds)) ] self.rings(len(text), text, self._best_time) self.save_best_time() self._timeout_id = GLib.timeout_add(7000, self.new_game) def rings(self, num, text, shape): i = 0 for x in range(num): shape[x].type = -1 shape[x].set_shape( self._new_dot(self._colors[FILL], self._dot_size_gameover)) shape[x].set_label(text[i]) shape[x].set_layer(100) i += 1 def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * THIRTEEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % THIRTEEN, int(dot / THIRTEEN)] def _happy_turtle_dance(self): ''' Turtle dances along the edge ''' self.game_lost = True i = self._find_the_turtle() if i == 0: if self._once_around: return else: self._once_around = True _logger.debug(i) x, y = self._dot_to_grid(i) if y == 0: x += 1 if x == 0: y -= 1 if x == THIRTEEN - 1: y += 1 if y == THIRTEEN - 1: x -= 1 i = self._grid_to_dot((x, y)) self._dots[i].set_label(':)') self._move_turtle(self._dots[i].get_xy()) self._orientation += 1 self._orientation %= 6 self._turtle.set_shape(self._turtle_images[self._orientation]) self._timeout_id = GLib.timeout_add(250, self._happy_turtle_dance) def _ordered_weights(self, pos): ''' Returns the list of surrounding points sorted by their distance to the edge ''' dots = self._surrounding_dots(pos) dots_and_weights = [] for dot in dots: dots_and_weights.append((dot, self._weights[dot])) sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1]) for i in range(6): dots[i] = sorted_dots[i][0] return dots def _daylight_ahead(self, pos): ''' Returns true if there is a straight path to the edge from the current position/orientation ''' dots = self._surrounding_dots(pos) while True: dot_type = self._dots[dots[self._orientation]].type if dot_type is None: return True elif dot_type: return False else: # keep looking pos = self._dot_to_grid(dots[self._orientation]) dots = self._surrounding_dots(pos) def _surrounding_dots(self, pos): ''' Returns dots surrounding a position in the grid ''' dots = [] evenodd = pos[1] % 2 for i in range(6): col = pos[0] + CIRCLE[evenodd][i][0] row = pos[1] + CIRCLE[evenodd][i][1] dots.append(self._grid_to_dot((col, row))) return dots def _initialize_weights(self): ''' How many steps to an edge? ''' self._weights = [] for d, dot in enumerate(self._dots): if dot.type is None: self._weights.append(0) elif dot.type: self._weights.append(1000) else: pos = self._dot_to_grid(d) pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1]) self._weights.append( min(min(pos[0], pos2[0]), min(pos[1], pos2[1]))) def _my_strategy_import(self, f, arg): ''' Run Python code passed as argument ''' userdefined = {} try: exec(f, globals(), userdefined) return userdefined['_turtle_strategy'](self, arg) except ZeroDivisionError as e: self._set_label('Python zero-divide error: {}'.format(e)) except ValueError as e: self._set_label('Python value error: {}'.format(e)) except SyntaxError as e: self._set_label('Python syntax error: {}'.format(e)) except NameError as e: self._set_label('Python name error: {}'.format(e)) except OverflowError as e: self._set_label('Python overflow error: {}'.format(e)) except TypeError as e: self._set_label('Python type error: {}'.format(e)) except: self._set_label('Python error') traceback.print_exc() return None def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def do_expose_event(self, event): ''' Handle the expose-event by drawing ''' # Restrict Cairo to the exposed area cr = self._canvas.window.cairo_create() cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self._sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def _new_dot(self, color, dot_size): ''' generate a dot of a color color ''' self._stroke = color self._fill = color self._svg_width = dot_size self._svg_height = dot_size return svg_str_to_pixbuf( self._header() + \ self._circle(dot_size / 2., dot_size / 2., dot_size / 2.) + \ self._footer()) def _new_turtle(self): ''' generate a turtle ''' self._svg_width = self._dot_size * 2 self._svg_height = self._dot_size * 2 self._stroke = '#101010' self._fill = '#404040' return svg_str_to_pixbuf( self._header() + \ self._turtle() + \ self._footer()) def _rotate_turtle(self, image): w, h = image.get_width(), image.get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(6): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context.translate(w / 2., h / 2.) context.rotate((30 + i * 60) * pi / 180.) context.translate(-w / 2., -h / 2.) Gdk.cairo_set_source_pixbuf(context, image, 0, 0) context.rectangle(0, 0, nw, nh) context.fill() self._turtle_images.append(surface) self._turtle_offset = int(self._dot_size / 2.) def _header(self): return '<svg\n' + 'xmlns:svg="http://www.w3.org/2000/svg"\n' + \ 'xmlns="http://www.w3.org/2000/svg"\n' + \ 'xmlns:xlink="http://www.w3.org/1999/xlink"\n' + \ 'version="1.1"\n' + 'width="' + str(self._svg_width) + '"\n' + \ 'height="' + str(self._svg_height) + '">\n' def _circle(self, r, cx, cy): return '<circle style="fill:' + str(self._fill) + ';stroke:' + \ str(self._stroke) + ';" r="' + str(r - 0.5) + '" cx="' + \ str(cx) + '" cy="' + str(cy) + '" />\n' def _footer(self): return '</svg>\n' def _turtle(self): svg = '<g\ntransform="scale(%.1f, %.1f)">\n' % (self._svg_width / 60., self._svg_height / 60.) svg += '%s%s%s%s%s%s%s%s' % ( ' <path d="M 27.5 48.3 ', 'C 26.9 48.3 26.4 48.2 25.9 48.2 L 27.2 50.5 L 28.6 48.2 ', 'C 28.2 48.2 27.9 48.3 27.5 48.3 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 40.2 11.7 ', 'C 38.0 11.7 36.2 13.3 35.8 15.3 ', 'C 37.7 16.7 39.3 18.4 40.5 20.5 ', 'C 42.8 20.4 44.6 18.5 44.6 16.2 ', 'C 44.6 13.7 42.6 11.7 40.2 11.7 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 40.7 39.9 ', 'C 39.5 42.1 37.9 44.0 35.9 45.4 ', 'C 36.4 47.3 38.1 48.7 40.2 48.7 ', 'C 42.6 48.7 44.6 46.7 44.6 44.3 ', 'C 44.6 42.0 42.9 40.2 40.7 39.9 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 14.3 39.9 ', 'C 12.0 40.1 10.2 42.0 10.2 44.3 ', 'C 10.2 46.7 12.2 48.7 14.7 48.7 ', 'C 16.7 48.7 18.5 47.3 18.9 45.4 ', 'C 17.1 43.9 15.5 42.1 14.3 39.9 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 19.0 15.4 ', 'C 18.7 13.3 16.9 11.7 14.7 11.7 ', 'C 12.2 11.7 10.2 13.7 10.2 16.2 ', 'C 10.2 18.5 12.1 20.5 14.5 20.6 ', 'C 15.7 18.5 17.2 16.8 19.0 15.4 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 27.5 12.6 ', 'C 29.4 12.6 31.2 13.0 32.9 13.7 ', 'C 33.7 12.6 34.1 11.3 34.1 9.9 ', 'C 34.1 6.2 31.1 3.2 27.4 3.2 ', 'C 23.7 3.2 20.7 6.2 20.7 9.9 ', 'C 20.7 11.3 21.2 12.7 22.0 13.7 ', 'C 23.7 13.0 25.5 12.6 27.5 12.6 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 43.1 30.4 ', 'C 43.1 35.2 41.5 39.7 38.5 43.0 ', 'C 35.6 46.4 31.6 48.3 27.5 48.3 ', 'C 23.4 48.3 19.4 46.4 16.5 43.0 ', 'C 13.5 39.7 11.9 35.2 11.9 30.4 ', 'C 11.9 20.6 18.9 12.6 27.5 12.6 ', 'C 36.1 12.6 43.1 20.6 43.1 30.4 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s' % ( ' <path d="M 25.9 33.8 L 24.3 29.1 ', 'L 27.5 26.5 L 31.1 29.2 L 29.6 33.8 Z" stroke_width="3.5" ', 'fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 27.5 41.6 ', 'C 23.5 41.4 22.0 39.5 22.0 39.5 L 25.5 35.4 L 30.0 35.5 ', 'L 33.1 39.7 C 33.1 39.7 30.2 41.7 27.5 41.6 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 18.5 33.8 ', 'C 17.6 30.9 18.6 27.0 18.6 27.0 L 22.6 29.1 L 24.1 33.8 ', 'L 20.5 38.0 C 20.5 38.0 19.1 36.0 18.4 33.8 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 19.5 25.1 ', 'C 19.5 25.1 20.0 23.2 22.5 21.3 ', 'C 24.7 19.7 27.0 19.6 27.0 19.6 L 26.9 24.6 L 23.4 27.3 ', 'L 19.5 25.1 Z" stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 32.1 27.8 L 28.6 25.0 ', 'L 29 19.8 C 29 19.8 30.8 19.7 33.0 21.4 ', 'C 35.2 23.2 36.3 26.4 36.3 26.4 L 32.1 27.8 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 31.3 34.0 L 32.6 29.6 ', 'L 36.8 28.0 C 36.8 28.0 37.5 30.7 36.8 33.7 ', 'C 36.2 36.0 34.7 38.1 34.7 38.1 L 31.3 34.0 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '</g>\n' return svg def save_best_time(self): file_path = os.path.join(get_activity_root(), 'data', 'best-time') best_time = [180] if os.path.exists(file_path): with open(file_path, "r") as fp: best_time = fp.readlines() int_best_time = int(best_time[0]) if not int_best_time <= self.elapsed_time and not self.game_lost: int_best_time = self.elapsed_time with open(file_path, "w") as fp: fp.write(str(int_best_time)) def load_best_time(self): file_path = os.path.join(get_activity_root(), 'data', 'best-time') if os.path.exists(file_path): with open(file_path, "r") as fp: highscore = fp.readlines() try: return int(highscore[0]) except (ValueError, IndexError) as e: logging.exception(e) return 0 return 0
class Game(): def __init__(self, canvas, parent=None, path=None, root=None, mode='array', colors=['#A0FFA0', '#FF8080']): self._canvas = canvas self._parent = parent self._path = path self._root = root self._mode = mode self.current_image = 0 self.playing = False self._timeout_id = None self._prev_mouse_pos = (0, 0) self._start_time = 0 self._colors = ['#FFFFFF'] self._colors.append(colors[0]) self._colors.append(colors[1]) self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.BUTTON_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.TOUCH_MASK) self._canvas.connect('draw', self.__draw_cb) self._canvas.connect('event', self.__event_cb) self.configure(move=False) self.we_are_sharing = False self._start_time = 0 self._timeout_id = None # Find the image files self._PATHS = glob.glob(os.path.join(self._path, 'images', '*.svg')) # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) a = max(Gdk.Screen.width(), Gdk.Screen.height()) b = min(Gdk.Screen.width(), Gdk.Screen.height()) self._bg_pixbufs = [] if self._parent.tablet_mode: # text on top # landscape self._bg_pixbufs.append( svg_str_to_pixbuf( genhole(a, a, 3 * style.GRID_CELL_SIZE, style.DEFAULT_SPACING, a - 3 * style.GRID_CELL_SIZE, style.GRID_CELL_SIZE * 3 + style.DEFAULT_SPACING))) # portrait self._bg_pixbufs.append( svg_str_to_pixbuf( genhole(a, a, 3 * style.GRID_CELL_SIZE, style.DEFAULT_SPACING, b - 3 * style.GRID_CELL_SIZE, style.GRID_CELL_SIZE * 3 + style.DEFAULT_SPACING))) else: # text on bottom # landscape self._bg_pixbufs.append( svg_str_to_pixbuf( genhole( a, a, 3 * style.GRID_CELL_SIZE, b - style.GRID_CELL_SIZE * 4 - style.DEFAULT_SPACING, a - 3 * style.GRID_CELL_SIZE, b - style.GRID_CELL_SIZE - style.DEFAULT_SPACING))) # portrait self._bg_pixbufs.append( svg_str_to_pixbuf( genhole( a, a, 3 * style.GRID_CELL_SIZE, a - style.GRID_CELL_SIZE * 4 - style.DEFAULT_SPACING, b - 3 * style.GRID_CELL_SIZE, a - style.GRID_CELL_SIZE - style.DEFAULT_SPACING))) if Gdk.Screen.width() > Gdk.Screen.height(): self._bg = Sprite(self._sprites, 0, 0, self._bg_pixbufs[0]) else: self._bg = Sprite(self._sprites, 0, 0, self._bg_pixbufs[1]) self._bg.set_layer(-2) self._bg.type = 'background' size = 3 * self._dot_size + 4 * self._space x = int((Gdk.Screen.width() - size) / 2.) self._dots = [] self._Dots = [] # larger dots for linear mode X = int((Gdk.Screen.width() - self._dot_size * 3) / 2.) Y = style.GRID_CELL_SIZE + self._yoff if self._parent.tablet_mode: yoffset = self._space * 2 + self._yoff else: yoffset = self._yoff for y in range(3): for x in range(3): xoffset = int( (self._width - 3 * self._dot_size - 2 * self._space) / 2.) self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space) + yoffset, self._new_dot_surface(color=self._colors[0]))) self._dots[-1].type = -1 # No image self._dots[-1].set_label_attributes(72) self._dots[-1].set_label('?') self._Dots.append( Sprite( self._sprites, X, Y, self._new_dot_surface(color=self._colors[0], large=True))) self._Dots[-1].type = -1 # No image self._Dots[-1].set_label_attributes(72 * 3) self._Dots[-1].set_label('?') self.number_of_images = len(self._PATHS) if USE_ART4APPS: self._art4apps = Art4Apps() self.number_of_images = len(self._art4apps.get_words()) self._record_pixbufs = [] for icon in ['media-audio', 'media-audio-recording']: self._record_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) self._play_pixbufs = [] for icon in ['play-inactive', 'play']: self._play_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) self._speak_pixbufs = [] for icon in ['speak-inactive', 'speak']: self._speak_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) left = style.GRID_CELL_SIZE right = Gdk.Screen.width() - 2 * style.GRID_CELL_SIZE y0 = style.DEFAULT_SPACING + style.DEFAULT_PADDING y1 = y0 + style.GRID_CELL_SIZE y2 = y1 + style.GRID_CELL_SIZE if not self._parent.tablet_mode: dy = Gdk.Screen.height() - 4 * style.GRID_CELL_SIZE - \ 2 * style.DEFAULT_SPACING y0 += dy y1 += dy y2 += dy y3 = int((Gdk.Screen.height() - 2 * style.GRID_CELL_SIZE) / 2) self._record = Sprite(self._sprites, right, y0, self._record_pixbufs[RECORD_OFF]) self._record.set_layer(1) self._record.type = 'record' self._play = Sprite(self._sprites, right, y1, self._play_pixbufs[PLAY_OFF]) self._play.set_layer(1) self._play.type = 'play-inactive' self._speak = Sprite(self._sprites, right, y2, self._speak_pixbufs[SPEAK_OFF]) self._speak.set_layer(1) self._speak.type = 'speak-inactive' self._next_prev_pixbufs = [] for icon in [ 'go-previous', 'go-next', 'go-previous-inactive', 'go-next-inactive' ]: self._next_prev_pixbufs.append( GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._root, 'icons', icon + '.svg'), style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)) self._prev = Sprite(self._sprites, left, y3, self._next_prev_pixbufs[PREV_INACTIVE]) self._prev.set_layer(1) self._prev.type = 'prev' if self._mode == 'array': self._prev.hide() self._next = Sprite(self._sprites, right, y3, self._next_prev_pixbufs[NEXT]) self._next.set_layer(1) self._next.type = 'next' if self._mode == 'array': self._next.hide() def configure(self, move=True): self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE if not move: if self._height < self._width: self._scale = self._height / (3 * DOT_SIZE * 1.2) else: self._scale = self._width / (3 * DOT_SIZE * 1.2) self._scale /= 1.5 self._dot_size = int(DOT_SIZE * self._scale) if self._parent.tablet_mode: # text on top self._yoff = style.GRID_CELL_SIZE * 3 + style.DEFAULT_SPACING else: self._yoff = style.DEFAULT_SPACING self._space = int(self._dot_size / 5.) return left = style.GRID_CELL_SIZE right = Gdk.Screen.width() - 2 * style.GRID_CELL_SIZE y0 = style.DEFAULT_SPACING + style.DEFAULT_PADDING y1 = y0 + style.GRID_CELL_SIZE y2 = y1 + style.GRID_CELL_SIZE if not self._parent.tablet_mode: dy = Gdk.Screen.height() - 4 * style.GRID_CELL_SIZE - \ 2 * style.DEFAULT_SPACING y0 += dy y1 += dy y2 += dy y3 = int((Gdk.Screen.height() - 2 * style.GRID_CELL_SIZE) / 2) self._record.move((right, y0)) self._play.move((right, y1)) self._speak.move((right, y2)) self._prev.move((left, y3)) self._next.move((right, y3)) # Move the dots X = int((Gdk.Screen.width() - self._dot_size * 3) / 2.) Y = style.GRID_CELL_SIZE + self._yoff if self._parent.tablet_mode: yoffset = self._space * 2 + self._yoff else: yoffset = self._yoff for y in range(3): for x in range(3): xoffset = int( (self._width - 3 * self._dot_size - 2 * self._space) / 2.) self._dots[x + y * 3].move( (xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space) + yoffset)) self._Dots[x + y * 3].move((X, Y)) # switch orientation the bg sprite if Gdk.Screen.width() > Gdk.Screen.height(): self._bg.set_image(self._bg_pixbufs[0]) else: self._bg.set_image(self._bg_pixbufs[1]) self._bg.set_layer(-2) def set_speak_icon_state(self, state): if state: self._speak.set_image(self._speak_pixbufs[SPEAK_ON]) self._speak.type = 'speak' else: self._speak.set_image(self._speak_pixbufs[SPEAK_OFF]) self._speak.type = 'speak-inactive' self._speak.set_layer(1) def set_record_icon_state(self, state): if state: self._record.set_image(self._record_pixbufs[RECORD_ON]) else: self._record.set_image(self._record_pixbufs[RECORD_OFF]) self._record.set_layer(1) def set_play_icon_state(self, state): if state: self._play.set_image(self._play_pixbufs[PLAY_ON]) self._play.type = 'play' else: self._play.set_image(self._play_pixbufs[PLAY_OFF]) self._play.type = 'play-inactive' self._play.set_layer(1) def autoplay(self): self.set_mode('linear') # forces current image to 0 self.playing = True self._autonext(next=False) def stop(self): self.playing = False if self._parent.audio_process is not None: self._parent.audio_process.terminate() self._parent.audio_process = None if self._timeout_id is not None: GObject.source_remove(self._timeout_id) self._timeout_id = None self._parent.autoplay_button.set_icon_name('media-playback-start') self._parent.autoplay_button.set_tooltip(_('Play')) self._parent.array_button.set_sensitive(True) def _autonext(self, next=True): self._timeout_id = None if not self.playing: return if next: self._Dots[self.current_image].hide() self.current_image += 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 8: self._next.set_image(self._next_prev_pixbufs[NEXT_INACTIVE]) self._next.set_layer(1) self._prev.set_image(self._next_prev_pixbufs[PREV]) self._prev.set_layer(1) self._parent.check_audio_status() self._parent.check_text_status() GObject.idle_add(self._play_sound) def _poll_audio(self): if self._parent.audio_process is None: # Already stopped? return if self._parent.audio_process.poll() is None: GObject.timeout_add(200, self._poll_audio) else: self._parent.audio_process = None self._next_image() def _play_sound(self): self._start_time = time.time() # Either play back a recording or speak the text if self._play.type == 'play': self._parent.playback_recording_cb() self._poll_audio() elif self._speak.type == 'speak': bounds = self._parent.text_buffer.get_bounds() text = self._parent.text_buffer.get_text(bounds[0], bounds[1], True) speak(text) self._next_image() def _next_image(self): accumulated_time = int(time.time() - self._start_time) if accumulated_time < 5: pause = 5 - accumulated_time else: pause = 1 if self.playing and self.current_image < 8: self._timeout_id = GObject.timeout_add(pause * 1000, self._autonext) else: self.stop() def __event_cb(self, win, event): ''' The mouse button was pressed. Is it on a sprite? or there was a gesture. ''' left = right = False if event.type in (Gdk.EventType.TOUCH_BEGIN, Gdk.EventType.TOUCH_CANCEL, Gdk.EventType.TOUCH_END, Gdk.EventType.BUTTON_PRESS, Gdk.EventType.BUTTON_RELEASE): x = int(event.get_coords()[1]) y = int(event.get_coords()[2]) if event.type in (Gdk.EventType.TOUCH_BEGIN, Gdk.EventType.BUTTON_PRESS): self._prev_mouse_pos = (x, y) elif event.type in (Gdk.EventType.TOUCH_END, Gdk.EventType.BUTTON_RELEASE): if self._parent.audio_process is not None: self._parent.audio_process.terminate() self._parent.audio_process = None terminated_audio = True else: terminated_audio = False if self.playing: self.stop() new_mouse_pos = (x, y) mouse_movement = (new_mouse_pos[0] - self._prev_mouse_pos[0], new_mouse_pos[1] - self._prev_mouse_pos[1]) # horizontal gestures only if (abs(mouse_movement[0]) / 5) > abs(mouse_movement[1]): if abs(mouse_movement[0]) > abs(mouse_movement[1]): if mouse_movement[0] < 0: right = True else: left = True if event.type in (Gdk.EventType.TOUCH_END, Gdk.EventType.BUTTON_RELEASE): spr = self._sprites.find_sprite((x, y)) if left or right or spr is not None: if spr.type in [ 'record', 'play', 'play-inactive', 'speak', 'speak-inactive' ]: if spr.type == 'record': self._parent.record_cb() elif spr.type == 'play' and not terminated_audio: self._parent.playback_recording_cb() elif spr.type == 'speak': bounds = self._parent.text_buffer.get_bounds() text = self._parent.text_buffer.get_text( bounds[0], bounds[1], True) speak(text) return elif self._mode == 'array': return self._parent.speak_text_cb() if self._parent.recording: self._parent.record_cb() if (left or spr.type == 'prev') and self.current_image > 0: self._Dots[self.current_image].hide() self.current_image -= 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 0: self._prev.set_image( self._next_prev_pixbufs[PREV_INACTIVE]) self._next.set_image(self._next_prev_pixbufs[NEXT]) elif (right or spr.type == 'next') and self.current_image < 8: self._Dots[self.current_image].hide() self.current_image += 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 8: self._next.set_image( self._next_prev_pixbufs[NEXT_INACTIVE]) self._prev.set_image(self._next_prev_pixbufs[PREV]) elif spr.type not in ['prev', 'background'] and \ self.current_image < 8: self._Dots[self.current_image].hide() self.current_image += 1 self._Dots[self.current_image].set_layer(100) if self.current_image == 8: self._next.set_image( self._next_prev_pixbufs[NEXT_INACTIVE]) self._prev.set_image(self._next_prev_pixbufs[PREV]) self._parent.check_audio_status() self._parent.check_text_status() self._prev.set_layer(1) self._next.set_layer(1) return False def get_mode(self): return self._mode def set_mode(self, mode): self.current_image = 0 self._prev.set_image(self._next_prev_pixbufs[PREV_INACTIVE]) self._next.set_image(self._next_prev_pixbufs[NEXT]) if mode == 'array': self._mode = 'array' self._prev.hide() self._next.hide() else: self._mode = 'linear' self._prev.set_layer(1) self._next.set_layer(1) for i in range(9): if self._mode == 'array': self._dots[i].set_layer(100) self._Dots[i].hide() else: self._dots[i].hide() if self.current_image == i: self._Dots[i].set_layer(100) else: self._Dots[i].hide() def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' if self._timeout_id is not None: GObject.source_remove(self._timeout_id) self.set_mode(self._mode) if self._mode == 'array': for dot in self._dots: if dot.type != -1: dot.type = -1 dot.set_shape( self._new_dot_surface(self._colors[abs(dot.type)])) dot.set_label('?') else: for dot in self._Dots: if dot.type != -1: dot.type = -1 dot.set_shape( self._new_dot_surface(self._colors[abs(dot.type)], large=True)) dot.set_label('?') self._dance_counter = 0 self._dance_step() def _dance_step(self): ''' Short animation before loading new game ''' if self._mode == 'array': for dot in self._dots: dot.set_shape( self._new_dot_surface(self._colors[int(uniform(0, 3))])) else: self._Dots[0].set_shape( self._new_dot_surface(self._colors[int(uniform(0, 3))], large=True)) self._dance_counter += 1 if self._dance_counter < 10: self._timeout_id = GObject.timeout_add(500, self._dance_step) else: self._new_images() def new_game(self): ''' Start a new game. ''' self._all_clear() def _new_images(self): ''' Select pictures at random ''' used_images = [0] * self.number_of_images for i in range(9): random_selection = int(uniform(0, self.number_of_images)) while used_images[random_selection] != 0: random_selection = int(uniform(0, self.number_of_images)) used_images[random_selection] = 1 self._dots[i].set_label('') self._dots[i].type = random_selection self._dots[i].set_shape( self._new_dot_surface(image=self._dots[i].type)) self._Dots[i].set_label('') self._Dots[i].type = self._dots[i].type self._Dots[i].set_shape( self._new_dot_surface(image=self._Dots[i].type, large=True)) if self._mode == 'array': self._dots[i].set_layer(100) self._Dots[i].hide() else: if self.current_image == i: self._Dots[i].set_layer(100) else: self._Dots[i].hide() self._dots[i].hide() if self.we_are_sharing: self._parent.send_new_images() def restore_game(self, dot_list): ''' Restore a game from the Journal or share ''' self.set_mode(self._mode) for i, dot in enumerate(dot_list): self._dots[i].type = dot self._dots[i].set_shape( self._new_dot_surface(image=self._dots[i].type)) self._dots[i].set_label('') self._Dots[i].type = dot self._Dots[i].set_shape( self._new_dot_surface(image=self._Dots[i].type, large=True)) self._Dots[i].set_label('') if self._mode == 'array': self._dots[i].set_layer(100) self._Dots[i].hide() else: if self.current_image == i: self._Dots[i].set_layer(100) else: self._Dots[i].hide() self._dots[i].hide() def save_game(self): ''' Return dot list for saving to Journal or sharing ''' dot_list = [] for dot in self._dots: dot_list.append(dot.type) return dot_list def set_sharing(self, share=True): self.we_are_sharing = share def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * 3 def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % 3, int(dot / 3)] def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def __expose_cb(self, win, event): ''' Callback to handle window expose events ''' self.do_expose_event(event) return True # Handle the expose-event by drawing def do_expose_event(self, event): # Create the cairo context cr = self._canvas.window.cairo_create() # Restrict Cairo to the exposed area; avoid extra work cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list if cr is not None: self._sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def export(self): ''' Write dot to cairo surface. ''' if self._mode == 'array': w = h = int(4 * self._space + 3 * self._dot_size) png_surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h) cr = cairo.Context(png_surface) cr.set_source_rgb(192, 192, 192) cr.rectangle(0, 0, w, h) cr.fill() for i in range(9): y = self._space + int(i / 3.) * (self._dot_size + self._space) x = self._space + (i % 3) * (self._dot_size + self._space) cr.save() cr.set_source_surface(self._dots[i].images[0], x, y) cr.rectangle(x, y, self._dot_size, self._dot_size) cr.fill() cr.restore() else: w = h = int(2 * self._space + 3 * self._dot_size) png_surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h) cr = cairo.Context(png_surface) cr.set_source_rgb(192, 192, 192) cr.rectangle(0, 0, w, h) cr.fill() y = self._space x = self._space cr.save() cr.set_source_surface(self._Dots[self.current_image].images[0], x, y) cr.rectangle(x, y, 3 * self._dot_size, 3 * self._dot_size) cr.fill() cr.restore() return png_surface def _new_dot_surface(self, color='#000000', image=None, large=False): ''' generate a dot of a color color ''' if large: size = self._dot_size * 3 else: size = self._dot_size self._svg_width = size self._svg_height = size if image is None: # color dot self._stroke = color self._fill = color pixbuf = svg_str_to_pixbuf(self._header() + self._circle(size / 2., size / 2., size / 2.) + self._footer()) else: if USE_ART4APPS: word = self._art4apps.get_words()[image] try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._art4apps.get_image_filename(word), size, size) except Exception, e: _logger.error('new dot surface %s %s: %s' % (image, word, e)) word = 'zebra' # default in case image is not found pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._art4apps.get_image_filename(word), size, size) else:
class Ball(): ''' The Bounce class is used to define the ball and the user interaction. ''' def __init__(self, sprites, filename): self.current_frame = 0 self.frames = [] # Easter Egg animation self.sprites = sprites self.ball = Sprite(self.sprites, 0, 0, svg_str_to_pixbuf( svg_from_file(filename))) self.ball.set_layer(1) self.ball.set_label_attributes(24) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self.frames.append(Sprite( self.sprites, 0, 0, svg_str_to_pixbuf( svg_header(SIZE, SIZE, 1.0) + TRANSFORMS[i] + \ ball + PUNCTURE + AIR + '</g>' + svg_footer()))) for frame in self.frames: frame.set_layer(1) frame.move((0, -SIZE)) # move animation frames off screen def new_ball(self, filename): ''' Create a ball object and Easter Egg animation from an SVG file. ''' self.ball.images[0] = svg_str_to_pixbuf(svg_from_file(filename)) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self.frames[i].images[0] = svg_str_to_pixbuf( svg_header(SIZE, SIZE, 1.0) + TRANSFORMS[i] + \ ball + PUNCTURE + AIR + '</g>' + svg_footer()) def new_ball_from_image(self, filename): ''' Just create a ball object from an image file ''' if filename == '': _logger.debug('Image file not found.') return try: self.ball.images[0] = gtk.gdk.pixbuf_new_from_file_at_size( filename, SIZE, SIZE) except: _logger.debug('Could not load image from %s.', filename) def ball_x(self): return self.ball.get_xy()[0] def ball_y(self): return self.ball.get_xy()[1] def frame_x(self, i): return self.frames[i].get_xy()[0] def frame_y(self, i): return self.frames[i].get_xy()[1] def width(self): return self.ball.rect[2] def height(self): return self.ball.rect[3] def move_ball(self, pos): self.ball.move(pos) def move_ball_relative(self, pos): self.ball.move_relative(pos) def move_frame(self, i, pos): self.frames[i].move(pos) def move_frame_relative(self, i, pos): self.frames[i].move_relative(pos) def hide_frames(self): for frame in self.frames: frame.move((0, -SIZE)) # hide the animation frames def next_frame(self, frame_counter): if frame_counter in ANIMATION: self._switch_frames(ANIMATION[frame_counter]) return self.current_frame def _switch_frames(self, frames): ''' Switch between frames in the animation ''' self.move_frame(frames[1], (self.frame_x(frames[0]), self.frame_y(frames[0]))) self.move_frame(frames[0], ((0, -SIZE))) # hide the frame self.current_frame = frames[1]
class Turtle: def __init__(self, turtles, key, turtle_colors=None): """ The turtle is not a block, just a sprite with an orientation """ self.x = 0 self.y = 0 self.hidden = False self.shapes = [] self.custom_shapes = False self.type = 'turtle' self.heading = 0 self.pen_shade = 50 self.pen_color = 0 self.pen_gray = 100 self.pen_size = 5 self.pen_state = True # If the turtle key is an int, we'll use a palette color as the # turtle color try: int_key = int(key) use_color_table = True except ValueError: use_color_table = False if turtle_colors is not None: self.colors = turtle_colors[:] self.shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = ['#%06x' % (color_table[fill]), '#%06x' % (color_table[stroke])] self.shapes = generate_turtle_pixbufs(self.colors) else: self.shapes = turtles.get_pixbufs() if turtles.sprite_list is not None: self.spr = Sprite(turtles.sprite_list, 0, 0, self.shapes[0]) else: self.spr = None turtles.add_to_dict(key, self) def set_shapes(self, shapes): """ Reskin the turtle """ n = len(shapes) if n == SHAPES: self.shapes = shapes[:] else: if n != 1: _logger.debug("%d images passed to set_shapes: ignoring" % (n)) images = [shapes[0]] if self.heading == 0: for i in range(3): images.append(images[i].rotate_simple(270)) for i in range(SHAPES): j = (i + 4) % SHAPES self.shapes[j] = images[int(j / 9)] else: j = int(self.heading + 5) % 360 / (360 / SHAPES) self.shapes[j] = images[0] self.custom_shapes = True self.show() def reset_shapes(self): """ Reset the shapes to the standard turtle """ if self.custom_shapes: self.shapes = generate_turtle_pixbufs(self.colors) self.custom_shapes = False def set_heading(self, heading): """ Set the turtle heading (one shape per 360/SHAPES degrees) """ self.heading = heading i = (int(self.heading + 5) % 360) / (360 / SHAPES) if not self.hidden and self.spr is not None: try: self.spr.set_shape(self.shapes[i]) except IndexError: self.spr.set_shape(self.shapes[0]) def set_color(self, color): """ Set the pen color for this turtle. """ self.pen_color = color def set_gray(self, gray): """ Set the pen gray level for this turtle. """ self.pen_gray = gray def set_shade(self, shade): """ Set the pen shade for this turtle. """ self.pen_shade = shade def set_pen_size(self, pen_size): """ Set the pen size for this turtle. """ self.pen_size = pen_size def set_pen_state(self, pen_state): """ Set the pen state (down==True) for this turtle. """ self.pen_state = pen_state def hide(self): """ Hide the turtle. """ if self.spr is not None: self.spr.hide() self.hidden = True def show(self): """ Show the turtle. """ if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) self.hidden = False self.move((self.x, self.y)) self.set_heading(self.heading) def move(self, pos): """ Move the turtle. """ self.x, self.y = int(pos[0]), int(pos[1]) if not self.hidden and self.spr is not None: self.spr.move(pos) return(self.x, self.y) def get_xy(self): """ Return the turtle's x, y coordinates. """ return(self.x, self.y) def get_heading(self): """ Return the turtle's heading. """ return(self.heading) def get_color(self): """ Return the turtle's color. """ return(self.pen_color) def get_gray(self): """ Return the turtle's gray level. """ return(self.pen_gray) def get_shade(self): """ Return the turtle's shade. """ return(self.pen_shade) def get_pen_size(self): """ Return the turtle's pen size. """ return(self.pen_size) def get_pen_state(self): """ Return the turtle's pen state. """ return(self.pen_state)