[gnome-games/sudoku-tube] Move the SudokuView class out



commit b7e46eedbc0a03af2f5cdd024af099d2efd73a3b
Author: Zhang Sen <zh jesse gmail com>
Date:   Wed Jul 22 18:52:49 2009 +0800

    Move the SudokuView class out

 gnome-sudoku/src/lib/dancer.py   |    7 +-
 gnome-sudoku/src/lib/gsudoku.py  |  589 --------------------------------------
 gnome-sudoku/src/lib/main.py     |   15 +-
 gnome-sudoku/src/lib/model.py    |  331 +++++++++++++++++++++
 gnome-sudoku/src/lib/printing.py |    4 +-
 gnome-sudoku/src/lib/view.py     |  260 +++++++++++++++++
 6 files changed, 605 insertions(+), 601 deletions(-)
---
diff --git a/gnome-sudoku/src/lib/dancer.py b/gnome-sudoku/src/lib/dancer.py
index 980c57b..8400694 100644
--- a/gnome-sudoku/src/lib/dancer.py
+++ b/gnome-sudoku/src/lib/dancer.py
@@ -101,7 +101,8 @@ class GridDancer:
 
 if __name__ == '__main__':
     def test_dance_grid ():
-        import gsudoku
+        import model
+        import view
         window = gtk.Window()
         game = '''9 1 6 3 2 8 4 5 7
                   5 7 4 6 1 9 2 8 3
@@ -112,8 +113,8 @@ if __name__ == '__main__':
                   4 6 9 1 8 7 5 3 2
                   1 2 8 9 5 3 6 7 4
                   7 5 3 4 6 2 8 1 9'''
-        view = gsudoku.SudokuView(9)
-        model = gsudoku.SudokuModel(game, 9)
+        view = view.SudokuView(9)
+        model = model.SudokuModel(game, 9)
         view.connect_to_model(model)
         dancer = GridDancer(view, model)
 
diff --git a/gnome-sudoku/src/lib/main.py b/gnome-sudoku/src/lib/main.py
index a25788f..6ba8ca4 100644
--- a/gnome-sudoku/src/lib/main.py
+++ b/gnome-sudoku/src/lib/main.py
@@ -15,7 +15,8 @@ from gettext import gettext as _
 from gettext import ngettext
 
 import game_selector
-import gsudoku
+import model
+import view
 import printing
 import saver
 import sudoku_generator_gui
@@ -118,8 +119,8 @@ class UI (gconf_wrapper.GConfWrapper):
         """Finally enter the puzzle"""
         self._puzzle = puzzle
         if game_type == game_selector.NewOrSavedGameSelector.NEW_GAME:
-            self._main_model = gsudoku.SudokuModel(puzzle, 9)
-            self._notes_model = gsudoku.NotesModel(self._main_model, group_size=9)
+            self._main_model = model.SudokuModel(puzzle, 9)
+            self._notes_model = model.NotesModel(self._main_model, group_size=9)
         elif game_type == game_selector.NewOrSavedGameSelector.SAVED_GAME:
             self._open_saved_game(puzzle)
         else:
@@ -130,10 +131,10 @@ class UI (gconf_wrapper.GConfWrapper):
 
     def _open_saved_game(self, jar):
         virgin, in_prog = jar["game"].split('\n')
-        self._main_model = gsudoku.SudokuModel(virgin, 9, in_prog)
+        self._main_model = model.SudokuModel(virgin, 9, in_prog)
         self._main_model.set_autofill_count(jar["gsd.auto_fills"])
 
-        self._notes_model = gsudoku.NotesModel(self._main_model, group_size=9)
+        self._notes_model = model.NotesModel(self._main_model, group_size=9)
         self._notes_model.set_hints_count(jar["gsd.hints"])
         for (x, y, top, bottom) in jar["notes"]:
             self._notes_model.set_notes(x, y, top, bottom)
@@ -228,7 +229,7 @@ class UI (gconf_wrapper.GConfWrapper):
         side_grid_container = self.builder.get_object("side_grid_container")
         tracker_ui_container = self.builder.get_object("tracker_ui_container")
 
-        self._main_grid_vew = gsudoku.SudokuView(group_size=9)
+        self._main_grid_vew = view.SudokuView(group_size=9)
         self._main_grid_vew.connect('puzzle-finished', self.you_win_callback)
         main_grid_container.pack_start(self._main_grid_vew, padding=6)
 
