Ticket #9076: trac_9076-arc.patch

File trac_9076-arc.patch, 13.5 KB (added by vdelecroix, 3 years ago)
  • doc/en/reference/plotting.rst

    # HG changeset patch
    # User Vincent Delecroix <20100.delecroix at gmail.com>
    # Date 1277560082 -7200
    # Node ID 7d34dff8e7be1dfd93032f937f5e94eedf25f307
    # Parent  dd1749111b83de52f0faad31127d39dbdd0d3229
    [mq]: trac_9076-arc-vd.patch
    
    diff -r dd1749111b83 -r 7d34dff8e7be doc/en/reference/plotting.rst
    a b  
    66 
    77   sage/plot/plot 
    88   sage/plot/animate 
     9   sage/plot/arc 
    910   sage/plot/arrow 
    1011   sage/plot/bar_chart 
    1112   sage/plot/bezier_path 
  • sage/plot/all.py

    diff -r dd1749111b83 -r 7d34dff8e7be sage/plot/all.py
    a b  
    2020from contour_plot import contour_plot, implicit_plot, region_plot 
    2121from density_plot import density_plot 
    2222from complex_plot import complex_plot 
     23from arc import arc 
    2324 
    2425from animate import Animation as animate 
    2526 
  • new file sage/plot/arc.py

    diff -r dd1749111b83 -r 7d34dff8e7be sage/plot/arc.py
    - +  
     1""" 
     2Arcs of circle and ellipse 
     3""" 
     4#***************************************************************************** 
     5#       Copyright (C) 2010 Vincent Delecroix <20100.delecroix@gmail.com>, 
     6# 
     7#  Distributed under the terms of the GNU General Public License (GPL) 
     8# 
     9#    This code is distributed in the hope that it will be useful, 
     10#    but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
     12#    General Public License for more details. 
     13# 
     14#  The full text of the GPL is available at: 
     15# 
     16#                  http://www.gnu.org/licenses/ 
     17#***************************************************************************** 
     18from sage.plot.primitive import GraphicPrimitive 
     19from sage.plot.colors import to_mpl_color 
     20 
     21from sage.plot.misc import options, rename_keyword 
     22 
     23from math import fmod, floor, sin, cos, sqrt, tan, pi, atan 
     24 
     25def is_cyclic_ordered(x1,x2,x3): 
     26    return ( 
     27      (x1 < x2 and x2 < x3) or 
     28      (x2 < x3 and x3 < x1) or 
     29      (x3 < x1 and x1 < x2)) 
     30 
     31class Arc(GraphicPrimitive): 
     32    """ 
     33    Primitive class for the Arc graphics type.  See arc? for information 
     34    about actually plotting an arc of a circle or an ellipse. 
     35 
     36    INPUT: 
     37 
     38    - ``x,y`` - coordinates of the center of the arc 
     39 
     40    - ``r1``, ``r2`` - lengths of the two radii 
     41 
     42    - ``angle`` - angle of the horizontal with width 
     43 
     44    - ``sector`` - sector of angle 
     45 
     46    - ``options`` - dict of valid plot options to pass to constructor 
     47 
     48    EXAMPLES: 
     49 
     50    Note that the construction should be done using arc:: 
     51 
     52        sage: from sage.plot.arc import Arc 
     53        sage: print Arc(0,0,1,1,pi/4,pi/4,pi/2,{}) 
     54        Arc with center (0.0,0.0) radii (1.0,1.0) angle 0.785398163397 inside the sector (0.785398163397,1.57079632679) 
     55    """ 
     56    def __init__(self, x, y, r1, r2, angle, s1, s2, options): 
     57        """ 
     58        Initializes base class Arc. 
     59 
     60        EXAMPLES: 
     61         
     62            sage: A = arc((2,3),1,1,pi/4,(0,pi)) 
     63            sage: A[0].x == 2 
     64            True 
     65            sage: A[0].y == 3 
     66            True 
     67            sage: A[0].r1 == 1 
     68            True 
     69            sage: A[0].r2 == 1 
     70            True 
     71            sage: bool(A[0].angle == pi/4) 
     72            True 
     73            sage: bool(A[0].s1 == 0) 
     74            True 
     75            sage: bool(A[0].s2 == pi) 
     76            True 
     77 
     78        TESTS:: 
     79 
     80            sage: from sage.plot.arc import Arc 
     81            sage: a = Arc(0,0,1,1,0,0,1,{}) 
     82            sage: print loads(dumps(a)) 
     83            Arc with center (0.0,0.0) radii (1.0,1.0) angle 0.0 inside the sector (0.0,1.0) 
     84        """ 
     85        self.x = float(x) 
     86        self.y = float(y) 
     87        self.r1 = float(r1) 
     88        self.r2 = float(r2) 
     89        if self.r1 <= 0 or self.r2 <= 0: 
     90            raise ValueError, "the radii must be positive real numbers." 
     91 
     92        self.angle = float(angle) 
     93        self.s1 = float(s1) 
     94        self.s2 = float(s2) 
     95        if self.s2 < self.s1: 
     96            self.s1,self.s2=self.s2,self.s1 
     97        GraphicPrimitive.__init__(self, options)         
     98 
     99    def get_minmax_data(self): 
     100        """ 
     101        Returns a dictionary with the bounding box data. 
     102 
     103        The bounding box is computed as minimal as possible. 
     104     
     105        EXAMPLES: 
     106 
     107        An example without angle:: 
     108 
     109            sage: p = arc((-2, 3), 1, 2) 
     110            sage: d = p.get_minmax_data() 
     111            sage: d['xmin'] 
     112            -3.0 
     113            sage: d['xmax'] 
     114            -1.0 
     115            sage: d['ymin'] 
     116            1.0 
     117            sage: d['ymax'] 
     118            5.0 
     119 
     120        The same example with a rotation of angle pi/2:: 
     121 
     122            sage: p = arc((-2, 3), 1, 2, pi/2) 
     123            sage: d = p.get_minmax_data() 
     124            sage: d['xmin'] 
     125            -4.0 
     126            sage: d['xmax'] 
     127            0.0 
     128            sage: d['ymin'] 
     129            2.0 
     130            sage: d['ymax'] 
     131            4.0 
     132        """ 
     133        from sage.plot.plot import minmax_data 
     134 
     135        twopi = 2*pi 
     136 
     137        s1 = self.s1 
     138        s2 = self.s2 
     139        s = s2-s1 
     140        s1 = fmod(s1,twopi) 
     141        if s1 < 0: s1 += twopi 
     142        s2 = fmod(s1 + s,twopi) 
     143        if s2 < 0: s2 += twopi 
     144 
     145        r1 = self.r1 
     146        r2 = self.r2 
     147 
     148        angle = fmod(self.angle,twopi) 
     149        if angle < 0: angle += twopi 
     150 
     151        epsilon = float(0.0000001) 
     152 
     153        cos_angle = cos(angle) 
     154        sin_angle = sin(angle) 
     155 
     156        if cos_angle > 1-epsilon: 
     157            xmin=-r1; ymin=-r2         
     158            xmax=r1; ymax=r2 
     159            axmin = pi; axmax = 0 
     160            aymin = 3*pi/2; aymax = pi/2 
     161 
     162        elif cos_angle < -1+epsilon: 
     163            xmin=-r1; ymin=-r2         
     164            xmax=r1; ymax=r2 
     165            axmin=0; axmax=pi 
     166            aymin=pi/2; aymax=3*pi/2 
     167             
     168        elif sin_angle > 1-epsilon: 
     169            xmin=-r2; ymin=-r1 
     170            xmax=r2; ymax=r1 
     171            axmin = pi/2; axmax = 3*pi/2 
     172            aymin = pi; aymax = 0 
     173             
     174        elif sin_angle < -1+epsilon: 
     175            xmin=-r2; ymin=-r1 
     176            xmax=r2; ymax=r1 
     177            axmin = 3*pi/2; axmax = pi/2 
     178            aymin = 0; aymax = pi 
     179             
     180        else: 
     181            tan_angle = sin_angle / cos_angle 
     182            axmax = atan(-r2/r1*tan_angle) 
     183            if axmax < 0: axmax += twopi 
     184            xmax = ( 
     185              r1 * cos_angle * cos(axmax) - 
     186              r2 * sin_angle * sin(axmax)) 
     187            if xmax < 0: 
     188                xmax = -xmax 
     189                axmax = fmod(axmax+pi,twopi) 
     190            xmin = -xmax 
     191            axmin = fmod(axmax + pi,twopi) 
     192             
     193            aymax = atan(r2/(r1*tan_angle)) 
     194            if aymax < 0: aymax += twopi 
     195            ymax = ( 
     196              r1 * sin_angle * cos(aymax) + 
     197              r2 * cos_angle * sin(aymax)) 
     198            if ymax < 0: 
     199                ymax = -ymax 
     200                aymax = fmod(aymax+pi,twopi)             
     201            ymin = -ymax 
     202            aymin = fmod(aymax + pi, twopi) 
     203 
     204        if s < twopi-epsilon: # bb determined by the sector 
     205            x1 = cos_angle*r1*cos(s1) - sin_angle*r2*sin(s1) 
     206            x2 = cos_angle*r1*cos(s2) - sin_angle*r2*sin(s2) 
     207            y1 = sin_angle*r1*cos(s1) + cos_angle*r2*sin(s1) 
     208            y2 = sin_angle*r1*cos(s2) + cos_angle*r2*sin(s2) 
     209             
     210            if is_cyclic_ordered(s1,s2,axmin): xmin = min(x1,x2) 
     211            if is_cyclic_ordered(s1,s2,aymin): ymin = min(y1,y2) 
     212            if is_cyclic_ordered(s1,s2,axmax): xmax = max(x1,x2) 
     213            if is_cyclic_ordered(s1,s2,aymax): ymax = max(y1,y2) 
     214 
     215        return minmax_data([self.x + xmin, self.x + xmax], 
     216                           [self.y + ymin, self.y + ymax], 
     217                           dict=True) 
     218 
     219    def _allowed_options(self): 
     220        """ 
     221        Return the allowed options for the Arc class. 
     222 
     223        EXAMPLES:: 
     224 
     225            sage: p = arc((3, 3), 1, 1) 
     226            sage: p[0]._allowed_options()['alpha'] 
     227            'How transparent the figure is.' 
     228        """ 
     229        return {'alpha':'How transparent the figure is.', 
     230                'thickness':'How thick the border of the arc is.', 
     231                'hue':'The color given as a hue.', 
     232                'rgbcolor':'The color', 
     233                'zorder':'2D only: The layer level in which to draw', 
     234                'linestyle':"2D only: The style of the line, which is one of 'dashed', 'dotted', 'solid', 'dashdot'."} 
     235 
     236    def _repr_(self): 
     237        """ 
     238        String representation of Arc primitive. 
     239 
     240        EXAMPLES:: 
     241 
     242            sage: from sage.plot.arc import Arc 
     243            sage: print Arc(2,3,2.2,2.2,0,2,3,{}) 
     244            Arc with center (2.0,3.0) radii (2.2,2.2) angle 0.0 inside the sector (2.0,3.0) 
     245        """ 
     246        return "Arc with center (%s,%s) radii (%s,%s) angle %s inside the sector (%s,%s)" %(self.x,self.y,self.r1,self.r2,self.angle,self.s1,self.s2) 
     247 
     248    def _render_on_subplot(self, subplot): 
     249        """ 
     250        TESTS:: 
     251 
     252            sage: A = arc((1,1),3,4,pi/4,(pi,4*pi/3)); A 
     253        """ 
     254        import matplotlib.patches as patches 
     255 
     256        options = self.options() 
     257 
     258        p = patches.Arc( 
     259                (self.x,self.y), 
     260                2.*self.r1, 
     261                2.*self.r2, 
     262                fmod(self.angle,2*pi)*(180./pi), 
     263                self.s1*(180./pi), 
     264                self.s2*(180./pi)) 
     265        p.set_linewidth(float(options['thickness'])) 
     266        a = float(options['alpha']) 
     267        p.set_alpha(a) 
     268        z = int(options.pop('zorder',1)) 
     269        p.set_zorder(z) 
     270        c = to_mpl_color(options['rgbcolor']) 
     271        p.set_linestyle(options['linestyle']) 
     272        p.set_edgecolor(c) 
     273        subplot.add_patch(p) 
     274 
     275    def plot3d(self): 
     276        r""" 
     277        TESTS: 
     278 
     279            sage: from sage.plot.arc import Arc 
     280            sage: Arc(0,0,1,1,0,0,1,{}).plot3d() 
     281            Traceback (most recent call last): 
     282            ... 
     283            NotImplementedError 
     284        """ 
     285        raise NotImplementedError 
     286 
     287@rename_keyword(color='rgbcolor') 
     288@options(alpha=1, thickness=1, linestyle='solid', zorder=5,rgbcolor='blue') 
     289def arc(center, r1, r2=None, angle=0.0, sector=(0.0,2*pi), **options): 
     290    r""" 
     291    An arc (that is a portion of a circle or an ellipse) 
     292 
     293    Type ``arc.options`` to see all options. 
     294 
     295    INPUT: 
     296 
     297    - ``center`` - 2-tuple of real numbers - position of the center. 
     298 
     299    - ``r1``, ``r2`` - positive real numbers - radii of the ellipse. If only r1 is set 
     300      then the two radii are supposed to be equal and this function returns an 
     301      arc of of circle. 
     302 
     303    - ``angle`` - real number - angle between the horizontal and the axis that 
     304      corresponds to r1. 
     305     
     306    - ``sector`` - 2-tuple (default: (0,2*pi))- angles sector in which the arc will 
     307      be drawn. 
     308 
     309    OPTIONS: 
     310 
     311    - ``alpha`` - float (default: 1) - transparency 
     312 
     313    - ``thickness`` - float (default: 1) - thickness of the arc 
     314 
     315    - ``color``, ``rgbcolor`` - string or 2-tuple (default: 'blue') - the color 
     316      of the arc 
     317 
     318    - ``linestyle`` - string (default: 'solid') - the style of the line 
     319 
     320    EXAMPLES: 
     321 
     322    Plot an arc of circle centered at (0,0) with radius 1 in the sector 
     323    (pi/4,3*pi/4):: 
     324 
     325        sage: arc((0,0), 1, sector=(pi/4,3*pi/4)) 
     326 
     327    Plot an arc of an ellipse between the angles 0 and pi/2:: 
     328 
     329        sage: arc((2,3), 2, 1, sector=(0,pi/2)) 
     330 
     331    Plot an arc of a rotated ellipse between the angles 0 and pi/2:: 
     332 
     333        sage: arc((2,3), 2, 1, angle=pi/5, sector=(0,pi/2)) 
     334 
     335    Plot an arc of an ellipse in red with a dashed linestyle:: 
     336 
     337        sage: arc((0,0), 2, 1, 0, (0,pi/2), linestyle="dashed", color="red") 
     338 
     339    It is not possible to draw ellipses in 3-D:: 
     340 
     341        sage: A = arc((0,0,0), 1) 
     342        Traceback (most recent call last): 
     343        ... 
     344        NotImplementedError 
     345    """ 
     346    from sage.plot.plot import Graphics 
     347    if len(center)==2: 
     348        if r2 is None: r2 = r1 
     349        g = Graphics() 
     350        g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) 
     351        if len(sector) != 2: 
     352            raise ValueError, "the sector must consist of two angles" 
     353        g.add_primitive(Arc( 
     354            center[0],center[1], 
     355            r1,r2, 
     356            angle, 
     357            sector[0],sector[1], 
     358            options)) 
     359        return g 
     360    elif len(center)==3: 
     361        raise NotImplementedError 
     362 
  • sage/plot/disk.py

    diff -r dd1749111b83 -r 7d34dff8e7be sage/plot/disk.py
    a b  
    145145        """ 
    146146        import matplotlib.patches as patches         
    147147        options = self.options() 
    148         deg1 = self.rad1*(360.0/(2.0*pi)) #convert radians to degrees  
    149         deg2 = self.rad2*(360.0/(2.0*pi)) 
     148        deg1 = self.rad1*(180./pi) #convert radians to degrees  
     149        deg2 = self.rad2*(180./pi) 
    150150        z = int(options.pop('zorder', 0)) 
    151151        p = patches.Wedge((float(self.x), float(self.y)), float(self.r), float(deg1), 
    152152                            float(deg2), zorder=z) 
  • sage/plot/plot.py

    diff -r dd1749111b83 -r 7d34dff8e7be sage/plot/plot.py
    a b  
    1111 
    1212-  :func:`circle() <sage.plot.circle.circle>` - a circle with given radius 
    1313 
     14-  :func:`arc() <sage.plot.arc.arc>` - an arc of a circle or an ellipse 
     15 
    1416-  :func:`disk() <sage.plot.disk.disk>` - a filled disk (i.e. a sector or wedge of a circle) 
    1517 
    1618-  :func:`line() <sage.plot.line.line>` - a line determined by a sequence of points (this need not