Ticket #2235: 0001-add-spiral-to-ring-layout-as-number-of-icons-increas.patch

File 0001-add-spiral-to-ring-layout-as-number-of-icons-increas.patch, 8.8 KB (added by walter, 14 years ago)

next, add spiral enhancement as new default

  • src/jarabe/desktop/favoriteslayout.py

    From bb4e9ee815df681b74a6c0d4b5dab214eb5895ac Mon Sep 17 00:00:00 2001
    From: Walter Bender <walter@sugarlabs.org>
    Date: Wed, 25 Aug 2010 09:40:52 -0400
    Subject: [PATCH] add spiral to ring layout as number of icons increases
    
    ---
     src/jarabe/desktop/favoriteslayout.py |  122 +++++++++++++++++++++++++++++---
     1 files changed, 110 insertions(+), 12 deletions(-)
    
    diff --git a/src/jarabe/desktop/favoriteslayout.py b/src/jarabe/desktop/favoriteslayout.py
    index 160ce0e..8bc0f1f 100644
    a b _ICON_SIZES = [style.MEDIUM_ICON_SIZE, style.STANDARD_ICON_SIZE, 
    198198_ICON_SPACING_FACTORS = [1.5, 1.4, 1.3, 1.2, 1.15, 1.1, 1.05, 1.0]
    199199
    200200
    201 class RingLayout(FavoritesLayout):
     201class BasicRingLayout(FavoritesLayout):
    202202    """Lay out icons in a ring around the XO man."""
    203203
    204     __gtype_name__ = 'RingLayout'
     204    __gtype_name__ = 'BasicRingLayout'
    205205    icon_name = 'view-radial'
    206206    """Name of icon used in home view dropdown palette."""
    207     key = 'ring-layout'
     207    key = 'basic-ring-layout'
    208208    """String used in profile to represent this view."""
    209209    # TRANS: label for the ring layout in the favorites view
    210210    palette_name = _('Ring')
    class RingLayout(FavoritesLayout): 
    312312            return 0
    313313
    314314
     315_MIMIMUM_RADIUS_ENCROACHMENT = 0.75
     316_INITIAL_ANGLE = math.pi
     317_SPIRAL_SPACING_FACTORS = [1.5, 1.5, 1.5, 1.4, 1.35, 1.3, 1.25, 1.2]
     318
     319
     320class RingLayout(BasicRingLayout):
     321    """ Variation of Basic Ring that morphs into a spiral as
     322    the number of icons increases beyond the capacity of the
     323    STANDARD_ICON_SIZE. """
     324
     325    __gtype_name__ = 'RingLayout'
     326    icon_name = 'view-radial'
     327    """Name of icon used in home view dropdown palette."""
     328    key = 'ring-layout'
     329    """String used in profile to represent this view."""
     330
     331    def __init__(self):
     332        BasicRingLayout.__init__(self)
     333        self._locked_children = {}
     334        self._spiral_mode = False
     335
     336    def _calculate_radius_and_icon_size(self, children_count):
     337        """ Adjust the ring or spiral radius and icon size as needed. """
     338        self._spiral_mode = False
     339        # Begin by increasing the radius.
     340        distance = style.MEDIUM_ICON_SIZE + style.DEFAULT_SPACING * \
     341            _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.MEDIUM_ICON_SIZE)]
     342        radius = max(children_count * distance / (2 * math.pi), _MINIMUM_RADIUS)
     343        if radius < _MAXIMUM_RADIUS:
     344            return radius, style.MEDIUM_ICON_SIZE
     345
     346        # Continue by shrinking the icon size.
     347        radius = _MAXIMUM_RADIUS
     348        distance = radius * (2 * math.pi) / children_count
     349        icon_size = int(distance - style.DEFAULT_SPACING * \
     350            _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)])
     351        if icon_size >= style.STANDARD_ICON_SIZE:
     352            return radius, icon_size
     353
     354        # Finally, switch to a spiral.
     355        self._spiral_mode = True
     356        icon_size = style.STANDARD_ICON_SIZE
     357        angle, radius = self._calculate_angle_and_radius(children_count,
     358                                                         icon_size)
     359        while radius > _MAXIMUM_RADIUS:
     360            i = _ICON_SIZES.index(icon_size)
     361            if i < len(_ICON_SIZES) - 1:
     362                icon_size = _ICON_SIZES[i + 1]
     363                angle, radius = self._calculate_angle_and_radius(
     364                    children_count, icon_size)
     365            else:
     366                break
     367        return radius, icon_size
     368
     369    def _calculate_position(self, radius, icon_size, icon_index, children_count,
     370                            sin=math.sin, cos=math.cos):
     371        """ Calculate an icon position on a circle or a spiral. """
     372        width, height = self.box.get_allocation()
     373        if self._spiral_mode:
     374            min_width_, box_width = self.box.get_width_request()
     375            min_height_, box_height = self.box.get_height_request(box_width)
     376            angle, radius = self._calculate_angle_and_radius(icon_index,
     377                                                             icon_size)
     378            x, y = self._convert_from_polar_to_cartesian(angle, radius,
     379                                                         icon_size,
     380                                                         width, height)
     381        else:
     382            angle = icon_index * (2 * math.pi / children_count) - math.pi / 2
     383            x = radius * cos(angle) + (width - icon_size) / 2
     384            y = radius * sin(angle) + (height - icon_size - \
     385                                       (style.GRID_CELL_SIZE / 2)) / 2
     386        return x, y
     387
     388    def _convert_from_polar_to_cartesian(self, angle, radius, icon_size, width,
     389                                         height):
     390        """ Convert angle, radius to x, y """
     391        x = int(math.sin(angle) * radius)
     392        y = int(math.cos(angle) * radius)
     393        x = - x + (width - icon_size) / 2
     394        y = y + (height - icon_size - (style.GRID_CELL_SIZE / 2)) / 2
     395        return x, y
     396
     397    def _calculate_angle_and_radius(self, icon_count, icon_size):
     398        """ Based on icon_count and icon_size, calculate radius and angle. """
     399        spiral_spacing = _SPIRAL_SPACING_FACTORS[_ICON_SIZES.index(icon_size)]
     400        icon_spacing = icon_size + style.DEFAULT_SPACING * \
     401            _ICON_SPACING_FACTORS[_ICON_SIZES.index(icon_size)]
     402        angle = _INITIAL_ANGLE
     403        radius = _MINIMUM_RADIUS - (icon_size * _MIMIMUM_RADIUS_ENCROACHMENT)
     404        for i in range(icon_count):
     405            circumference = radius * 2 * math.pi
     406            n = circumference / icon_spacing
     407            angle += (2 * math.pi / n)
     408            radius += (float(icon_spacing) * spiral_spacing / n)
     409        return angle, radius
     410
     411
    315412_SUNFLOWER_CONSTANT = style.STANDARD_ICON_SIZE * .75
    316413"""Chose a constant such that STANDARD_ICON_SIZE icons are nicely spaced."""
    317414
    This is the golden angle: http://en.wikipedia.org/wiki/Golden_angle 
    337434Calculation: math.radians(360) / ( _GOLDEN_RATIO * _GOLDEN_RATIO )
    338435"""
    339436
    340 class SunflowerLayout(RingLayout):
     437class SunflowerLayout(BasicRingLayout):
    341438    """Spiral layout based on Fibonacci ratio in phyllotaxis.
    342439
    343440    See http://algorithmicbotany.org/papers/abop/abop-ch4.pdf
    class SunflowerLayout(RingLayout): 
    356453    """String used to identify this layout in home view dropdown palette."""
    357454
    358455    def __init__(self):
    359         RingLayout.__init__(self)
     456        BasicRingLayout.__init__(self)
    360457        self.skipped_indices = []
    361458
    362459    def _calculate_radius_and_icon_size(self, children_count):
    class SunflowerLayout(RingLayout): 
    407504
    408505            return x, y
    409506
    410 class BoxLayout(RingLayout):
     507class BoxLayout(BasicRingLayout):
    411508    """Lay out icons in a square around the XO man."""
    412509
    413510    __gtype_name__ = 'BoxLayout'
    class BoxLayout(RingLayout): 
    423520    """String used to identify this layout in home view dropdown palette."""
    424521
    425522    def __init__(self):
    426         RingLayout.__init__(self)
     523        BasicRingLayout.__init__(self)
    427524
    428525    def _calculate_position(self, radius, icon_size, index, children_count,
    429526                            sin=None, cos=None):
    class BoxLayout(RingLayout): 
    444541        cos = lambda r: cos_d(math.degrees(r))
    445542        sin = lambda r: cos_d(math.degrees(r) - 90)
    446543
    447         return RingLayout._calculate_position\
     544        return BasicRingLayout._calculate_position\
    448545               (self, radius, icon_size, index, children_count,
    449546                sin=sin, cos=cos)
    450547
    451 class TriangleLayout(RingLayout):
     548class TriangleLayout(BasicRingLayout):
    452549    """Lay out icons in a triangle around the XO man."""
    453550
    454551    __gtype_name__ = 'TriangleLayout'
    class TriangleLayout(RingLayout): 
    464561    """String used to identify this layout in home view dropdown palette."""
    465562
    466563    def __init__(self):
    467         RingLayout.__init__(self)
     564        BasicRingLayout.__init__(self)
    468565
    469566    def _calculate_radius_and_icon_size(self, children_count):
    470567        # use slightly larger minimum radius than parent, because sides
    471568        # of triangle come awful close to the center.
    472569        radius, icon_size = \
    473             RingLayout._calculate_radius_and_icon_size(self, children_count)
     570            BasicRingLayout._calculate_radius_and_icon_size(self,
     571                                                            children_count)
    474572        return max(radius, _MINIMUM_RADIUS + style.MEDIUM_ICON_SIZE), icon_size
    475573
    476574    def _calculate_position(self, radius, icon_size, index, children_count,
    class TriangleLayout(RingLayout): 
    501599        cos = lambda r: cos_d(math.degrees(r))
    502600        sin = lambda r: sin_d(math.degrees(r))
    503601
    504         return RingLayout._calculate_position\
     602        return BasicRingLayout._calculate_position\
    505603               (self, radius, icon_size, index, children_count,
    506604                sin=sin, cos=cos)