@@ -240,7 +241,7 @@ class UI (gconf_wrapper.GConfWrapper):
         side_grid_container.add(self._side_grid_vew)
 
     def _setup_side_view(self):
-        self._side_grid_vew = gsudoku.SudokuView(group_size=9)
+        self._side_grid_vew = view.SudokuView(group_size=9)
         for x in range(9):
             for y in range(9):
                 self._side_grid_vew.set_readonly_appearance(x, y, True)
diff --git a/gnome-sudoku/src/lib/model.py b/gnome-sudoku/src/lib/model.py
new file mode 100644
index 0000000..598e2b0
--- /dev/null
+++ b/gnome-sudoku/src/lib/model.py
@@ -0,0 +1,331 @@
+# -*- coding: utf-8 -*-
+import sudoku
+
+
+class ParallelDict (dict):
+    """A handy new sort of dictionary for tracking conflicts.
+
+    pd = ParallelDict()
+    pd[1] = [2, 3, 4] # 1 is linked with 2, 3 and 4
+    pd -> {1:[2, 3, 4], 2:[1], 3:[1], 4:[1]}
+    pd[2] = [1, 3, 4] # 2 is linked with 3 and 4 as well as 1
+    pd -> {1: [2, 3, 4], 2:[3, 4], 3:[1, 2], 4:[1, 2]}
+    Now for the cool part...
+    del pd[1]
+    pd -> {2: [2, 3], 3:[2], 4:[2]}
+
+    Pretty neat, no?
+    """
+    def __init__ (self, *args):
+        dict.__init__(self, *args)
+
+    def __setitem__ (self, k, v):
+        dict.__setitem__(self, k, set(v))
+        for i in v:
+            if i == k:
+                continue
+            if self.has_key(i):
+                self[i].add(k)
+            else:
+                dict.__setitem__(self, i, set([k]))
+
+    def __delitem__ (self, k):
+        v = self[k]
+        dict.__delitem__(self, k)
+        for i in v:
+            if i == k:
+                continue
+            if self.has_key(i):
+                # Make sure we have a reference to i. If we don't
+                # something has gone wrong... but according to bug
+                # 385937 this has gone wrong at least once, so we'd
+                # better check for it.
+                if k in self[i]:
+                    self[i].remove(k)
+                if not self[i]:
+                    # If k was the last value in the list of values
+                    # for i, then we delete i from our dictionary
+                    dict.__delitem__(self, i)
+
+
+class NumberBoxModel:
+    def __init__(self, x, y, value=None, conflict=None):
+        self.x = x
+        self.y = y
+        self.value = value
+        self.conflict = conflict
+
+
+class NotesModel:
+    """Track notes for a SudokuModel
+
+    Act as a observer of SudokuModel, and a model/observable for SudokuView.
+    SudokuModel should know nothing about notes.
+    """
+
+    def __init__(self, sudoku_model, group_size):
+        self._notes = {}
+        self._observers = []
+        self._group_size = group_size
+        self._model = sudoku_model
+        self._model.add_observer(self)
+
+        self._always_show_hints = False
+        self._hints_count = 0
+
+    def toggle_auto_hint(self, flag):
+        if flag:
+            self._always_show_hints = True
+            self.update_all_hints()
+        else:
+            self._always_show_hints = False
+            self._clear_hints()
+
+    def add_observer(self, observer):
+        self._observers.append(observer)
+        init_change = []
+        for (x, y) in self._notes:
+            top, bottom = self._notes[(x, y)]
+            init_change.append((x, y, top, bottom))
+        self._signal_observers(init_change)
+
+    def _signal_observers(self, changes):
+        for observer in self._observers:
+            observer.update_notes(changes)
+
+    def set_notes(self, x, y, top_note=None, bottom_note=None):
+        if (x, y) not in self._notes:
+            self._notes[(x, y)] = [None, None]
+
+        if top_note is not None:
+            self._notes[(x, y)][0] = top_note
+        if bottom_note is not None:
+            self._notes[(x, y)][1] = bottom_note
+
+        change = (x, y, top_note, bottom_note)
+        self._signal_observers([change])
+
+    def update(self, changes):
+        if not self._always_show_hints:
+            return
+
+        self.update_all_hints()
+
+    def puzzle_finished_cb(self):
+        self._clear_hints()
+
+    def update_all_hints(self):
+        for x in range(self._group_size):
+            for y in range(self._group_size):
+                self._update_hint_for_entry(x, y)
+
+    def show_hint(self, x, y):
+        self._update_hint_for_entry(x, y)
+
+    def _update_hint_for_entry(self, x, y):
+        # no need to show anything if it's already filled, being correct or not
+        if self._model.get_value(x, y):
+            self.set_notes(x, y, bottom_note="")
+            return
+
+        vals = self._model.grid.possible_values(x, y)
+        vals = list(vals)
+        vals.sort()
+        if vals:
+            txt = ''.join([str(v) for v in vals])
+            self.set_notes(x, y, bottom_note=txt)
+            self._increase_hints_count()
+        else:
+            self.set_notes(x, y, bottom_note="X")
+
+    def _clear_hints(self):
+        self._clear_bottom_note()
+
+    def _clear_top_note(self):
+        for x in range(self._group_size):
+            for y in range(self._group_size):
+                self.set_notes(x, y, top_note="")
+
+    def _clear_bottom_note(self):
+        for x in range(self._group_size):
+            for y in range(self._group_size):
+                self.set_notes(x, y, bottom_note="")
+
+    def _increase_hints_count(self):
+        self._hints_count += 1
+
+    def set_hints_count(self, count):
+        self._hints_count = count
+
+    def get_hints_count(self):
+        return self._hints_count
+
+    def clear_notes(self):
+        self._clear_top_note()
+        self._clear_bottom_note()
+
+class SudokuModel:
+
+    def __init__ (self, virgin_grid, group_size, initial_grid=None):
+        self.autofill_count = 0
+        self.show_impossible_implications = False
+        self.impossible_hints = 0
+        self.impossibilities = []
+        self.__trackers_tracking__ = {}
+        self.group_size = group_size
+        self.grid = None
+        self._virgin_grid = None
+        self._observers = []
+
+        self.__error_pairs__ = ParallelDict()
+        self._setup_virgin_grid(virgin_grid)
+        if initial_grid:
+            self._setup_initial_grid(initial_grid)
+
+    def add_observer(self, observer):
+        self._observers.append(observer)
+
+    def _signal_observers(self, values):
+        for observer in self._observers:
+            observer.update(values)
+
+    def _signal_completeness(self):
+        for observer in self._observers:
+            observer.puzzle_finished_cb()
+
+    def reset_grid (self):
+        """Reset grid to its original setup.
+        """
+
+        for x in range(self.group_size):
+            for y in range(self.group_size):
+                if not self.grid.virgin._get_(x, y):
+                    # conflicting values are not being tracked, so we have to
+                    # set_value on all the entries, instead of only filled ones
+                    self.set_value(x, y, 0)
+
+    def _setup_virgin_grid(self, virgin):
+        """both grid and initial_grid should be str"""
+        self._virgin_grid = [int(c) for c in virgin.split()]
+        self.grid = sudoku.InteractiveSudoku(virgin, group_size=self.group_size)
+
+    def _setup_initial_grid(self, initial_grid):
+        values = [int(c) for c in initial_grid.split()]
+        for row in range(self.group_size):
+            for col in range(self.group_size):
+                index = row * 9 + col
+                if values[index] and not self.grid._get_(col, row):
+                    self.set_value(col, row, values[index])
+
+    def set_value(self, x, y, value):
+        result = []
+        old_conflicts = self._remove_old_conficts(x, y)
+        new_conflicts = []
+
+        # Some value won't go into the grid, e.g. conflicts values?
+        # This makes things complicated.
+        if not value:
+            if self.grid._get_(x, y):
+                self.grid.remove(x, y)
+        else:
+            try:
+                self.grid.add(x, y, value, force=True)
+            except sudoku.ConflictError, err:
+                new_conflicts = self._find_new_conflicts(x, y, value)
+
+        flag = True if new_conflicts else False
+        result.append((x, y, value, flag))
+        result.extend(old_conflicts)
+        result.extend(new_conflicts)
+
+        self._signal_observers(result)
+        self._check_for_completeness()
+
+    def get_value(self, x, y):
+        return self.grid._get_(x, y)
+
+    def get_virgin_value(self, x, y):
+        return self._virgin_grid[9 * y + x]
+
+    def _remove_old_conficts(self, x, y):
+        result = []
+        old_conflicts = self._remove_related_conflicts(x, y)
+        for entry in old_conflicts:
+            change = (entry[0], entry[1], None, False)
+            result.append(change)
+        return result
+
+    def _find_new_conflicts(self, x, y, value):
+        result = []
+        new_conflicts = self.grid.find_conflicts(x, y, value)
+        if new_conflicts:
+            self.__error_pairs__[(x, y)] = new_conflicts
+        for entry in new_conflicts:
+            change = (entry[0], entry[1], None, True)
+            result.append(change)
+        return result
+
+    def _remove_related_conflicts(self, x, y):
+        result = []
+        if self.__error_pairs__.has_key((x, y)):
+            errors_removed = self.__error_pairs__[(x, y)]
+            del self.__error_pairs__[(x, y)]
+            for coord in errors_removed:
+                # If we're not an error by some other pairing...
+                if not self.__error_pairs__.has_key(coord):
+                    result.append(coord)
+        return result
+
+    def _check_for_completeness(self):
+        if self.grid.check_for_completeness():
+            self._signal_completeness()
+
+    def set_autofill_count(self, count):
+        self.autofill_count = count
+
+    def get_autofill_count(self):
+        return self.autofill_count
+
+    def auto_fill (self):
+        self.autofill_count += 1
+        result = []
+
+        changed = set(self.grid.auto_fill()) # there are duplicate?
+        for coords, val in changed:
+            x, y = coords
+            result.append((x, y, val, None))
+
+        self._signal_observers(result)
+        self._check_for_completeness()
+
+    def auto_fill_current(self, x, y):
+        filled = self.grid.auto_fill_for_xy(x, y)
+        if filled and filled != -1:
+            self.set_value(x, y, filled[1])
+
+    def _mark_impossible_implications(self, x, y):
+        if not self.grid:
+            return
+        implications = self.grid.find_impossible_implications(x, y)
+        if implications:
+            for x, y in implications:
+                self.__entries__[(x, y)].set_impossible(True)
+                if not (x, y) in self.impossibilities:
+                    self.impossible_hints += 1
+        for x, y in self.impossibilities:
+            if not (x, y) in implications:
+                self.__entries__[(x, y)].set_impossible(False)
+        self.impossibilities = implications
+
+    def _trackers_for_point(self, x, y, val = None):
+        if val:
+            # if we have a value we can do this a simpler way...
+            track_for_point = filter(
+                lambda t: (x, y, val) in t[1],
+                self.trackers.items()
+                )
+        else:
+            track_for_point = filter(
+                lambda tkr: True in [t[0] == x and t[1] == y for t in tkr[1]],
+                self.trackers.items())
+        return [t[0] for t in track_for_point]
diff --git a/gnome-sudoku/src/lib/printing.py b/gnome-sudoku/src/lib/printing.py
index 85fab43..bcbcc2c 100644
--- a/gnome-sudoku/src/lib/printing.py
+++ b/gnome-sudoku/src/lib/printing.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 import gtk, cairo, time
 import os.path
