Attachments you submit will be routed for moderation. If you have an account, please log in first.

Ticket #2235: 0001-a-more-succint-approach-to-the-spiral-morph.patch

File 0001-a-more-succint-approach-to-the-spiral-morph.patch, 12.7 KB (added by walter, 3 years ago)

the more succint version which begins with a revert back to the 0.88 version

  • src/jarabe/desktop/favoriteslayout.py

    From 3bd63f6ff461b96743829d8fe62731000b9b74ed Mon Sep 17 00:00:00 2001
    From: Walter Bender <walter@sugarlabs.org>
    Date: Wed, 13 Oct 2010 12:41:39 -0400
    Subject: [PATCH] a more succint approach to the spiral morph
    
    ---
     src/jarabe/desktop/favoriteslayout.py |  182 +++++++++++++++++++++++++++------
     1 files changed, 149 insertions(+), 33 deletions(-)
    
    diff --git a/src/jarabe/desktop/favoriteslayout.py b/src/jarabe/desktop/favoriteslayout.py
    index 85e1b59..8bc0f1f 100644
    a b  
    11# Copyright (C) 2008 One Laptop Per Child 
     2# Copyright (C) 2010 Sugar Labs 
    23# 
    34# This program is free software; you can redistribute it and/or modify 
    45# it under the terms of the GNU General Public License as published by 
     
    181182    def allow_dnd(self): 
    182183        return True 
    183184 
     185 
    184186_MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING + \ 
    185187        style.STANDARD_ICON_SIZE * 2 
    186188_MAXIMUM_RADIUS = (gtk.gdk.screen_height() - style.GRID_CELL_SIZE) / 2 - \ 
    187189        style.STANDARD_ICON_SIZE - style.DEFAULT_SPACING 
    188  
    189 class RingLayout(FavoritesLayout): 
     190_INTERMEDIATE_C = (style.STANDARD_ICON_SIZE + style.SMALL_ICON_SIZE) / 2 
     191_INTERMEDIATE_A = (style.STANDARD_ICON_SIZE * 2 + _INTERMEDIATE_C) / 3 
     192_INTERMEDIATE_E = (_INTERMEDIATE_C + style.SMALL_ICON_SIZE * 2) / 3 
     193_INTERMEDIATE_B = (_INTERMEDIATE_A + _INTERMEDIATE_C) / 2 
     194_INTERMEDIATE_D = (_INTERMEDIATE_C + _INTERMEDIATE_E) / 2 
     195_ICON_SIZES = [style.MEDIUM_ICON_SIZE, style.STANDARD_ICON_SIZE, 
     196               _INTERMEDIATE_A, _INTERMEDIATE_B, _INTERMEDIATE_C, 
     197               _INTERMEDIATE_D, _INTERMEDIATE_E, style.SMALL_ICON_SIZE] 
     198_ICON_SPACING_FACTORS = [1.5, 1.4, 1.3, 1.2, 1.15, 1.1, 1.05, 1.0] 
     199 
     200 
     201class BasicRingLayout(FavoritesLayout): 
    190202    """Lay out icons in a ring around the XO man.""" 
    191203 
    192     __gtype_name__ = 'RingLayout' 
     204    __gtype_name__ = 'BasicRingLayout' 
    193205    icon_name = 'view-radial' 
    194206    """Name of icon used in home view dropdown palette.""" 
    195     key = 'ring-layout' 
     207    key = 'basic-ring-layout' 
    196208    """String used in profile to represent this view.""" 
    197209    # TRANS: label for the ring layout in the favorites view 
    198210    palette_name = _('Ring') 
     
    221233            self._locked_children[child] = (x, y) 
    222234 
    223235    def _calculate_radius_and_icon_size(self, children_count): 
    224         # what's the radius required without downscaling? 
    225         distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING 
    226         icon_size = style.STANDARD_ICON_SIZE 
    227         # circumference is 2*pi*r; we want this to be at least 
    228         # 'children_count * distance' 
    229         radius = children_count * distance / (2 * math.pi) 
    230         # limit computed radius to reasonable bounds. 
    231         radius = max(radius, _MINIMUM_RADIUS) 
    232         radius = min(radius, _MAXIMUM_RADIUS) 
    233         # recompute icon size from limited radius 
    234         if children_count > 0: 
    235             icon_size = (2 * math.pi * radius / children_count) \ 
    236                         - style.DEFAULT_SPACING 
    237         # limit adjusted icon size. 
    238         icon_size = max(icon_size, style.SMALL_ICON_SIZE) 
    239         icon_size = min(icon_size, style.MEDIUM_ICON_SIZE) 
     236        """ Adjust the ring radius and icon size as needed. """ 
     237        # Begin by increasing the radius. 
     238        distance = style.MEDIUM_ICON_SIZE + style.DEFAULT_SPACING * \ 
     239            _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.MEDIUM_ICON_SIZE)] 
     240        radius = max(children_count * distance / (2 * math.pi), _MINIMUM_RADIUS) 
     241        if radius < _MAXIMUM_RADIUS: 
     242            return radius, style.MEDIUM_ICON_SIZE 
     243 
     244        # Continue by shrinking the icon size to STANDARD_ICON_SIZE. 
     245        radius = _MAXIMUM_RADIUS 
     246        distance = radius * (2 * math.pi) / children_count 
     247        icon_size = int(distance - style.DEFAULT_SPACING * \ 
     248            _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)]) 
     249        if icon_size >= style.STANDARD_ICON_SIZE: 
     250            return radius, icon_size 
     251 
     252        # Continue by shrinking the icon size to SMALL_ICON_SIZE. 
     253        icon_size = max(int(distance - style.DEFAULT_SPACING * \ 
     254                            _ICON_SPACING_FACTORS[_ICON_SIZES.index( 
     255                    style.SMALL_ICON_SIZE)]), style.SMALL_ICON_SIZE) 
    240256        return radius, icon_size 
    241257 
    242     def _calculate_position(self, radius, icon_size, index, children_count, 
     258    def _calculate_position(self, radius, icon_size, icon_index, children_count, 
    243259                            sin=math.sin, cos=math.cos): 
     260        """ Calculate an icon position on a circle. """ 
    244261        width, height = self.box.get_allocation() 
    245         angle = index * (2 * math.pi / children_count) - math.pi / 2 
     262        angle = icon_index * (2 * math.pi / children_count) - math.pi / 2 
    246263        x = radius * cos(angle) + (width - icon_size) / 2 
    247         y = radius * sin(angle) + (height - icon_size - 
    248                                    (style.GRID_CELL_SIZE/2) ) / 2 
     264        y = radius * sin(angle) + (height - icon_size - \ 
     265                                       (style.GRID_CELL_SIZE / 2)) / 2 
    249266        return x, y 
    250267 
    251268    def _get_children_in_ring(self): 
     
    294311        else: 
    295312            return 0 
    296313 
     314 
     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 
    297412_SUNFLOWER_CONSTANT = style.STANDARD_ICON_SIZE * .75 
    298413"""Chose a constant such that STANDARD_ICON_SIZE icons are nicely spaced.""" 
    299414 
     
    319434Calculation: math.radians(360) / ( _GOLDEN_RATIO * _GOLDEN_RATIO ) 
    320435""" 
    321436 
    322 class SunflowerLayout(RingLayout): 
     437class SunflowerLayout(BasicRingLayout): 
    323438    """Spiral layout based on Fibonacci ratio in phyllotaxis. 
    324439 
    325440    See http://algorithmicbotany.org/papers/abop/abop-ch4.pdf 
     
    338453    """String used to identify this layout in home view dropdown palette.""" 
    339454 
    340455    def __init__(self): 
    341         RingLayout.__init__(self) 
     456        BasicRingLayout.__init__(self) 
    342457        self.skipped_indices = [] 
    343458 
    344459    def _calculate_radius_and_icon_size(self, children_count): 
     
    389504 
    390505            return x, y 
    391506 
    392 class BoxLayout(RingLayout): 
     507class BoxLayout(BasicRingLayout): 
    393508    """Lay out icons in a square around the XO man.""" 
    394509 
    395510    __gtype_name__ = 'BoxLayout' 
     
    405520    """String used to identify this layout in home view dropdown palette.""" 
    406521 
    407522    def __init__(self): 
    408         RingLayout.__init__(self) 
     523        BasicRingLayout.__init__(self) 
    409524 
    410525    def _calculate_position(self, radius, icon_size, index, children_count, 
    411526                            sin=None, cos=None): 
     
    426541        cos = lambda r: cos_d(math.degrees(r)) 
    427542        sin = lambda r: cos_d(math.degrees(r) - 90) 
    428543 
    429         return RingLayout._calculate_position\ 
     544        return BasicRingLayout._calculate_position\ 
    430545               (self, radius, icon_size, index, children_count, 
    431546                sin=sin, cos=cos) 
    432547 
    433 class TriangleLayout(RingLayout): 
     548class TriangleLayout(BasicRingLayout): 
    434549    """Lay out icons in a triangle around the XO man.""" 
    435550 
    436551    __gtype_name__ = 'TriangleLayout' 
     
    446561    """String used to identify this layout in home view dropdown palette.""" 
    447562 
    448563    def __init__(self): 
    449         RingLayout.__init__(self) 
     564        BasicRingLayout.__init__(self) 
    450565 
    451566    def _calculate_radius_and_icon_size(self, children_count): 
    452567        # use slightly larger minimum radius than parent, because sides 
    453568        # of triangle come awful close to the center. 
    454569        radius, icon_size = \ 
    455             RingLayout._calculate_radius_and_icon_size(self, children_count) 
     570            BasicRingLayout._calculate_radius_and_icon_size(self, 
     571                                                            children_count) 
    456572        return max(radius, _MINIMUM_RADIUS + style.MEDIUM_ICON_SIZE), icon_size 
    457573 
    458574    def _calculate_position(self, radius, icon_size, index, children_count, 
     
    483599        cos = lambda r: cos_d(math.degrees(r)) 
    484600        sin = lambda r: sin_d(math.degrees(r)) 
    485601 
    486         return RingLayout._calculate_position\ 
     602        return BasicRingLayout._calculate_position\ 
    487603               (self, radius, icon_size, index, children_count, 
    488604                sin=sin, cos=cos)