Ticket #6893: plot.patch

File plot.patch, 68.5 KB (added by jason, 11 years ago)
  • sage/plot/axes.py

    # HG changeset patch
    # User Mike Hansen <mhansen@gmail.com>
    # Date 1234152882 28800
    # Node ID bdf61c76a98ecc6fb2d1bf35072ecec6db76fdc1
    # Parent  af0cd051387b3bbee33ba32d3e2aadaedc5d6b5c
    * * *
    
    diff -r af0cd051387b -r bdf61c76a98e sage/plot/axes.py
    a b  
    2424#
    2525#                  http://www.gnu.org/licenses/
    2626#*****************************************************************************
    27 from math import floor, log
    28 from sage.structure.sage_object import SageObject
    29 import sage.misc.misc
    30 from copy import copy
    31 
    32 class Axes(SageObject):
    33     """
    34     Axes for SAGE 2D Graphics.
    35 
    36     Set all axis properties and then add one of
    37     the following axes to the current (matplotlib) subplot:
    38     add_xy__axes
    39     add_xy_frame_axes
    40     add_xy_matrix_frame_axes
    41 
    42     """
    43     def __init__(self, color=(0,0,0), fontsize=8, linewidth=0.6,axes_labels=None,
    44                  axes_label_color=(0,0,0), tick_label_color=(0,0,0)):
    45         self.__color = color
    46         self.__tick_label_color = tick_label_color
    47         self.__axes_labels = axes_labels
    48         self.__axes_label_color = axes_label_color
    49         self.__fontsize = fontsize
    50         self.__linewidth = linewidth
    51         self.__draw_x_axis = True
    52         self.__draw_y_axis = True
    53        
    54     def _tasteful_ticks(self, minval, maxval):
    55         """
    56         This function finds spacing for axes tick marks that are well spaced.
    57         Were 'well spaced' means for any given min and max values
    58         the tick spacing should look even and visually nice (tasteful).
    59        
    60         """
    61         minval, maxval = float(minval), float(maxval)
    62         absmin, absmax = abs(minval), abs(maxval)
    63         # Initialize the domain flags:
    64         onlyneg, domneg, onlypos, dompos = False, False, False, False
    65 
    66         # Is the range: *only or dominantly* and  *negative or positive*?
    67         if absmin > absmax:
    68             if maxval < 0:
    69                 onlyneg = True
    70             else:
    71                 domneg  = True
    72 
    73             # Is the stepsize going to be < 1?
    74             if absmin < 1:
    75                 n = 0
    76                 s = str(absmin).split('.')[1]
    77                 for c in s:
    78                     n+=1
    79                     if c != '0':
    80                         break
    81                 p = -(n-1)
    82                 d0 = eval(s[n-1])
    83                 #string may be of length 1
    84                 try:
    85                     d1 = eval(s[n])
    86                 except IndexError:
    87                     d1 = 0
    88 
    89             #the stepsize will be 1 or greater:
    90             else:
    91                 if absmin >= 10:
    92                     sl = [s for s in str(int(absmin))]
    93                     d0 = eval(sl[0])
    94                     d1 = eval(sl[1])
    95                     p = len(sl)
    96                 else:
    97                     sl = str(absmin).split('.')
    98                     d0 = eval(sl[0])
    99                     d1 = eval(sl[1][0])
    100                     p = 1
    101 
    102         else: #this means: abs(minval) < abs(maxval)
    103             if minval > 0:
    104                  onlypos = True
    105             else:
    106                  dompos = True
    107             #is the stepsize going to be < 1?
    108             if absmax < 1:
    109                 n = 0
    110                 s = str(absmax).split('.')[1]
    111                 for c in s:
    112                     n+=1
    113                     if c != '0':
    114                         break
    115                 p = -(n-1)
    116                 d0 = eval(s[n-1])
    117                 try:
    118                     sn = s[n]
    119                 except IndexError:
    120                     sn = "0"
    121                 d1 = eval(sn)
    122             #the stepsize will be 1 or greater:
    123             else:
    124                 if maxval >= 10:
    125                     sl = [s for s in str(int(absmax))]
    126                     d0 = eval(sl[0])
    127                     d1 = eval(sl[1])
    128                     p = len(sl)
    129                 else:
    130                     sl = str(absmax).split('.')
    131                     d0 = eval(sl[0])
    132                     d1 = eval(sl[1][0])
    133                     p = 1
    134        
    135         #choose a step size depending on either
    136         #1st or 2nd digits, d0 and d1, of given maxval or minval
    137         o0 = 10**(p-1)
    138         o1 = 10**(p-2)
    139         #fundamental step sizes: [1,2,2.5,5,10]
    140         # 'fundamental' means that we split the x and y
    141         # ranges into widths from the above step sizes
    142         if d0 == 1:
    143             if d1 > 5 and p!=1:
    144                 funda = 2.5
    145                 step = funda*o1
    146             elif d1 < 5 and p==1:
    147                 funda = 2.5
    148                 step = funda*o1
    149             elif d1 < 5 and p>1:
    150                 funda = 2.5
    151                 step = funda*o1
    152             else:
    153                 funda = 5
    154                 step = funda*o1
    155         elif d0 == 2 or (d0 == 3 and p > 1):
    156             funda = 5
    157             step = funda*o1
    158         elif d0 in [3, 4, 5, 6]:
    159             funda = 1
    160             step = funda*o0
    161         else:
    162             funda = 2
    163             step = funda*o0
    164 
    165         #the 'fundamental' range
    166         fundrange = sage.misc.misc.srange(0, d0*o0 + d1*o1 + step, step)
    167        
    168         #Now find the tick step list for major ticks (tslmajor)
    169         #'oppaxis' is the positioning value of the other axis.
    170         if onlyneg:
    171             tslmajor = self._in_range([-x for x in fundrange], minval, maxval)
    172             tslmajor.sort()
    173             oppaxis = tslmajor[-1]
    174 
    175         elif domneg or dompos:
    176             tslmajor = self._in_range([-x for x in fundrange] + fundrange, minval, maxval)
    177             tslmajor.sort()
    178             oppaxis = 0
    179 
    180         else: #onlypos
    181             tslmajor = self._in_range(fundrange, minval, maxval)
    182             tslmajor.sort()
    183             oppaxis = tslmajor[0]
    184        
    185         return tslmajor, oppaxis, step
    186 
    187     def _in_range(self, v, minval, maxval):
    188         "find axis values set in a given range"
    189         return list(set([x for x in v if x >= minval and x <= maxval]))
    190 
    191     def _trunc(self, x, digits_before_the_decimal):
    192         s = '%f'%float(x)
    193         i = s.find('.')
    194         t = s[:i - digits_before_the_decimal]
    195         if digits_before_the_decimal > 0:
    196             t += '0'* digits_before_the_decimal
    197         return float(eval(t))
    198        
    199     def _format_tick_string(self, s):
    200         s = str(s)
    201         if s[-2:] == '.0':
    202             return s[:-2]
    203         return s
    204 
    205     def _tasteless_ticks(self, minval, maxval, num_pieces):
    206         minval0 = minval
    207         maxval0 = maxval
    208         rnd   = int(floor(log(maxval - minval)/log(10)))
    209         if rnd < 0:
    210             rnd -= 1
    211 
    212         step  = (maxval - minval)/float(num_pieces)
    213         minval = self._trunc(minval, rnd)
    214         maxval = self._trunc(maxval + step, rnd)
    215 
    216         step  = (maxval - minval)/float(num_pieces)
    217         tslmajor = sage.misc.misc.srange(minval, minval+(num_pieces+1)*step, step)
    218         tslmajor = self._in_range(tslmajor, minval0, maxval0)
    219 
    220         oppaxis = 0
    221         if maxval <= 0:  # only negative
    222             oppaxis = tslmajor[-1]
    223         elif minval >= 0:
    224             oppaxis = tslmajor[0]
    225            
    226         return tslmajor, oppaxis, step
    227 
    228     def _find_axes(self, minval, maxval):
    229         """
    230         Try to find axis tick positions that are well spaced
    231         """
    232         if minval >= maxval:
    233             raise ValueError, "maxval >= minval is required"
    234 
    235         # If there is a small differences between max and min values
    236         # compared to the size of the largest of (abs(maxval), abs(minval))
    237         # the function 'tasteless_ticks' is used, which in common usage is rare.
    238         if (abs((maxval - minval)/float(max(abs(maxval),abs(minval)))) < 0.2):
    239             tslmajor, oppaxis, step = self._tasteless_ticks(minval, maxval, 10)
    240         else:
    241             tslmajor, oppaxis, step = self._tasteful_ticks(minval, maxval)
    242         min = tslmajor[0] - step
    243         tslminor = sage.misc.misc.srange(min, maxval + 0.2*step, 0.2*step)
    244         tslminor = self._in_range(tslminor, minval, maxval)
    245         return oppaxis, step, tslminor, tslmajor
    246 
    247     def _draw_axes(self, subplot, axes, xmin, xmax, ymin, ymax, x_axis_ypos, y_axis_xpos):
    248         from matplotlib import lines
    249         if isinstance(axes, (list, tuple)) and len(axes) == 2 and \
    250         (axes[0] in [True, False]) and (axes[1] in [True, False]):
    251             self.__draw_x_axis = axes[0]
    252             self.__draw_y_axis = axes[1]
    253             #draw the x-axes?
    254             if self.__draw_x_axis:
    255                 subplot.add_line(lines.Line2D([xmin, xmax], [x_axis_ypos, x_axis_ypos],
    256                                  color=self.__color, linewidth=float(self.__linewidth)))
    257             #draw y axis line?
    258             if self.__draw_y_axis:
    259                 subplot.add_line(lines.Line2D([y_axis_xpos, y_axis_xpos],[ymin, ymax],
    260                                   color=self.__color, linewidth=float(self.__linewidth)))
    261         else: #draw them both
    262             subplot.add_line(lines.Line2D([xmin, xmax], [x_axis_ypos, x_axis_ypos],
    263                              color=self.__color, linewidth=float(self.__linewidth)))
    264             subplot.add_line(lines.Line2D([y_axis_xpos, y_axis_xpos],[ymin, ymax],
    265                               color=self.__color, linewidth=float(self.__linewidth)))
    266 
    267     def _draw_axes_labels(self, subplot, axes_labels, xmin, xmax, ymin, ymax, xstep, ystep, x_axis_ypos, y_axis_xpos, pad=0.2):
    268         al = axes_labels
    269         if not isinstance(al, (list,tuple)) or len(al) != 2:
    270             raise TypeError, "axes_labels must be a list of two strings."
    271         #draw x-axis label if there is a x-axis:
    272         fontsize = int(self.__fontsize)
    273         if self.__draw_x_axis:
    274             s = str(al[0])
    275             subplot.text(xmax + pad*xstep, x_axis_ypos, s,
    276                          fontsize = fontsize,
    277                          color = self.__axes_label_color, horizontalalignment="left",
    278                          verticalalignment="center", family="monospace")
    279             xmax += 0.0025*(xmax-xmin)*len(s) * fontsize
    280            
    281         #draw y-axis label if there is a y-axis
    282         if self.__draw_y_axis:
    283             subplot.text(y_axis_xpos, ymax + 2*pad*ystep, str(al[1]),
    284                          fontsize = fontsize,
    285                          color = self.__axes_label_color,
    286                          horizontalalignment="left", verticalalignment="center", family="monospace")
    287             ymax += 0.075*(ymax-ymin)
    288         return xmin, xmax, ymin, ymax
    289            
    290 
    291     def add_xy_axes(self, subplot, xmin, xmax, ymin, ymax, axes=True,
    292                     ticks="automatic", axesstyle="automatic", axes_labels=None):
    293         r"""
    294         \code{_add_xy_axes} is used when the 'save' method
    295         of any Graphics object is called.
    296 
    297         Additionally this function uses the function '_find_axes'
    298         from axis.py which attempts to find aesthetically pleasing
    299         tick and label spacing values.
    300 
    301         Some definitons of the parameters:
    302 
    303         y_axis_xpos : "where on the x-axis to draw the y-axis"
    304         xstep : "the spacing between major tick marks"
    305         xtslminor : "x-axis minor tick step list"
    306         xtslmajor : "x-axis major tick step list"
    307         yltheight : "where the top of the major ticks go"
    308         ystheight : "where the top of the minor ticks go"
    309         ylabel : "where the ylabel is drawn"
    310         xlabel : "where the xlabel is drawn"
    311 
    312         """
    313         from matplotlib import lines
    314         xmin = float(xmin); xmax=float(xmax); ymin=float(ymin); ymax=float(ymax)
    315         yspan = ymax - ymin
    316         xspan = xmax - xmin
    317 
    318         #evalute find_axes for x values and y ticks
    319         y_axis_xpos, xstep, xtslminor, xtslmajor = self._find_axes(xmin, xmax)
    320         yltheight = 0.015*xspan     
    321         ystheight = 0.25*yltheight
    322         ylabel = y_axis_xpos - 2*ystheight
    323        
    324         #evalute find_axes for y values and x ticks
    325         x_axis_ypos, ystep, ytslminor, ytslmajor = self._find_axes(ymin, ymax)
    326         xltheight = 0.015*yspan
    327         xstheight = 0.25*xltheight
    328         xlabel = x_axis_ypos - xltheight
    329 
    330         if axes:
    331             self._draw_axes(subplot, axes, xmin, xmax, ymin, ymax, x_axis_ypos, y_axis_xpos)
    332 
    333         #the x-axis ticks and labels
    334         #first draw major tick marks and their corresponding values
    335         for x in xtslmajor:
    336             if x == y_axis_xpos:
    337                 continue
    338             s = self._format_tick_string(x)
    339             subplot.text(x, xlabel, s, fontsize=int(self.__fontsize), horizontalalignment="center",
    340                         color=self.__tick_label_color, verticalalignment="top")
    341             subplot.add_line(lines.Line2D([x, x], [x_axis_ypos, x_axis_ypos + xltheight],
    342                         color=self.__color, linewidth=float(self.__linewidth)))
    343 
    344         #now draw the x-axis minor tick marks
    345         for x in xtslminor:
    346             subplot.add_line(lines.Line2D([x, x], [x_axis_ypos, x_axis_ypos + xstheight],
    347                         color=self.__color, linewidth=float(self.__linewidth)))
    348 
    349         #the y-axis ticks and labels
    350         #first draw major tick marks and their corresponding values
    351         for y in ytslmajor:
    352             if y == x_axis_ypos:
    353                 continue
    354             s = self._format_tick_string(y)
    355             subplot.text(ylabel, y, s, fontsize=int(self.__fontsize), verticalalignment="center",
    356                         color=self.__tick_label_color, horizontalalignment="right")
    357             subplot.add_line(lines.Line2D([y_axis_xpos, y_axis_xpos + yltheight], [y, y],
    358                     color=self.__color, linewidth=float(self.__linewidth)))
    359            
    360         #now draw the x-axis minor tick marks
    361         for y in ytslminor:
    362             subplot.add_line(lines.Line2D([y_axis_xpos, y_axis_xpos + ystheight], [y, y],
    363                 color=self.__color, linewidth=float(self.__linewidth)))
    364 
    365         # now draw the x and y axis labels
    366         if self.__axes_labels:
    367             xmin, xmax, ymin, ymax = self._draw_axes_labels(subplot, self.__axes_labels, xmin, xmax, ymin, ymax, xstep, ystep,
    368                                    x_axis_ypos, y_axis_xpos, pad=0.2)
    369 
    370         return xmin, xmax, ymin, ymax
    371                                  
    372 
    373     def _draw_frame(self, subplot, xmins, xmaxs, ymins, ymaxs):       
    374         """
    375         Draw a frame around a graphic at the given
    376         (scaled out) x and y min and max values.
    377         """
    378         from matplotlib import lines
    379         #border horizontal axis:
    380         #bottom:
    381         subplot.add_line(lines.Line2D([xmins, xmaxs], [ymins, ymins],
    382                                         color=self.__color, linewidth=float(self.__linewidth)))
    383         #top:
    384         subplot.add_line(lines.Line2D([xmins, xmaxs], [ymaxs, ymaxs],
    385                                         color=self.__color, linewidth=float(self.__linewidth)))
    386         #border vertical axis:
    387         #left:
    388         subplot.add_line(lines.Line2D([xmins, xmins], [ymins, ymaxs],
    389                                         color=self.__color, linewidth=float(self.__linewidth)))
    390         #right:
    391         subplot.add_line(lines.Line2D([xmaxs, xmaxs], [ymins, ymaxs],
    392                                         color=self.__color, linewidth=float(self.__linewidth)))
    393 
    394 
    395     def add_xy_frame_axes(self, subplot, xmin, xmax, ymin, ymax,
    396                           axes_with_no_ticks=False, axes_labels=None):
    397         r"""
    398         Draw a frame around the perimeter of a graphic.
    399 
    400         Only major tick marks are drawn on a frame axes.
    401 
    402         If \code{axes_with_no_ticks} is true, then also draw
    403         centered axes with no tick marks.
    404 
    405         """
    406         from matplotlib import lines
    407         xmin = float(xmin); xmax=float(xmax); ymin=float(ymin); ymax=float(ymax)
    408         yspan = ymax - ymin
    409         xspan = xmax - xmin
    410 
    411         #evalute find_axes for x values and y ticks
    412         y_axis_xpos, xstep, xtslminor, xtslmajor = self._find_axes(xmin, xmax)
    413         yltheight = 0.015 * xspan     
    414         ystheight = 0.25  * yltheight
    415         #ylabel    = y_axis_xpos - 2*ystheight
    416         ylabel    = -2*ystheight
    417        
    418         #evalute find_axes for y values and x ticks
    419         x_axis_ypos, ystep, ytslminor, ytslmajor = self._find_axes(ymin, ymax)
    420         xltheight = 0.015 * yspan
    421         xstheight = 0.25  * xltheight
    422         #xlabel    = x_axis_ypos - xltheight
    423         xlabel    = -xltheight
    424 
    425         #scale the axes out from the actual plot
    426         xmins, xmaxs, ymins, ymaxs = self._adjustments_for_frame(xmin, xmax, ymin, ymax)
    427        
    428         #now draw the frame border:
    429         self._draw_frame(subplot, xmins, xmaxs, ymins, ymaxs)
    430 
    431         #these are the centered axes, like in regular plot, but with no ticks
    432         if axes_with_no_ticks:
    433             #the x axis line
    434             subplot.add_line(lines.Line2D([xmins, xmaxs], [x_axis_ypos, x_axis_ypos],
    435                                             color=self.__color, linewidth=float(self.__linewidth)))
    436 
    437             #the y axis line
    438             subplot.add_line(lines.Line2D([y_axis_xpos, y_axis_xpos],[ymins, ymaxs],
    439                                             color=self.__color, linewidth=float(self.__linewidth)))
    440            
    441         #the x-axis ticks and labels
    442         #first draw major tick marks and their corresponding values
    443         for x in xtslmajor:
    444             s = self._format_tick_string(x)
    445             subplot.text(x, xlabel + ymins, s, fontsize=int(self.__fontsize),
    446                          horizontalalignment="center", verticalalignment="top")
    447 
    448         #now draw the x-axis minor tick marks
    449         for x in xtslminor:
    450             subplot.add_line(lines.Line2D([x, x], [ymins, xstheight + ymins],
    451                         color=self.__color, linewidth=float(self.__linewidth)))
    452             subplot.add_line(lines.Line2D([x, x], [ymaxs, ymaxs - xstheight],
    453                         color=self.__color, linewidth=float(self.__linewidth)))
    454 
    455 
    456         #the y-axis ticks and labels
    457         #first draw major tick marks and their corresponding values
    458         for y in ytslmajor:
    459             s = self._format_tick_string(y)
    460             subplot.text(ylabel + xmins, y, s, fontsize=int(self.__fontsize),
    461                          verticalalignment="center", horizontalalignment="right")
    462            
    463         #now draw the x-axis minor tick marks
    464         for y in ytslminor:
    465             subplot.add_line(lines.Line2D([xmins, ystheight + xmins], [y, y],
    466                 color=self.__color, linewidth=float(self.__linewidth)))
    467             subplot.add_line(lines.Line2D([xmaxs, xmaxs - ystheight], [y, y],
    468                 color=self.__color, linewidth=float(self.__linewidth)))
    469 
    470     def _adjustments_for_frame(self, xmin, xmax, ymin, ymax):
    471         r"""
    472         Scale the axes out from the actual plot to accommodate a frame.
    473 
    474         INPUT:
    475             xmin, xmax, ymin, ymax -- numbers
    476 
    477         OUTPUT:
    478             xmin, xmax, ymin, ymax -- numbers
    479 
    480         TESTS:
    481             sage: from sage.plot.axes import Axes
    482             sage: Axes()._adjustments_for_frame(-10,40,10,35)
    483             (-11.0, 41.0, 9.5, 35.5)
    484         """
    485         xmin = float(xmin); xmax=float(xmax); ymin=float(ymin); ymax=float(ymax)
    486         yspan = ymax - ymin
    487         xspan = xmax - xmin
    488         ys = 0.02*yspan
    489         xs = 0.02*xspan
    490         ymin -= ys
    491         ymax += ys
    492         xmin -= xs
    493         xmax += xs
    494         return xmin, xmax, ymin, ymax
    495 
    496     def add_xy_matrix_frame_axes(self, subplot, xmin, xmax, ymin, ymax):
    497         """
    498         Draw a frame around a \code{matrix_plot}.
    499 
    500         The tick marks drawn on the frame correspond to
    501         the ith row and jth column of the matrix.
    502 
    503         """
    504         from matplotlib import lines
    505         xmax = int(xmax)
    506         ymax = int(ymax)
    507 
    508         #evalute find_axes for x values and y ticks
    509         # the > 14 is just for appearance improvement
    510         if xmax > 19:
    511             #get nice tick spacing and assure we get largest point
    512             tl, opax, step = self._tasteful_ticks(0, xmax)
    513             #print tl, opax, step
    514             xtl = self._tasteful_ticks(0, xmax)[0] + [xmax-1]
    515             xrm = [float(x+0.5) for x in xtl]
    516             xtslmajor = [int(n) for n in xtl]
    517         else:
    518             xtl = sage.misc.misc.srange(0, xmax)
    519             xrm = [float(x+0.5) for x in xtl]
    520             xtslmajor = [int(n) for n in xtl]
    521         yltheight = 0.015*xmax     
    522         ystheight = 0.25*yltheight
    523         ylabel = -2*ystheight
    524        
    525         #evalute find_axes for y values and x ticks
    526         # the > 14 is just for appearance improvement
    527         if ymax > 19:
    528             #get nice tick spacing and assure we get largest point
    529             tl, opax, step = self._tasteful_ticks(0, ymax)
    530             yrm = [0.5]+[float(y+0.5+1) for y in tl[1:-1]]+[ymax-0.5]
    531             yrm.reverse()
    532             ytslmajor = [0] + tl[1:-1] + [ymax-1]
    533         else:
    534             ytl = sage.misc.misc.srange(0, ymax)
    535             ytslmajor = [int(n) for n in ytl]
    536             yrm = [float(y+0.5) for y in ytslmajor]
    537             yrm.reverse()
    538         xltheight = 0.015*ymax
    539         xstheight = 0.25*xltheight
    540         xlabel = -xltheight
    541 
    542         #scale the axes out from the actual plot
    543         xs = 0.02*xmax
    544         ys = 0.02*ymax
    545         xmins = -xs
    546         xmaxs = xmax + xs
    547         ymins = -ys
    548         ymaxs = ymax + ys
    549        
    550         #now draw the frame border:
    551         self._draw_frame(subplot, xmins, xmaxs, ymins, ymaxs)
    552 
    553         #the x-axis ticks and labels
    554         #first draw major tick marks and their corresponding values
    555         for xr, x in zip(xrm, xtslmajor):
    556             s = self._format_tick_string(x)
    557             subplot.text(xr, xlabel + ymins, s, fontsize=int(self.__fontsize),
    558                          horizontalalignment="center", verticalalignment="top")
    559             subplot.text(xr, -2*xlabel + ymaxs, s, fontsize=int(self.__fontsize),
    560                          horizontalalignment="center", verticalalignment="top")
    561             subplot.add_line(lines.Line2D([xr, xr], [ymins, xstheight + ymins],
    562                         color=self.__color, linewidth=float(self.__linewidth)))
    563             subplot.add_line(lines.Line2D([xr, xr], [ymaxs, ymaxs - xstheight],
    564                         color=self.__color, linewidth=float(self.__linewidth)))
    565                    
    566         #the y-axis ticks and labels
    567         #first draw major tick marks and their corresponding values
    568         for yr, y in zip(yrm, ytslmajor):
    569             s = self._format_tick_string(y)
    570             subplot.text(ylabel + xmins, yr, s, fontsize=int(self.__fontsize),
    571                          verticalalignment="center", horizontalalignment="right")
    572             subplot.text(-2*ylabel + xmaxs, yr, s, fontsize=int(self.__fontsize),
    573                          verticalalignment="center", horizontalalignment="left")
    574             subplot.add_line(lines.Line2D([xmins, ystheight + xmins], [yr, yr],
    575                 color=self.__color, linewidth=float(self.__linewidth)))
    576             subplot.add_line(lines.Line2D([xmaxs, xmaxs - ystheight], [yr, yr],
    577                 color=self.__color, linewidth=float(self.__linewidth)))
    578 
    579 class GridLines(SageObject):
    580     """
    581     Grid lines for SAGE 2D Graphics.
    582 
    583     See the docstring for Graphics.show for examples.
    584     """
    585     def __init__(self, gridlines=None, gridlinesstyle=None,
    586             vgridlinesstyle=None, hgridlinesstyle=None):
    587         r"""
    588         Add horizontal and vertical grid lines to a Graphics object.
    589 
    590         INPUT:
    591             gridlines    -- (default: None) can be any of the following:
    592                             1. None, False: do not add grid lines.
    593                             2. True, "automatic", "major": add grid lines
    594                                at major ticks of the axes.
    595                             3. "minor": add grid at major and minor ticks.
    596                             4. [xlist,ylist]: a tuple or list containing
    597                                two elements, where xlist (or ylist) can be
    598                                any of the following.
    599                                4a. None, False: don't add horizontal (or
    600                                    vertical) lines.
    601                                4b. True, "automatic", "major": add
    602                                    horizontal (or vertical) grid lines at
    603                                    the major ticks of the axes.
    604                                4c. "minor": add horizontal (or vertical)
    605                                    grid lines at major and minor ticks of
    606                                    axes.
    607                                4d. an iterable yielding numbers n or pairs
    608                                    (n,opts), where n is the coordinate of
    609                                    the line and opt is a dictionary of
    610                                    MATPLOTLIB options for rendering the
    611                                    line.
    612             gridlinesstyle,
    613             hgridlinesstyle,
    614             vgridlinesstyle
    615                          -- (default: None) a dictionary of MATPLOTLIB
    616                             options for the rendering of the grid lines,
    617                             the horizontal grid lines or the vertical grid
    618                             lines, respectively.
    619 
    620         TESTS:
    621             sage: from sage.plot.axes import GridLines
    622             sage: GridLines()
    623             <class 'sage.plot.axes.GridLines'>
    624             sage: gl = GridLines(False)
    625             sage: gl = GridLines(True)
    626             sage: gl = GridLines("automatic")
    627             sage: gl = GridLines("major")
    628             sage: gl = GridLines("minor")
    629             sage: gl = GridLines([True,False])
    630             sage: gl = GridLines(["minor","major"])
    631             sage: gl = GridLines(["automatic",None])
    632             sage: gl = GridLines([range(-10,10,2), lambda x,y:srange(x,y,0.5)])
    633             sage: gl = GridLines(None, dict(color="red"),
    634             ...     dict(linestyle=":"), dict(color="blue"))
    635             sage: gl = GridLines(None, dict(rgbcolor="red"),
    636             ...     dict(linestyle=":"), dict(color="blue"))
    637         """
    638         self.__gridlines = gridlines
    639 
    640         defaultstyle = dict(color=(0.3,0.3,0.3),linewidth=0.4)
    641         if gridlinesstyle is not None:
    642             rgbcolor_keyword_support(gridlinesstyle)
    643             defaultstyle.update(gridlinesstyle)
    644         self.__gridlinesstyle = [copy(defaultstyle),copy(defaultstyle)]
    645         if vgridlinesstyle is not None:
    646             rgbcolor_keyword_support(vgridlinesstyle)
    647             self.__gridlinesstyle[0].update(vgridlinesstyle)
    648         if hgridlinesstyle is not None:
    649             rgbcolor_keyword_support(hgridlinesstyle)
    650             self.__gridlinesstyle[1].update(hgridlinesstyle)
    651 
    652     def add_gridlines(self, subplot, xmin, xmax, ymin, ymax, frame=False):
    653         # Process the input to get valid gridline data.
    654         r"""
    655         Add the grid lines to a subplot object.
    656 
    657         INPUT:
    658             subplot     -- an instance of matplotlib.axes.Subplot
    659             xmin, xmax  -- $x$ range of the Graphics object containing subplot
    660             ymin, ymax  -- $y$ range of the Graphics object containing subplot
    661             frame       -- (default: False) if True, then adjust the lengths of
    662                            the grid lines to touch connect to the frame.
    663 
    664         OUTPUT:
    665             None (modifies subplot)
    666 
    667         TESTS:
    668             sage: from sage.plot.axes import GridLines
    669             sage: from matplotlib.figure import Figure
    670             sage: subplot = Figure().add_subplot(111)
    671             sage: lims = [-10,20,10,35]
    672 
    673             sage: subplot = Figure().add_subplot(111)
    674             sage: GridLines().add_gridlines(subplot,*lims)
    675             sage: len(subplot.lines)
    676             0
    677 
    678             sage: subplot = Figure().add_subplot(111)
    679             sage: GridLines(False).add_gridlines(subplot,*lims)
    680             sage: len(subplot.lines)
    681             0
    682 
    683             sage: subplot = Figure().add_subplot(111)
    684             sage: GridLines(True).add_gridlines(subplot,*lims)
    685             sage: len(subplot.lines)
    686             13
    687 
    688             sage: subplot = Figure().add_subplot(111)
    689             sage: GridLines("automatic").add_gridlines(subplot,*lims)
    690             sage: len(subplot.lines)
    691             13
    692 
    693             sage: subplot = Figure().add_subplot(111)
    694             sage: GridLines("major").add_gridlines(subplot,*lims)
    695             sage: len(subplot.lines)
    696             13
    697 
    698             sage: subplot = Figure().add_subplot(111)
    699             sage: GridLines("minor").add_gridlines(subplot,*lims)
    700             sage: len(subplot.lines)
    701             57
    702 
    703             sage: subplot = Figure().add_subplot(111)
    704             sage: GridLines([True,False]).add_gridlines(subplot,*lims)
    705             sage: len(subplot.lines)
    706             7
    707 
    708             sage: subplot = Figure().add_subplot(111)
    709             sage: GridLines(["minor","major"]).add_gridlines(subplot,*lims)
    710             sage: len(subplot.lines)
    711             37
    712 
    713             sage: subplot = Figure().add_subplot(111)
    714             sage: GridLines(["automatic",None]).add_gridlines(subplot,*lims)
    715             sage: len(subplot.lines)
    716             7
    717 
    718             sage: subplot = Figure().add_subplot(111)
    719             sage: GridLines([range(-10,10,2), lambda x,y:srange(x,y,0.5)]).add_gridlines(subplot,*lims)
    720             sage: len(subplot.lines)
    721             60
    722 
    723             sage: subplot = Figure().add_subplot(111)
    724             sage: GridLines("automatic", dict(color="red"),
    725             ...     dict(linestyle=":"),
    726             ...     dict(color="blue")).add_gridlines(subplot,*lims)
    727             sage: len(subplot.lines)
    728             13
    729 
    730             sage: subplot = Figure().add_subplot(111)
    731             sage: GridLines([1,2,3]).add_gridlines(subplot,*lims)
    732             Traceback (most recent call last):
    733             ...
    734             TypeError: gridlines is not a list or tuple of length 2
    735 
    736             sage: subplot = Figure().add_subplot(111)
    737             sage: GridLines([1,2]).add_gridlines(subplot,*lims)
    738             Traceback (most recent call last):
    739             ...
    740             TypeError: elements of gridlines need to be iterable: [1, 2]
    741         """
    742         points = [[xmin, xmax], [ymin, ymax]]
    743         if self.__gridlines is None or self.__gridlines is False:
    744             return
    745         elif self.__gridlines in ["major", "automatic"] or self.__gridlines is True:
    746             self.__gridlines = [
    747                     self._get_ticks_locations(points[0]),
    748                     self._get_ticks_locations(points[1])
    749                     ]
    750         elif self.__gridlines == "minor":
    751             self.__gridlines = [
    752                     self._get_ticks_locations(points[0], self.__gridlines),
    753                     self._get_ticks_locations(points[1], self.__gridlines)
    754                     ]
    755         else:
    756             try:
    757                 gridlines = [None]*2
    758                 gridlines[0], gridlines[1] = self.__gridlines
    759             except ValueError:
    760                 raise TypeError, "gridlines is not a list or tuple of length 2"
    761 
    762             for i in range(2):
    763                 if gridlines[i] is None or gridlines[i] is False:
    764                     gridlines[i] = []
    765                 elif gridlines[i] in ["major", "automatic"] or gridlines[i] is True:
    766                     gridlines[i] = self._get_ticks_locations(points[i])
    767                 elif gridlines[i] == "minor":
    768                     gridlines[i] = self._get_ticks_locations(points[i],gridlines[i])
    769                 elif callable(gridlines[i]):
    770                     gridlines[i] = gridlines[i](*points[i])
    771 
    772             if not (hasattr(gridlines[0],'__iter__') and
    773                     hasattr(gridlines[1],'__iter__')):
    774                 raise TypeError, "elements of gridlines need to be iterable: %s" \
    775                     % gridlines
    776             self.__gridlines = gridlines
    777 
    778         # add the gridlines to subplot.
    779         if frame is True:
    780             xmin, xmax, ymin, ymax = \
    781                     self._get_adjustments_for_frame(*points)
    782             points = [[xmin, xmax], [ymin, ymax]]
    783 
    784         new_gridlines = []
    785         for i in range(2):
    786             new_list = []
    787             for entry in self.__gridlines[i]:
    788                 kwds = copy(self.__gridlinesstyle[i])
    789                 if hasattr(entry,'__len__'):
    790                     if len(entry) == 2:
    791                         val = entry[0]
    792                         rgbcolor_keyword_support(entry[1])
    793                         kwds.update(entry[1])
    794                 else:
    795                     val = entry
    796                     kwds = copy(self.__gridlinesstyle[i])
    797                 new_list.append([val,kwds])
    798             new_gridlines.append(new_list)
    799         xlines, ylines = new_gridlines
    800 
    801         # draw the grid lines
    802         from matplotlib import lines
    803         # horizontal lines
    804         for (yval, ykwds) in ylines:
    805             subplot.add_line(
    806                     lines.Line2D(points[0],[yval,yval],**ykwds)
    807                     )
    808         # vertical lines
    809         for (xval, xkwds) in xlines:
    810             subplot.add_line(
    811                     lines.Line2D([xval,xval],points[1],**xkwds)
    812                     )
    813 
    814     def _get_ticks_locations(self, interval, ticks="major"):
    815         r"""
    816         Find the locations of the major and/or minor ticks of the axes
    817         in the interval.
    818 
    819         INPUT:
    820             interval -- an interval as a pair of numbers
    821             ticks -- "major" or "minor". If "minor", then return also
    822                 the locations of the minor ticks.
    823 
    824         OUTPUT:
    825             list -- the locations of the ticks on the axes
    826 
    827         TESTS:
    828             sage: from sage.plot.axes import GridLines
    829             sage: GridLines()._get_ticks_locations([-10,20])
    830             [-10, -5, 0, 5, 10, 15, 20]
    831             sage: GridLines()._get_ticks_locations([10,35],"minor")
    832             [10.0, 11.0, 12.0, 13.0, ..., 32.0, 33.0, 34.0, 35.0]
    833 
    834         """
    835         # Axes._find_axes[2] returns locations of minor ticks
    836         # Axes._find_axes[3] returns locations of major ticks
    837         if ticks == "minor":
    838             minorticks = True
    839         else:
    840             minorticks = False
    841         return Axes()._find_axes(*interval)[2 if minorticks else 3]
    842 
    843     def _get_adjustments_for_frame(self, xinterval, yinterval):
    844         r"""
    845         Returns new limits for axes to accommodate a frame drawn around the
    846         plot.
    847 
    848         INPUT:
    849             xinterval -- x-axis interval as pairs of numbers
    850             yinterval -- y-axis interval as pairs of numbers
    851 
    852         OUTPUT:
    853             xmin, xmax, ymin, ymax -- numbers
    854 
    855         TESTS:
    856             sage: from sage.plot.axes import GridLines
    857             sage: GridLines()._get_adjustments_for_frame([-10,40],[10,35])
    858             (-11.0, 41.0, 9.5, 35.5)
    859         """
    860         return Axes()._adjustments_for_frame(*(xinterval+yinterval))
    861 
    86227def rgbcolor_keyword_support(d):
    86328    r"""
    86429    Change the rgbcolor key to color.
     
    90469        else:
    90570            d["color"] = d["rgbcolor"]
    90671            del d["rgbcolor"]
     72    return d
  • sage/plot/contour_plot.py

    diff -r af0cd051387b -r bdf61c76a98e sage/plot/contour_plot.py
    a b  
    157157
    158158    g = Graphics()
    159159    g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options))
     160    g.axes(False)
    160161    return g       
    161162
    162163@options(contours=(0,0), fill=False)
  • sage/plot/matrix_plot.py

    diff -r af0cd051387b -r bdf61c76a98e sage/plot/matrix_plot.py
    a b  
    140140
    141141    g = Graphics()
    142142    g.add_primitive(MatrixPlot(xy_data_array, xrange, yrange, options))
     143    g.axes(False)
    143144    return g
  • sage/plot/plot.py

    diff -r af0cd051387b -r bdf61c76a98e sage/plot/plot.py
    a b  
    314314        return SHOW_DEFAULT
    315315    SHOW_DEFAULT = bool(default)
    316316
    317 do_verify = True
    318 
    319317from sage.misc.randstate import current_randstate #for plot adaptive refinement
    320318import os #for viewing and writing images
    321319from colorsys import hsv_to_rgb #for the hue function
     
    327325import sage.misc.misc
    328326
    329327from misc import rgbcolor, Color, options, rename_keyword, to_mpl_color
    330 
     328from sage.misc.cachefunc import cached_method
    331329import operator
    332330
    333331############### WARNING ###
     
    337335# Sage startup times are much improved.)  - William
    338336###############
    339337
    340 #Sage 2D Graphics Axes class:
    341 from axes import Axes
    342 from axes import GridLines
    343 
    344338def is_Graphics(x):
    345339    """
    346340    Return True if `x` is a Graphics object.
     
    388382       
    389383            sage: G = Graphics()
    390384        """
    391         self.__aspect_ratio = None
    392         self.__fontsize = 10
    393         self.__show_axes = True
    394         self.__axes_color = (0, 0, 0)
    395         self.__axes_label_color = (0, 0, 0)
    396         self.__tick_label_color = (0, 0, 0)
    397         self.__axes_width = 0.8
    398385        self.__objects = []
    399386
     387    def get_saved_options(self):
     388        try:
     389            return self.__saved_options
     390        except AttributeError:
     391            self.__saved_options = {}
     392            return self.__saved_options
     393
     394    def get_saved_option(self, option, default=None):
     395        return self.get_saved_options().get(option, default)
     396
     397    def set_saved_option(self, option, value):
     398        options = self.get_saved_options()
     399        options[option] = value
     400   
    400401    def set_aspect_ratio(self, ratio):
    401402        """
    402403        Set the aspect ratio.
     
    433434        ratio = float(ratio)
    434435        if ratio <= 0:
    435436            raise ValueError, "the aspect ratio must be positive"
    436         self.__aspect_ratio = ratio
     437        self.set_saved_option('aspect_ratio', ratio)
    437438
    438439    def aspect_ratio(self):
    439440        """
     
    451452            sage: P.aspect_ratio()
    452453            2.0
    453454        """
    454         return self.__aspect_ratio
     455        return self.get_saved_option('aspect_ratio')
    455456
    456457    def get_axes_range(self):
    457458        """
     
    475476            [('xmax', 3.0), ('xmin', -1.0), ('ymax', 5.0), ('ymin', -4.0)]
    476477        """
    477478        axes_range = self.get_minmax_data()
    478         axes_range.update(self._get_axes_range_dict())
     479        for name in ['xmin', 'xmax', 'ymin', 'ymax']:
     480            value = self.get_saved_option(name, None)
     481            if value is not None:
     482                axes_range[name] = value
    479483        return axes_range
    480484
    481485    def set_axes_range(self, xmin=None, xmax=None, ymin=None, ymax=None):
     
    484488       
    485489        INPUT:
    486490       
    487        
    488491        -  ``xmin, xmax, ymin, ymax`` - floats
    489492       
    490        
    491493        EXAMPLES::
    492494       
    493495            sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
     
    497499            (-1.0, 20.0, 0.0, 2.0)
    498500        """
    499501        l = locals()
    500         axes_range = self._get_axes_range_dict()
    501502        for name in ['xmin', 'xmax', 'ymin', 'ymax']:
    502503            if l[name] is not None:
    503                 axes_range[name] = float(l[name])
     504                self.set_saved_option(name, float(l[name]))
    504505
    505506    axes_range = set_axes_range
    506507
    507     def _get_axes_range_dict(self):
    508         """
    509         Returns the underlying dictionary used to store the user's
    510         custom ranges for the axes on this object.
    511 
    512         EXAMPLES::
    513 
    514             sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
    515             sage: L._get_axes_range_dict()
    516             {}
    517             sage: L.set_axes_range(xmin=-1)
    518             sage: L._get_axes_range_dict()
    519             {'xmin': -1.0}
    520         """
    521         try:
    522             return self.__axes_range
    523         except AttributeError:
    524             self.__axes_range = {}
    525             return self.__axes_range
    526 
    527508    def fontsize(self, s=None):
    528509        """
    529         Set the font size of axes labels and tick marks.
     510        DEPRECATED:
     511       
     512        Set the font size of axes labels.
    530513       
    531514        INPUT:
    532        
    533        
    534515        -  ``s`` - integer, a font size in points.
    535516       
    536        
    537517        If called with no input, return the current fontsize.
    538518       
    539519        EXAMPLES::
     
    549529       
    550530            sage: L
    551531        """
     532       
    552533        if s is None:
    553             try:
    554                 return self.__fontsize
    555             except AttributeError:
    556                 self.__fontsize = 10
    557                 return self.__fontsize
    558         self.__fontsize = int(s)
     534            return self.get_saved_option('fontsize', 10)
     535        else:
     536            return self.set_saved_option('fontsize', int(s))
    559537
    560538    def axes(self, show=None):
    561539        """
     
    564542       
    565543        INPUT:
    566544       
    567        
    568545        -  ``show`` - bool
    569546       
    570        
    571547        If called with no input, return the current axes setting.
    572548       
    573549        EXAMPLES::
     
    596572            sage: L
    597573        """
    598574        if show is None:
    599             try:
    600                 return self.__show_axes
    601             except AttributeError:
    602                 self.__show_axes = True
    603                 return self.__show_axes
    604         self.__show_axes = bool(show)
     575            return self.get_saved_option('show_axes', True)
     576        else:
     577            return self.set_saved_option('show_axes', bool(show))
    605578
    606579    def axes_color(self, c=None):
    607580        """
     
    641614            sage: L
    642615        """
    643616        if c is None:
    644             try:
    645                 return self.__axes_color
    646            
    647             except AttributeError:
    648                 self.__axes_color = (0.0, 0.0, 0.0)
    649                 return self.__axes_color
    650         self.__axes_color = rgbcolor(c)
     617            return self.get_saved_option('axes_color', (0, 0, 0))
     618        else:
     619            return self.set_saved_option('axes_color', rgbcolor(c))
    651620
    652621    def axes_labels(self, l=None):
    653622        """
     
    680649            sage: p
    681650        """
    682651        if l is None:
    683             try:
    684                 return self.__axes_labels
    685             except AttributeError:
    686                 self.__axes_labels = None
    687                 return self.__axes_labels
    688         if not isinstance(l, (list, tuple)):
    689             raise TypeError, "l must be a list or tuple"
    690         if len(l) != 2:
    691             raise ValueError, "l must have length 2"
    692         self.__axes_labels = (str(l[0]), str(l[1]))
     652            return self.get_saved_option('axes_labels', None)
     653        else:
     654            if not isinstance(l, (list, tuple)):
     655                raise TypeError, "l must be a list or tuple"
     656            if len(l) != 2:
     657                raise ValueError, "l must have length 2"
     658            self.set_saved_option('axes_labels', tuple(str(i) for i in l))
    693659
    694660    def axes_label_color(self, c=None):
    695661        r"""
     
    735701            sage: p
    736702        """
    737703        if c is None:
    738             try:
    739                 return self.__axes_label_color
    740             except AttributeError:
    741                 self.__axes_label_color = (0, 0, 0)
    742                 return self.__axes_label_color
    743         self.__axes_label_color = rgbcolor(c)
    744 
     704            return self.get_saved_option('axes_label_color', (0, 0, 0))
     705        else:
     706            self.set_saved_option('axes_label_color', rgbcolor(c))
    745707
    746708    def axes_width(self, w=None):
    747709        r"""
     
    776738            sage: p
    777739        """
    778740        if w is None:
    779             try:
    780                 return self.__axes_width
    781             except AttributeError:
    782                 self.__axes_width = True
    783                 return self.__axes_width
    784         self.__axes_width = float(w)
     741            return self.get_saved_option('axes_label_width', 0.8)
     742        else:
     743            self.set_saved_option('axes_label_width', float(w))
    785744
    786745    def tick_label_color(self, c=None):
    787746        """
     
    807766            sage: p
    808767        """
    809768        if c is None:
    810             try:
    811                 return self.__tick_label_color
    812             except AttributeError:
    813                 self.__tick_label_color = (0, 0, 0)
    814                 return self.__tick_label_color
    815         self.__tick_label_color = rgbcolor(c)
     769            return self.get_saved_option('tick_label_color', (0, 0, 0))
     770        else:
     771            self.set_saved_option('tick_label_color', rgbcolor(c))
    816772
    817773    def _repr_(self):
    818774        r"""
     
    987943       
    988944        This only works when other is a Python int equal to 0. In all other
    989945        cases a TypeError is raised. The main reason for this function is
    990         to make suming a list of graphics objects easier.
     946        to make summing a list of graphics objects easier.
    991947       
    992948        EXAMPLES::
    993949       
     
    1033989            raise TypeError, "other (=%s) must be a Graphics objects"%other
    1034990        g = Graphics()
    1035991        g.__objects = self.__objects + other.__objects
    1036         g.__aspect_ratio = max(self.__aspect_ratio, other.__aspect_ratio)
     992        g.set_saved_option('aspect_ratio',
     993                           max(self.get_saved_option('aspect_ratio'),
     994                               other.get_saved_option('aspect_ratio')))
    1037995        return g
    1038996 
    1039997    def add_primitive(self, primitive):
     
    10711029            g = g.translate(0,0,z)
    10721030        return g
    10731031       
    1074     def show(self, xmin=None, xmax=None, ymin=None, ymax=None,
    1075              figsize=DEFAULT_FIGSIZE, filename=None,
    1076              dpi=DEFAULT_DPI, axes=None, axes_labels=None,frame=False,
    1077              fontsize=None, aspect_ratio=None,
    1078              gridlines=None, gridlinesstyle=None,
    1079              vgridlinesstyle=None, hgridlinesstyle=None):
     1032    def show(self, **options):
    10801033        """
    10811034        Show this graphics image with the default image viewer.
    10821035
     
    12691222            sage: matrix_plot(M).show(gridlines=True)
    12701223        """
    12711224        if DOCTEST_MODE:
    1272             self.save(DOCTEST_MODE_FILE,
    1273                       xmin, xmax, ymin, ymax, figsize,
    1274                       dpi=dpi, axes=axes, axes_labels=axes_labels,frame=frame,
    1275                       aspect_ratio=aspect_ratio, gridlines=gridlines,
    1276                       gridlinesstyle=gridlinesstyle,
    1277                       vgridlinesstyle=vgridlinesstyle,
    1278                       hgridlinesstyle=hgridlinesstyle)
    1279             return
    1280         if EMBEDDED_MODE:
    1281             self.save(filename, xmin, xmax, ymin, ymax, figsize,
    1282                       dpi=dpi, axes=axes, axes_labels=axes_labels,frame=frame,
    1283                       aspect_ratio=aspect_ratio, gridlines=gridlines,
    1284                       gridlinesstyle=gridlinesstyle,
    1285                       vgridlinesstyle=vgridlinesstyle,
    1286                       hgridlinesstyle=hgridlinesstyle)
    1287             return
    1288         if filename is None:
    1289             filename = sage.misc.misc.tmp_filename() + '.png'
    1290         self.save(filename, xmin, xmax, ymin, ymax, figsize, dpi=dpi, axes=axes,
    1291                   axes_labels=axes_labels,
    1292                   frame=frame, fontsize=fontsize,
    1293                   aspect_ratio=aspect_ratio,
    1294                   gridlines=gridlines,
    1295                   gridlinesstyle=gridlinesstyle,
    1296                   vgridlinesstyle=vgridlinesstyle,
    1297                   hgridlinesstyle=hgridlinesstyle)
    1298         os.system('%s %s 2>/dev/null 1>/dev/null &'%(sage.misc.viewer.browser(), filename))
     1225            options['filename'] = DOCTEST_MODE_FILE
     1226        if options.get('filename', None) is None:
     1227            options['filename'] = sage.misc.misc.tmp_filename() + '.png'
     1228
     1229        self.save(**options)
     1230       
     1231        if not EMBEDDED_MODE:
     1232            os.system('%s %s 2>/dev/null 1>/dev/null &'%(sage.misc.viewer.browser(),
     1233                                                         options['filename']))
    12991234
    13001235    def xmin(self, xmin=None):
    13011236        """
     
    14041339            ymin -= 1
    14051340            ymax += 1
    14061341        return {'xmin':xmin, 'xmax':xmax, 'ymin':ymin, 'ymax':ymax}
     1342
     1343    def get_filename_and_extension(self, **options):
     1344        """
    14071345       
    1408     def save(self, filename=None,
    1409              xmin=None, xmax=None, ymin=None, ymax=None,
    1410              figsize=DEFAULT_FIGSIZE, figure=None, sub=None, savenow=True,
    1411              dpi=DEFAULT_DPI, axes=None, axes_labels=None, fontsize=None,
    1412              frame=False, verify=True, aspect_ratio = None,
    1413              gridlines=None, gridlinesstyle=None,
    1414              vgridlinesstyle=None, hgridlinesstyle=None):
     1346        """
     1347        filename = options.get('filename', sage.misc.misc.graphics_filename())
     1348
     1349        #Check the extension for the filename
     1350        try:
     1351            ext = os.path.splitext(filename)[1].lower()
     1352        except IndexError:
     1353            ext = ''
     1354
     1355        if ext.lstrip('.') not in ['', 'png', 'ps', 'eps', 'pdf', 'svg', 'sobj']:
     1356            raise ValueError, "file extension must be either 'png', 'ps, 'eps', 'pdf, 'svg' or 'sobj'"
     1357
     1358        return filename, ext
     1359
     1360    def _mpl_figsize_and_range(self, options):
     1361        """
     1362        TESTS::
     1363
     1364            sage: p = plot(sin(x),-10,10)
     1365
     1366
     1367        We test to see that the
     1368       
     1369            sage: options = {}
     1370            sage: p._mpl_start(options)
     1371           
     1372            sage: p._mpl_figsize_and_range(options)
     1373            sage: options['subplot'].get_xlim()
     1374            (-10.0, 10.0)
     1375            sage: options['subplot'].get_ylim()
     1376            (-0.99..., 0.99...)
     1377           
     1378            sage: options['ymin'] = -0.5; options['ymax'] = 0.5
     1379            sage: p._mpl_figsize_and_range(options)
     1380            sage: options['subplot'].get_ylim()
     1381            (-0.5, 0.5)
     1382
     1383            sage: p.xmin(-2)
     1384            sage: options = {}
     1385            sage: p._mpl_start(options)
     1386            sage: p._mpl_figsize_and_range(options)
     1387            sage: options['subplot'].get_xlim()
     1388            (-2.0, 10.0)
     1389
     1390p         """
     1391        figure = options['figure']
     1392        subplot = options['subplot']
     1393        figsize = options.get('figsize', DEFAULT_FIGSIZE)
     1394        aspect_ratio = options.get('aspect_ratio', self.aspect_ratio())
     1395       
     1396        #Get the range of the axes and calculate the figure size
     1397        self.set_axes_range(options.get('xmin', None),
     1398                            options.get('xmax', None),
     1399                            options.get('ymin', None),
     1400                            options.get('ymax', None))
     1401        options.update(self.get_axes_range())
     1402
     1403        # adjust the figsize in case the user also specifies an aspect ratio
     1404        width, height = adjust_figsize_for_aspect_ratio(figsize, aspect_ratio,
     1405                                                        xmin=options['xmin'],
     1406                                                        xmax=options['xmax'],
     1407                                                        ymin=options['ymin'],
     1408                                                        ymax=options['ymax'])
     1409        figure.set_figwidth(float(width))
     1410        figure.set_figheight(float(height))
     1411
     1412        #Set the range of the axes
     1413        subplot.set_xlim(options['xmin'], options['xmax'])
     1414        subplot.set_ylim(options['ymin'], options['ymax'])
     1415
     1416    def _mpl_axes(self, options):
     1417        frame = options.get('frame', True)
     1418
     1419        subplot = options['subplot']
     1420        xmin = options['xmin']; xmax = options['xmax']
     1421        ymin = options['ymin']; ymax = options['ymax']
     1422
     1423        #################
     1424        #  Axes Labels  #
     1425        #################
     1426        labels = options.get('axes_labels', self.axes_labels())
     1427        if labels is not None:
     1428            xlabel, ylabel = labels
     1429            if xlabel:
     1430                subplot.set_xlabel(xlabel)
     1431            if ylabel:
     1432                subplot.set_ylabel(ylabel)
     1433
     1434        ####################
     1435        #  x=0, y=0 Lines  #
     1436        ####################
     1437        show_axes  = options.get('show_axes', self.axes())
     1438        axes_color = options.get('axes_color',
     1439                                 self.axes_color())
     1440        axes_width = options.get('axes_width',
     1441                                 self.axes_width())
     1442        if show_axes:
     1443            from matplotlib import lines
     1444            if xmin < 0 and xmax > 0:
     1445                subplot.axvline(color=axes_color, linewidth=axes_width)
     1446            if ymin < 0 and ymax > 0:
     1447                subplot.axhline(color=axes_color, linewidth=axes_width)
     1448
     1449        ###########
     1450        #  Frame  #
     1451        ###########
     1452        show_frame = options.get('frame', True)
     1453        if not show_frame:
     1454            subplot.set_axis_off()       
     1455
     1456
     1457        ###########
     1458        #  Title  #
     1459        ###########
     1460        subplot.set_title(options.get('title',
     1461                                      self.get_saved_option('title',
     1462                                                            '')))
     1463
     1464    def _mpl_grid(self, options):
     1465        from axes import rgbcolor_keyword_support
     1466
     1467        gridlines = options.get('gridlines', None)
     1468        if gridlines is None or gridlines is False:
     1469            return
     1470       
     1471        if gridlines in [True, 'major', 'automatic', 'minor']:
     1472            hlines = gridlines
     1473            vlines = gridlines
     1474        else:
     1475            try:
     1476                hlines, vlines = gridlines
     1477            except ValueError:
     1478                raise TypeError, "gridlines is not a list or tuple of length 2"
     1479
     1480        ############
     1481        #  Styles  #
     1482        ############
     1483        import copy
     1484        style = rgbcolor_keyword_support(options.get('gridlinesstyle', {}))
     1485        hstyle = copy.copy(style)
     1486        hstyle.update(rgbcolor_keyword_support(options.get('hgridlinesstyle', {})))
     1487        vstyle = copy.copy(style)
     1488        vstyle.update(rgbcolor_keyword_support(options.get('vgridlinesstyle', {})))
     1489
     1490        ############
     1491        #  Render  #
     1492        ############
     1493        subplot = options['subplot']
     1494        for (lines, axis, add_line, style) in [(hlines, subplot.get_yaxis(), subplot.axhline, hstyle),
     1495                                               (vlines, subplot.get_xaxis(), subplot.axvline, vstyle)]:
     1496            if not lines:
     1497                continue
     1498
     1499            if isinstance(lines, (str, bool, type(None))):
     1500                axis.grid(True, **style)
     1501                if lines == 'minor':
     1502                    axis.grid(True, which='minor', **style)
     1503                continue
     1504
     1505            #Handle the case where we are given an iterable
     1506            for entry in lines:
     1507                if hasattr(entry, '__len__') and len(entry) == 2:
     1508                    pos, new_line_style = entry
     1509                    line_style = copy.copy(style)
     1510                    line_style.update(rgbcolor_keyword_support(new_line_style))
     1511                else:
     1512                    pos = entry
     1513                    line_style = style
     1514
     1515                add_line(float(pos), **line_style)
     1516
     1517    def _mpl_primitives(self, options):
     1518        subplot = options['subplot']
     1519        for g in self.__objects:
     1520            g._render_on_subplot(subplot)
     1521
     1522
     1523    def _mpl_scales(self, options):
     1524        """
     1525        Sets the scales for the axes.
     1526        """
     1527        subplot = options['subplot']
     1528        subplot.set_xscale(options.get('xscale',
     1529                                       self.get_saved_option('xscale',
     1530                                                             'linear')))
     1531        subplot.set_yscale(options.get('yscale',
     1532                                       self.get_saved_option('yscale',
     1533                                                             'linear')))
     1534
     1535    def _mpl_final(self, options):       
     1536        dpi = options.get('dpi', DEFAULT_DPI)
     1537        filename = options['filename']
     1538        figure = options['figure']
     1539        subplot = options['subplot']
     1540
     1541        # You can output in PNG, PS, EPS, PDF, or SVG format,
     1542        # depending on the file extension.  matplotlib looks at the
     1543        # file extension to see what the renderer should be.  The
     1544        # default is FigureCanvasAgg for png's because this is by far
     1545        # the most common type of files rendered, like in the Notebook
     1546        # for example.  if the file extension is not '.png', then
     1547        # matplotlib will handle it.
     1548        from matplotlib.backends.backend_agg import FigureCanvasAgg
     1549        canvas = FigureCanvasAgg(figure)
     1550        default_dpi = {'.eps':72, '.ps':72, '.pdf':72, '.svg':80, '.png':100}
     1551        dpi = default_dpi[ext] if dpi is None else dpi
     1552        canvas.print_figure(filename, dpi=dpi)
     1553
     1554    def _mpl_start(self, options):
     1555        #Get the figure and subplot
     1556        from matplotlib.figure import Figure
     1557        figure = Figure(DEFAULT_FIGSIZE)
     1558        subplot = figure.add_subplot(111)
     1559        options['figure'] = figure
     1560        options['subplot'] = subplot
     1561
     1562    def _save_mpl(self, options):
     1563        self._mpl_start(options)
     1564        self._mpl_figsize_and_range(options)
     1565        self._mpl_axes(options)
     1566        self._mpl_grid(options)
     1567        self._mpl_primitives(options)
     1568        self._mpl_scales(options)
     1569        self._mpl_final(options)
     1570               
     1571    def save(self, **options):
    14151572        r"""
    14161573        Save the graphics to an image file of type: PNG, PS, EPS, SVG,
    14171574        SOBJ, depending on the file extension you give the filename.
     
    14351592       
    14361593            sage: point((-1,1),pointsize=30, rgbcolor=(1,0,0))
    14371594        """
    1438         self.set_axes_range(xmin, xmax, ymin, ymax)
    1439         d = self.get_axes_range()
    1440         xmin = d['xmin']
    1441         xmax = d['xmax']
    1442         ymin = d['ymin']
    1443         ymax = d['ymax']
    1444 
    1445         # adjust the figsize in case the user also specifies an aspect ratio
    1446         if aspect_ratio is None:
    1447             aspect_ratio = self.aspect_ratio()
    1448         figsize = adjust_figsize_for_aspect_ratio(figsize, aspect_ratio, xmin=xmin,
    1449                                                   xmax=xmax, ymin=ymin, ymax=ymax)
    1450        
    1451         global do_verify
    1452         do_verify = verify
    1453 
    1454         if axes is None:
    1455             axes = self.__show_axes
    1456 
    1457         from matplotlib.figure import Figure
    1458         if filename is None:
    1459             filename = sage.misc.misc.graphics_filename()
    1460         try:
    1461             ext = os.path.splitext(filename)[1].lower()
    1462         except IndexError:
    1463             raise ValueError, "file extension must be either 'png', 'eps', 'svg' or 'sobj'"
    1464        
     1595        filename, ext = self.get_filename_and_extension(**options)
    14651596        if ext == '' or ext == '.sobj':
    14661597            SageObject.save(self, filename)
    14671598            return
     1599        else:
     1600            options['filename'] = filename
    14681601
    1469         self.fontsize(fontsize)
    1470         self.axes_labels(l=axes_labels)
    1471 
    1472         if figure is None:
    1473             figure = Figure(figsize)
    1474 
    1475         #The line below takes away the excessive whitespace around
    1476         #images.  ('figsize' and  'dpi' still work as expected):
    1477         figure.subplots_adjust(left=0.04, bottom=0.04, right=0.96, top=0.96)
    1478        
    1479         #the incoming subplot instance
    1480         subplot = sub
    1481         if not subplot:
    1482             subplot = figure.add_subplot(111)
    1483 
    1484         #take away the matplotlib axes:
    1485         subplot.xaxis.set_visible(False)
    1486         subplot.yaxis.set_visible(False)
    1487         subplot.set_frame_on(False)
    1488 
    1489         #add all the primitives to the subplot
    1490         #check if there are any ContourPlot instances
    1491         #in self._objects, and if so change the axes
    1492         #to be frame axes instead of centered axes
    1493         contour = False
    1494         plotfield = False
    1495         matrixplot = False
    1496         from contour_plot import ContourPlot
    1497         from matrix_plot import MatrixPlot
    1498         from plot_field import PlotField
    1499         for g in self.__objects:
    1500             if isinstance(g, ContourPlot):
    1501                 contour = True
    1502             if isinstance(g, PlotField):
    1503                 plotfield = True
    1504             if isinstance(g, MatrixPlot):
    1505                 matrixplot = True
    1506             g._render_on_subplot(subplot)
    1507        
    1508         #adjust the xy limits and draw the axes:
    1509         if axes is None:
    1510             axes = self.__show_axes
    1511 
    1512         #construct an Axes instance, see 'axes.py' for relevant code
    1513         sage_axes = Axes(color=self.__axes_color, fontsize=self.__fontsize,
    1514                          axes_labels=self.__axes_labels,
    1515                          axes_label_color=self.__axes_label_color,
    1516                          tick_label_color=self.__tick_label_color, linewidth=self.__axes_width)
    1517 
    1518         # construct a GridLines instance, see 'axes.py' for relevant code
    1519         sage_gridlines = GridLines(gridlines=gridlines, gridlinesstyle=gridlinesstyle,
    1520                 vgridlinesstyle=vgridlinesstyle, hgridlinesstyle=hgridlinesstyle)
    1521 
    1522         #adjust the xy limits and draw the axes:
    1523         if not (contour or plotfield or matrixplot): #the plot is a 'regular' plot
    1524             xmin -= 0.1*(xmax-xmin)
    1525             xmax += 0.1*(xmax-xmin)
    1526             ymin -= 0.1*(ymax-ymin)
    1527             ymax += 0.1*(ymax-ymin)
    1528             if frame: #add the frame axes
    1529                 axmin, axmax = xmin - 0.04*abs(xmax - xmin), xmax + 0.04*abs(xmax - xmin)
    1530                 aymin, aymax = ymin - 0.04*abs(ymax - ymin), ymax + 0.04*abs(ymax - ymin)
    1531                 subplot.set_xlim([axmin, axmax])
    1532                 subplot.set_ylim([aymin, aymax])
    1533                 # draw the grid
    1534                 sage_gridlines.add_gridlines(subplot, xmin, xmax, ymin, ymax, True)
    1535                 #add a frame to the plot and possibly 'axes_with_no_ticks'
    1536                 sage_axes.add_xy_frame_axes(subplot, xmin, xmax, ymin, ymax,
    1537                                         axes_with_no_ticks=axes)
    1538 
    1539             elif not frame and axes: #regular plot with regular axes
    1540                 # draw the grid
    1541                 sage_gridlines.add_gridlines(subplot, xmin, xmax, ymin, ymax, False)
    1542                 # draw the axes
    1543                 xmin, xmax, ymin, ymax = sage_axes.add_xy_axes(subplot, xmin, xmax, ymin, ymax)
    1544                 subplot.set_xlim(xmin, xmax)
    1545                 subplot.set_ylim(ymin, ymax)
    1546                
    1547             else: #regular plot with no axes
    1548                 subplot.set_xlim(xmin, xmax)
    1549                 subplot.set_ylim(ymin, ymax)
    1550                 # draw the grid
    1551                 sage_gridlines.add_gridlines(subplot, xmin, xmax, ymin, ymax, False)
    1552                
    1553         elif (contour or plotfield): #contour or field plot in self.__objects, so adjust axes accordingly
    1554             subplot.set_xlim([xmin - 0.05*abs(xmax - xmin), xmax + 0.05*abs(xmax - xmin)])
    1555             subplot.set_ylim([ymin - 0.05*abs(ymax - ymin), ymax + 0.05*abs(ymax - ymin)])
    1556             # draw the grid
    1557             sage_gridlines.add_gridlines(subplot, xmin, xmax, ymin, ymax, True)
    1558             # draw the axes
    1559             if axes: #axes=True unless user specifies axes=False
    1560                 sage_axes.add_xy_frame_axes(subplot, xmin, xmax, ymin, ymax)
    1561                
    1562         else: #we have a 'matrix_plot' in self.__objects, so adjust axes accordingly
    1563             subplot.set_xlim([xmin - 0.05*abs(xmax - xmin), xmax + 0.05*abs(xmax - xmin)])
    1564             subplot.set_ylim([ymin - 0.05*abs(ymax - ymin), ymax + 0.05*abs(ymax - ymin)])
    1565             # draw the grid
    1566             if gridlines in ["major", "automatic", True]:
    1567                 gridlines = [sage.misc.misc.srange(-0.5,xmax+1,1),
    1568                         sage.misc.misc.srange(-0.5,ymax+1,1)]
    1569             sage_gridlines = GridLines(gridlines=gridlines,
    1570                     gridlinesstyle=gridlinesstyle,
    1571                     vgridlinesstyle=vgridlinesstyle,
    1572                     hgridlinesstyle=hgridlinesstyle)
    1573             sage_gridlines.add_gridlines(subplot, xmin, xmax, ymin, ymax, False)
    1574             # draw the axes
    1575             if axes: #axes=True unless user specifies axes=False
    1576                 sage_axes.add_xy_matrix_frame_axes(subplot, xmin, xmax, ymin, ymax)
    1577 
    1578         # You can output in PNG, PS, EPS, PDF, or SVG format, depending on the file extension.
    1579         # matplotlib looks at the file extension to see what the renderer should be.
    1580         # The default is FigureCanvasAgg for png's because this is by far the most
    1581         # common type of files rendered, like in the Notebook for example.
    1582         # if the file extension is not '.png', then matplotlib will handle it.
    1583         if savenow:
    1584             from matplotlib.backends.backend_agg import FigureCanvasAgg
    1585             canvas = FigureCanvasAgg(figure)
    1586             if ext in ['.eps', '.ps', '.pdf']:
    1587                 if dpi is None:
    1588                     dpi = 72
    1589             elif ext == '.svg':
    1590                 if dpi is None:
    1591                     dpi = 80
    1592             elif ext == '.png':
    1593                 if dpi is None:
    1594                     dpi = 100
    1595             else:
    1596                 raise ValueError, "file extension must be either 'png', 'ps, 'eps', 'pdf, 'svg' or 'sobj'"
    1597             canvas.print_figure(filename, dpi=dpi)
     1602        self._save_mpl(options)
    15981603
    15991604def xydata_from_point_list(points):
    16001605    r"""
     
    24132418        #make a blank matplotlib Figure:
    24142419        from matplotlib.figure import Figure
    24152420        figure = Figure(figsize)
    2416         global do_verify
    2417         do_verify = True
    24182421        for i,g in zip(range(1, dims+1), glist):
    24192422            subplot = figure.add_subplot(rows, cols, i)
    2420             g.save(filename, dpi=dpi, figure=figure, sub=subplot,
    2421                    savenow = (i==dims), verify=do_verify,
     2423            g.save(filename=filename, dpi=dpi, figure=figure, sub=subplot,
     2424                   savenow = (i==dims),
    24222425                   axes = axes,
    24232426                   **args)#only save if i==dims.
    24242427
     
    24322435        self._render(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args)
    24332436
    24342437    def show(self, filename=None, dpi=DEFAULT_DPI, figsize=DEFAULT_FIGSIZE,
    2435              axes = None, **args):
     2438             axes = None, **kwds):
    24362439        r"""
    24372440        Show this graphics array using the default viewer.
    24382441       
     
    24642467        """
    24652468        if (figsize != DEFAULT_FIGSIZE): self.__set_figsize__(figsize)
    24662469        if DOCTEST_MODE:
    2467             self.save(DOCTEST_MODE_FILE,
    2468                       dpi=dpi, figsize=self._figsize, axes = axes, **args)
     2470            self.save(filename=DOCTEST_MODE_FILE,
     2471                      dpi=dpi, figsize=self._figsize, axes = axes, **kwds)
    24692472            return
    24702473        if EMBEDDED_MODE:
    2471             self.save(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args)
     2474            self.save(filename=filename, dpi=dpi, figsize=self._figsize, axes = axes, **kwds)
    24722475            return
    24732476        if filename is None:
    24742477            filename = sage.misc.misc.tmp_filename() + '.png'
    2475         self._render(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args)
     2478        self._render(filename=filename, dpi=dpi, figsize=self._figsize, axes = axes, **kwds)
    24762479        os.system('%s %s 2>/dev/null 1>/dev/null &'%(
    24772480                         sage.misc.viewer.browser(), filename))
    24782481
  • sage/plot/primitive.py

    diff -r af0cd051387b -r bdf61c76a98e sage/plot/primitive.py
    a b  
    6565        """
    6666        Translate 2d plot options into 3d plot options.
    6767        """
    68         if options == None:
     68        if options is None:
    6969            options = self.options()
    7070        options_3d = {}
    7171        if 'rgbcolor' in options:
    72             options_3d['rgbcolor'] = options['rgbcolor']
    73             del options['rgbcolor']
     72            options_3d['rgbcolor'] = options.pop('rgbcolor')
    7473        if 'alpha' in options:
    75             options_3d['opacity'] = options['alpha']
    76             del options['alpha']
     74            options_3d['opacity'] = options.pop('alpha')
    7775        if len(options) != 0:
    7876            raise NotImplementedError, "Unknown plot3d equivalent for %s" % ", ".join(options.keys())
    7977        return options_3d
     
    9391            sage: GraphicPrimitive({}).options()
    9492            {}
    9593        """
    96         from sage.plot.plot import do_verify, hue
     94        from sage.plot.plot import hue
    9795        O = dict(self.__options)
    98         if do_verify:
    99             A = self._allowed_options()
    100             t = False
    101             K = A.keys() + ['xmin', 'xmax', 'ymin', 'ymax', 'axes']
    102             for k in O.keys():
    103                 if not k in K:
    104                     do_verify = False
    105                     verbose("WARNING: Ignoring option '%s'=%s"%(k,O[k]), level=0)
    106                     t = True
    107             if t:
    108                 s = "\nThe allowed options for %s are:\n"%self
    109                 K.sort()
    110                 for k in K:
    111                     if A.has_key(k):
    112                         s += "    %-15s%-60s\n"%(k,A[k])
    113                 verbose(s, level=0)
    114 
     96        A = self._allowed_options()
     97        t = False
     98        K = A.keys() + ['xmin', 'xmax', 'ymin', 'ymax', 'axes']
     99        for k in O.keys():
     100            if not k in K:
     101                verbose("WARNING: Ignoring option '%s'=%s"%(k,O[k]), level=0)
     102                t = True
     103        if t:
     104            s = "\nThe allowed options for %s are:\n"%self
     105            K.sort()
     106            for k in K:
     107                if A.has_key(k):
     108                    s += "    %-15s%-60s\n"%(k,A[k])
     109            verbose(s, level=0)
    115110
    116111        if 'hue' in O:
    117112            t = O['hue']