-import sudoku, gsudoku, saver, defaults
+import sudoku, model, saver, defaults
 from gtk_goodies import gconf_wrapper
 
 def fit_squares_in_rectangle (width, height, n, margin = 0):
@@ -85,7 +85,7 @@ class SudokuPrinter:
             cr.move_to(left, top - height / 2)
             cr.show_text(label)
 
-            if isinstance(sudoku, gsudoku.SudokuModel):
+            if isinstance(sudoku, model.SudokuModel):
                 sudoku = sudoku.grid
 
             sudoku_thumber.draw_sudoku (cr, sudoku.grid, None, best_square_size, left, top)
diff --git a/gnome-sudoku/src/lib/view.py b/gnome-sudoku/src/lib/view.py
new file mode 100644
index 0000000..5b10373
--- /dev/null
+++ b/gnome-sudoku/src/lib/view.py
@@ -0,0 +1,260 @@
+# -*- coding: utf-8 -*-
+
+import math
+
+import gtk
+import gobject
+
+import colors
+import number_box
+
+def gtkcolor_to_rgb (color):
+    return (color.red   / float(2**16),
+            color.green / float(2**16),
+            color.blue  / float(2**16))
+
+SPACING_FACTOR = 40 # The size of a box compared (roughly) to the size
+                    # of padding -- the larger this is, the smaller
+                    # the spaces
+SMALL_TO_BIG_FACTOR = 3.5 # The number of times wider than a small line a big line is.
+
+class SudokuNumberGrid (gtk.AspectFrame):
+
+    def __init__ (self, group_size):
+        self.table = gtk.Table(rows = group_size, columns = group_size, homogeneous = True)
+        self.group_size = group_size
+        self.__entries__ = {}
+        self._setup_table()
+
+        gtk.AspectFrame.__init__(self, obey_child = False)
+        self.set_shadow_type(gtk.SHADOW_NONE)
+        self.eb = gtk.EventBox()
+        self.eb.add(self.table)
+        self.add(self.eb)
+        self.connect('size-allocate', self._allocate_cb)
+        self.show_all()
+
+    def _setup_table(self):
+        for x in range(self.group_size):
+            for y in range(self.group_size):
+                e = number_box.SudokuNumberBox(upper = self.group_size)
+                e.x = x
+                e.y = y
+                self.table.attach(e, x, x+1, y, y+1,
+                                  )
+                self.__entries__[(x, y)] = e
+
+    def _allocate_cb (self, widget, rect):
+        if rect.width > rect.height:
+            side = rect.height
+        else: side = rect.width
+        # we want our small spacing to be 1/15th the size of a box
+        spacing = float(side) / (self.group_size * SPACING_FACTOR)
+        if spacing == 0:
+            spacing = 1
+        if hasattr(self, 'small_spacing') and spacing == self.small_spacing:
+            return
+        else:
+            self._change_spacing(spacing)
+
+    def _change_spacing (self, small_spacing):
+        self.small_spacing = small_spacing
+        self.big_spacing = int(small_spacing*SMALL_TO_BIG_FACTOR)
+        self.table.set_row_spacings(int(small_spacing))
+        self.table.set_col_spacings(int(small_spacing))
+        box_side = int(math.sqrt(self.group_size))
+        for n in range(1, box_side):
+            self.table.set_row_spacing(box_side*n-1, self.big_spacing)
+            self.table.set_col_spacing(box_side*n-1, self.big_spacing)
+        self.table.set_border_width(self.big_spacing)
+
+    def set_bg_color (self, color):
+        if type(color) == str:
+            try:
+                color = gtk.gdk.color_parse(color)
+            except:
+                print 'set_bg_color handed Bad color', color
+                return
+        self.eb.modify_bg(gtk.STATE_NORMAL, color)
+        self.eb.modify_base(gtk.STATE_NORMAL, color)
+        self.eb.modify_fg(gtk.STATE_NORMAL, color)
+        self.table.modify_bg(gtk.STATE_NORMAL, color)
+        self.table.modify_base(gtk.STATE_NORMAL, color)
+        self.table.modify_fg(gtk.STATE_NORMAL, color)
+        for e in self.__entries__.values():
+            e.modify_bg(gtk.STATE_NORMAL, color)
+
+    def set_value(self, x, y, value):
+        self.__entries__[(x, y)].set_value(value)
+
+    def set_readonly_appearance(self, x, y, flag):
+        self.__entries__[(x, y)].set_read_only(flag)
+
+    def set_color(self, x, y, color):
+        self.__entries__[(x, y)].set_color(color)
+
+    def set_background_color(self, x, y, color):
+        self.__entries__[(x, y)].set_background_color(color)
+
+    def set_focus(self, x, y):
+        self.table.set_focus_child(self.__entries__[x, y])
+
+    def get_focus(self):
+        return self.table.get_focus_child()
+
+
+class SudokuView(SudokuNumberGrid, gobject.GObject):
+
+    # some signals to give notice about change of the View
+    __gsignals__ = {
+            # atm. only used by dancer
+            "puzzle-finished": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+            # atm. only used by tracker
+            "view-updated": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+                (int, int, int))}
+
+    def __init__(self, group_size):
+        SudokuNumberGrid.__init__(self, group_size)
+        gobject.GObject.__init__(self)
+
+        self._model = None
+        self._do_highlight_cells = False
+        self._notes_model = None
+
+        for e in self.__entries__.values():
+            e.show()
+            e.connect('number-changed', self._number_changed_cb)
+            e.connect('notes-changed', self._notes_changed_cb)
+            e.connect('focus-in-event', self._focus_callback)
+            e.connect('key-press-event', self._key_press_cb)
+
+    def connect_to_model(self, model):
+        if not model:
+            return
+        self._model = model
+        model.add_observer(self)
+        for x in range(self.group_size):
+            for y in range(self.group_size):
+                val = model.get_value(x, y)
+                if val:
+                    self.set_value(x, y, val)
+                    if model.get_virgin_value(x, y):
+                        self.set_readonly_appearance(x, y, True)
+
+    def connect_to_notes_model(self, model):
+        self._notes_model = model
+        self._notes_model.add_observer(self)
+
+    def update(self, values):
+        """Update the GUI, called by a Model
+
+        values should be a list of NumberBoxModel"""
+        for box in values:
+            x, y, value, conflict = box
+            if value is not None:
+                self.set_value(x, y, value)
+                self.emit("view-updated", x, y, value)
+            if conflict is not None:
+                self._show_conflict(x, y, conflict)
+
+    def puzzle_finished_cb(self):
+        self.emit("puzzle-finished")
+
+    def _show_conflict(self, x, y, conflict):
+        self.__entries__[(x, y)].set_error_highlight(conflict)
+
+    def update_notes(self, notes_list):
+        for notes in notes_list:
+            x, y, top_note, bottom_note = notes
+            self.__entries__[(x, y)].set_notes((top_note, bottom_note))
+
+    def _number_changed_cb(self, widget, new_number):
+        self.update_model(widget.x, widget.y, new_number)
+
+    def update_model(self, x, y, value):
+        self._model.set_value(x, y, value)
+
+    def _notes_changed_cb(self, widget, top_note, bottom_note):
+        self._notes_model.set_notes(widget.x, widget.y, top_note, bottom_note)
+
+    def _focus_callback(self, widget, event):
+        if self._do_highlight_cells:
+            self._highlight_cells()
+
+    def _key_press_cb(self, widget, event):
+        key = gtk.gdk.keyval_name(event.keyval)
+        dest_x, dest_y = self._go_around(widget.x, widget.y, key)
+        if dest_x is not None:
+            self.set_focus(dest_x, dest_y)
+
+    def toggle_highlight (self, val):
+        self._do_highlight_cells = val
+        if val:
+            self._highlight_cells()
+        else:
+            self.unhighlight_cells()
+
+    def unhighlight_cells(self):
+        for e in self.__entries__.values():
+            e.set_background_color(None)
+
+    def _highlight_cells(self):
+        focused = self.get_focus()
+        if not focused: # no need to do anything if no box has focus
+            return
+        self.unhighlight_cells() # first clear all the cells
+        if not hasattr(self, 'box_color'):
+            self._get_highlight_colors()
+        my_x, my_y = focused.x, focused.y
+
+        grid = self._model.grid
+        for x, y in grid.col_coords[my_x]:
+            if (x, y) != (my_x, my_y):
+                self.__entries__[(x, y)].set_background_color(self.col_color)
+        for x, y in grid.row_coords[my_y]:
+            if (x, y) != (my_x, my_y):
+                self.__entries__[(x, y)].set_background_color(self.row_color)
+        for x, y in grid.box_coords[grid.box_by_coords[(my_x, my_y)]]:
+            if (x, y) != (my_x, my_y):
+                e = self.__entries__[(x, y)]
+                if x == my_x:
+                    e.set_background_color(self.box_and_col_color)
+                elif y == my_y:
+                    e.set_background_color(self.box_and_row_color)
+                else:
+                    e.set_background_color(self.box_color)
+
+    def _get_highlight_colors(self):
+        entry = self.__entries__.values()[0]
+        default_color = gtkcolor_to_rgb(entry.style.bg[gtk.STATE_SELECTED])
+        hsv = colors.rgb_to_hsv(*default_color)
+        box_s = hsv[1]
+        box_v = hsv[2]
+        if box_v < 0.5:
+            box_v = box_v * 2
+        if box_s > 0.75:
+            box_s = box_s * 0.5
+        else:
+            box_s = box_s * 1.5
+            if box_s > 1:
+                box_s = 1.0
+        self.box_color = colors.hsv_to_rgb(hsv[0], box_s, box_v)
+        self.box_and_row_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.33 / 2})
+        self.row_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.33})
+        self.col_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.66})
+        self.box_and_col_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 1.0 - (0.33 / 2)})
+
+    def _go_around(self, x, y, direction):
+        '''return the coordinate if we should go to the other side of the grid.
+        Or else return None.'''
+        limit_min, limit_max = 0, self.group_size - 1
+        y_edge = [(limit_min, 'Up'), (limit_max, 'Down')]
+        x_edge = [(limit_min, 'Left'), (limit_max, 'Right')]
+        opposite_edge = {limit_min: limit_max, limit_max: limit_min}
+
+        if (y, direction) in y_edge:
+            return x, opposite_edge[y]
+        elif (x, direction) in x_edge:
+            return opposite_edge[x], y
+        else:
+            return None, None



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]