예제 #1
0
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()
예제 #2
0
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))
예제 #3
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)
예제 #4
0
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()
예제 #5
0
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()
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
    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
예제 #9
0
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()
예제 #10
0
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'
예제 #11
0
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]
예제 #12
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:
예제 #13
0
파일: game.py 프로젝트: leonardcj/xocolors
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'
예제 #14
0
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)
예제 #15
0
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
예제 #16
0
파일: tile.py 프로젝트: erilyth/paths
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))
예제 #17
0
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]
예제 #18
0
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
예제 #19
0
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
예제 #20
0
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
예제 #21
0
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)))
예제 #22
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)
예제 #23
0
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
예제 #24
0
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)
예제 #25
0
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)))
예제 #26
0
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
예제 #27
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:
예제 #28
0
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]
예제 #29
0
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)