Ticket #6893: plot.patch
File plot.patch, 68.5 KB (added by , 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 24 24 # 25 25 # http://www.gnu.org/licenses/ 26 26 #***************************************************************************** 27 from math import floor, log28 from sage.structure.sage_object import SageObject29 import sage.misc.misc30 from copy import copy31 32 class Axes(SageObject):33 """34 Axes for SAGE 2D Graphics.35 36 Set all axis properties and then add one of37 the following axes to the current (matplotlib) subplot:38 add_xy__axes39 add_xy_frame_axes40 add_xy_matrix_frame_axes41 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 = color46 self.__tick_label_color = tick_label_color47 self.__axes_labels = axes_labels48 self.__axes_label_color = axes_label_color49 self.__fontsize = fontsize50 self.__linewidth = linewidth51 self.__draw_x_axis = True52 self.__draw_y_axis = True53 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 values58 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, False65 66 # Is the range: *only or dominantly* and *negative or positive*?67 if absmin > absmax:68 if maxval < 0:69 onlyneg = True70 else:71 domneg = True72 73 # Is the stepsize going to be < 1?74 if absmin < 1:75 n = 076 s = str(absmin).split('.')[1]77 for c in s:78 n+=179 if c != '0':80 break81 p = -(n-1)82 d0 = eval(s[n-1])83 #string may be of length 184 try:85 d1 = eval(s[n])86 except IndexError:87 d1 = 088 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 = 1101 102 else: #this means: abs(minval) < abs(maxval)103 if minval > 0:104 onlypos = True105 else:106 dompos = True107 #is the stepsize going to be < 1?108 if absmax < 1:109 n = 0110 s = str(absmax).split('.')[1]111 for c in s:112 n+=1113 if c != '0':114 break115 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 = 1134 135 #choose a step size depending on either136 #1st or 2nd digits, d0 and d1, of given maxval or minval137 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 y141 # ranges into widths from the above step sizes142 if d0 == 1:143 if d1 > 5 and p!=1:144 funda = 2.5145 step = funda*o1146 elif d1 < 5 and p==1:147 funda = 2.5148 step = funda*o1149 elif d1 < 5 and p>1:150 funda = 2.5151 step = funda*o1152 else:153 funda = 5154 step = funda*o1155 elif d0 == 2 or (d0 == 3 and p > 1):156 funda = 5157 step = funda*o1158 elif d0 in [3, 4, 5, 6]:159 funda = 1160 step = funda*o0161 else:162 funda = 2163 step = funda*o0164 165 #the 'fundamental' range166 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 = 0179 180 else: #onlypos181 tslmajor = self._in_range(fundrange, minval, maxval)182 tslmajor.sort()183 oppaxis = tslmajor[0]184 185 return tslmajor, oppaxis, step186 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_decimal197 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 s204 205 def _tasteless_ticks(self, minval, maxval, num_pieces):206 minval0 = minval207 maxval0 = maxval208 rnd = int(floor(log(maxval - minval)/log(10)))209 if rnd < 0:210 rnd -= 1211 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 = 0221 if maxval <= 0: # only negative222 oppaxis = tslmajor[-1]223 elif minval >= 0:224 oppaxis = tslmajor[0]225 226 return tslmajor, oppaxis, step227 228 def _find_axes(self, minval, maxval):229 """230 Try to find axis tick positions that are well spaced231 """232 if minval >= maxval:233 raise ValueError, "maxval >= minval is required"234 235 # If there is a small differences between max and min values236 # 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] - step243 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, tslmajor246 247 def _draw_axes(self, subplot, axes, xmin, xmax, ymin, ymax, x_axis_ypos, y_axis_xpos):248 from matplotlib import lines249 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 both262 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_labels269 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) * fontsize280 281 #draw y-axis label if there is a y-axis282 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, ymax289 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' method295 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 pleasing299 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 lines314 xmin = float(xmin); xmax=float(xmax); ymin=float(ymin); ymax=float(ymax)315 yspan = ymax - ymin316 xspan = xmax - xmin317 318 #evalute find_axes for x values and y ticks319 y_axis_xpos, xstep, xtslminor, xtslmajor = self._find_axes(xmin, xmax)320 yltheight = 0.015*xspan321 ystheight = 0.25*yltheight322 ylabel = y_axis_xpos - 2*ystheight323 324 #evalute find_axes for y values and x ticks325 x_axis_ypos, ystep, ytslminor, ytslmajor = self._find_axes(ymin, ymax)326 xltheight = 0.015*yspan327 xstheight = 0.25*xltheight328 xlabel = x_axis_ypos - xltheight329 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 labels334 #first draw major tick marks and their corresponding values335 for x in xtslmajor:336 if x == y_axis_xpos:337 continue338 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 marks345 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 labels350 #first draw major tick marks and their corresponding values351 for y in ytslmajor:352 if y == x_axis_ypos:353 continue354 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 marks361 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 labels366 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, ymax371 372 373 def _draw_frame(self, subplot, xmins, xmaxs, ymins, ymaxs):374 """375 Draw a frame around a graphic at the given376 (scaled out) x and y min and max values.377 """378 from matplotlib import lines379 #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 draw403 centered axes with no tick marks.404 405 """406 from matplotlib import lines407 xmin = float(xmin); xmax=float(xmax); ymin=float(ymin); ymax=float(ymax)408 yspan = ymax - ymin409 xspan = xmax - xmin410 411 #evalute find_axes for x values and y ticks412 y_axis_xpos, xstep, xtslminor, xtslmajor = self._find_axes(xmin, xmax)413 yltheight = 0.015 * xspan414 ystheight = 0.25 * yltheight415 #ylabel = y_axis_xpos - 2*ystheight416 ylabel = -2*ystheight417 418 #evalute find_axes for y values and x ticks419 x_axis_ypos, ystep, ytslminor, ytslmajor = self._find_axes(ymin, ymax)420 xltheight = 0.015 * yspan421 xstheight = 0.25 * xltheight422 #xlabel = x_axis_ypos - xltheight423 xlabel = -xltheight424 425 #scale the axes out from the actual plot426 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 ticks432 if axes_with_no_ticks:433 #the x axis line434 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 line438 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 labels442 #first draw major tick marks and their corresponding values443 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 marks449 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 labels457 #first draw major tick marks and their corresponding values458 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 marks464 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 -- numbers476 477 OUTPUT:478 xmin, xmax, ymin, ymax -- numbers479 480 TESTS:481 sage: from sage.plot.axes import Axes482 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 - ymin487 xspan = xmax - xmin488 ys = 0.02*yspan489 xs = 0.02*xspan490 ymin -= ys491 ymax += ys492 xmin -= xs493 xmax += xs494 return xmin, xmax, ymin, ymax495 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 to501 the ith row and jth column of the matrix.502 503 """504 from matplotlib import lines505 xmax = int(xmax)506 ymax = int(ymax)507 508 #evalute find_axes for x values and y ticks509 # the > 14 is just for appearance improvement510 if xmax > 19:511 #get nice tick spacing and assure we get largest point512 tl, opax, step = self._tasteful_ticks(0, xmax)513 #print tl, opax, step514 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*xmax522 ystheight = 0.25*yltheight523 ylabel = -2*ystheight524 525 #evalute find_axes for y values and x ticks526 # the > 14 is just for appearance improvement527 if ymax > 19:528 #get nice tick spacing and assure we get largest point529 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*ymax539 xstheight = 0.25*xltheight540 xlabel = -xltheight541 542 #scale the axes out from the actual plot543 xs = 0.02*xmax544 ys = 0.02*ymax545 xmins = -xs546 xmaxs = xmax + xs547 ymins = -ys548 ymaxs = ymax + ys549 550 #now draw the frame border:551 self._draw_frame(subplot, xmins, xmaxs, ymins, ymaxs)552 553 #the x-axis ticks and labels554 #first draw major tick marks and their corresponding values555 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 labels567 #first draw major tick marks and their corresponding values568 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 lines594 at major ticks of the axes.595 3. "minor": add grid at major and minor ticks.596 4. [xlist,ylist]: a tuple or list containing597 two elements, where xlist (or ylist) can be598 any of the following.599 4a. None, False: don't add horizontal (or600 vertical) lines.601 4b. True, "automatic", "major": add602 horizontal (or vertical) grid lines at603 the major ticks of the axes.604 4c. "minor": add horizontal (or vertical)605 grid lines at major and minor ticks of606 axes.607 4d. an iterable yielding numbers n or pairs608 (n,opts), where n is the coordinate of609 the line and opt is a dictionary of610 MATPLOTLIB options for rendering the611 line.612 gridlinesstyle,613 hgridlinesstyle,614 vgridlinesstyle615 -- (default: None) a dictionary of MATPLOTLIB616 options for the rendering of the grid lines,617 the horizontal grid lines or the vertical grid618 lines, respectively.619 620 TESTS:621 sage: from sage.plot.axes import GridLines622 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 = gridlines639 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.Subplot659 xmin, xmax -- $x$ range of the Graphics object containing subplot660 ymin, ymax -- $y$ range of the Graphics object containing subplot661 frame -- (default: False) if True, then adjust the lengths of662 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 GridLines669 sage: from matplotlib.figure import Figure670 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 0677 678 sage: subplot = Figure().add_subplot(111)679 sage: GridLines(False).add_gridlines(subplot,*lims)680 sage: len(subplot.lines)681 0682 683 sage: subplot = Figure().add_subplot(111)684 sage: GridLines(True).add_gridlines(subplot,*lims)685 sage: len(subplot.lines)686 13687 688 sage: subplot = Figure().add_subplot(111)689 sage: GridLines("automatic").add_gridlines(subplot,*lims)690 sage: len(subplot.lines)691 13692 693 sage: subplot = Figure().add_subplot(111)694 sage: GridLines("major").add_gridlines(subplot,*lims)695 sage: len(subplot.lines)696 13697 698 sage: subplot = Figure().add_subplot(111)699 sage: GridLines("minor").add_gridlines(subplot,*lims)700 sage: len(subplot.lines)701 57702 703 sage: subplot = Figure().add_subplot(111)704 sage: GridLines([True,False]).add_gridlines(subplot,*lims)705 sage: len(subplot.lines)706 7707 708 sage: subplot = Figure().add_subplot(111)709 sage: GridLines(["minor","major"]).add_gridlines(subplot,*lims)710 sage: len(subplot.lines)711 37712 713 sage: subplot = Figure().add_subplot(111)714 sage: GridLines(["automatic",None]).add_gridlines(subplot,*lims)715 sage: len(subplot.lines)716 7717 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 60722 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 13729 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 2735 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 return745 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]*2758 gridlines[0], gridlines[1] = self.__gridlines759 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__') and773 hasattr(gridlines[1],'__iter__')):774 raise TypeError, "elements of gridlines need to be iterable: %s" \775 % gridlines776 self.__gridlines = gridlines777 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 = entry796 kwds = copy(self.__gridlinesstyle[i])797 new_list.append([val,kwds])798 new_gridlines.append(new_list)799 xlines, ylines = new_gridlines800 801 # draw the grid lines802 from matplotlib import lines803 # horizontal lines804 for (yval, ykwds) in ylines:805 subplot.add_line(806 lines.Line2D(points[0],[yval,yval],**ykwds)807 )808 # vertical lines809 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 axes817 in the interval.818 819 INPUT:820 interval -- an interval as a pair of numbers821 ticks -- "major" or "minor". If "minor", then return also822 the locations of the minor ticks.823 824 OUTPUT:825 list -- the locations of the ticks on the axes826 827 TESTS:828 sage: from sage.plot.axes import GridLines829 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 ticks836 # Axes._find_axes[3] returns locations of major ticks837 if ticks == "minor":838 minorticks = True839 else:840 minorticks = False841 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 the846 plot.847 848 INPUT:849 xinterval -- x-axis interval as pairs of numbers850 yinterval -- y-axis interval as pairs of numbers851 852 OUTPUT:853 xmin, xmax, ymin, ymax -- numbers854 855 TESTS:856 sage: from sage.plot.axes import GridLines857 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 862 27 def rgbcolor_keyword_support(d): 863 28 r""" 864 29 Change the rgbcolor key to color. … … 904 69 else: 905 70 d["color"] = d["rgbcolor"] 906 71 del d["rgbcolor"] 72 return d -
sage/plot/contour_plot.py
diff -r af0cd051387b -r bdf61c76a98e sage/plot/contour_plot.py
a b 157 157 158 158 g = Graphics() 159 159 g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options)) 160 g.axes(False) 160 161 return g 161 162 162 163 @options(contours=(0,0), fill=False) -
sage/plot/matrix_plot.py
diff -r af0cd051387b -r bdf61c76a98e sage/plot/matrix_plot.py
a b 140 140 141 141 g = Graphics() 142 142 g.add_primitive(MatrixPlot(xy_data_array, xrange, yrange, options)) 143 g.axes(False) 143 144 return g -
sage/plot/plot.py
diff -r af0cd051387b -r bdf61c76a98e sage/plot/plot.py
a b 314 314 return SHOW_DEFAULT 315 315 SHOW_DEFAULT = bool(default) 316 316 317 do_verify = True318 319 317 from sage.misc.randstate import current_randstate #for plot adaptive refinement 320 318 import os #for viewing and writing images 321 319 from colorsys import hsv_to_rgb #for the hue function … … 327 325 import sage.misc.misc 328 326 329 327 from misc import rgbcolor, Color, options, rename_keyword, to_mpl_color 330 328 from sage.misc.cachefunc import cached_method 331 329 import operator 332 330 333 331 ############### WARNING ### … … 337 335 # Sage startup times are much improved.) - William 338 336 ############### 339 337 340 #Sage 2D Graphics Axes class:341 from axes import Axes342 from axes import GridLines343 344 338 def is_Graphics(x): 345 339 """ 346 340 Return True if `x` is a Graphics object. … … 388 382 389 383 sage: G = Graphics() 390 384 """ 391 self.__aspect_ratio = None392 self.__fontsize = 10393 self.__show_axes = True394 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.8398 385 self.__objects = [] 399 386 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 400 401 def set_aspect_ratio(self, ratio): 401 402 """ 402 403 Set the aspect ratio. … … 433 434 ratio = float(ratio) 434 435 if ratio <= 0: 435 436 raise ValueError, "the aspect ratio must be positive" 436 self. __aspect_ratio = ratio437 self.set_saved_option('aspect_ratio', ratio) 437 438 438 439 def aspect_ratio(self): 439 440 """ … … 451 452 sage: P.aspect_ratio() 452 453 2.0 453 454 """ 454 return self. __aspect_ratio455 return self.get_saved_option('aspect_ratio') 455 456 456 457 def get_axes_range(self): 457 458 """ … … 475 476 [('xmax', 3.0), ('xmin', -1.0), ('ymax', 5.0), ('ymin', -4.0)] 476 477 """ 477 478 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 479 483 return axes_range 480 484 481 485 def set_axes_range(self, xmin=None, xmax=None, ymin=None, ymax=None): … … 484 488 485 489 INPUT: 486 490 487 488 491 - ``xmin, xmax, ymin, ymax`` - floats 489 492 490 491 493 EXAMPLES:: 492 494 493 495 sage: L = line([(1,2), (3,-4), (2, 5), (1,2)]) … … 497 499 (-1.0, 20.0, 0.0, 2.0) 498 500 """ 499 501 l = locals() 500 axes_range = self._get_axes_range_dict()501 502 for name in ['xmin', 'xmax', 'ymin', 'ymax']: 502 503 if l[name] is not None: 503 axes_range[name] = float(l[name])504 self.set_saved_option(name, float(l[name])) 504 505 505 506 axes_range = set_axes_range 506 507 507 def _get_axes_range_dict(self):508 """509 Returns the underlying dictionary used to store the user's510 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_range523 except AttributeError:524 self.__axes_range = {}525 return self.__axes_range526 527 508 def fontsize(self, s=None): 528 509 """ 529 Set the font size of axes labels and tick marks. 510 DEPRECATED: 511 512 Set the font size of axes labels. 530 513 531 514 INPUT: 532 533 534 515 - ``s`` - integer, a font size in points. 535 516 536 537 517 If called with no input, return the current fontsize. 538 518 539 519 EXAMPLES:: … … 549 529 550 530 sage: L 551 531 """ 532 552 533 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)) 559 537 560 538 def axes(self, show=None): 561 539 """ … … 564 542 565 543 INPUT: 566 544 567 568 545 - ``show`` - bool 569 546 570 571 547 If called with no input, return the current axes setting. 572 548 573 549 EXAMPLES:: … … 596 572 sage: L 597 573 """ 598 574 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)) 605 578 606 579 def axes_color(self, c=None): 607 580 """ … … 641 614 sage: L 642 615 """ 643 616 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)) 651 620 652 621 def axes_labels(self, l=None): 653 622 """ … … 680 649 sage: p 681 650 """ 682 651 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)) 693 659 694 660 def axes_label_color(self, c=None): 695 661 r""" … … 735 701 sage: p 736 702 """ 737 703 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)) 745 707 746 708 def axes_width(self, w=None): 747 709 r""" … … 776 738 sage: p 777 739 """ 778 740 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)) 785 744 786 745 def tick_label_color(self, c=None): 787 746 """ … … 807 766 sage: p 808 767 """ 809 768 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)) 816 772 817 773 def _repr_(self): 818 774 r""" … … 987 943 988 944 This only works when other is a Python int equal to 0. In all other 989 945 cases a TypeError is raised. The main reason for this function is 990 to make sum ing a list of graphics objects easier.946 to make summing a list of graphics objects easier. 991 947 992 948 EXAMPLES:: 993 949 … … 1033 989 raise TypeError, "other (=%s) must be a Graphics objects"%other 1034 990 g = Graphics() 1035 991 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'))) 1037 995 return g 1038 996 1039 997 def add_primitive(self, primitive): … … 1071 1029 g = g.translate(0,0,z) 1072 1030 return g 1073 1031 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): 1080 1033 """ 1081 1034 Show this graphics image with the default image viewer. 1082 1035 … … 1269 1222 sage: matrix_plot(M).show(gridlines=True) 1270 1223 """ 1271 1224 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'])) 1299 1234 1300 1235 def xmin(self, xmin=None): 1301 1236 """ … … 1404 1339 ymin -= 1 1405 1340 ymax += 1 1406 1341 return {'xmin':xmin, 'xmax':xmax, 'ymin':ymin, 'ymax':ymax} 1342 1343 def get_filename_and_extension(self, **options): 1344 """ 1407 1345 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 1390 p """ 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): 1415 1572 r""" 1416 1573 Save the graphics to an image file of type: PNG, PS, EPS, SVG, 1417 1574 SOBJ, depending on the file extension you give the filename. … … 1435 1592 1436 1593 sage: point((-1,1),pointsize=30, rgbcolor=(1,0,0)) 1437 1594 """ 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) 1465 1596 if ext == '' or ext == '.sobj': 1466 1597 SageObject.save(self, filename) 1467 1598 return 1599 else: 1600 options['filename'] = filename 1468 1601 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) 1598 1603 1599 1604 def xydata_from_point_list(points): 1600 1605 r""" … … 2413 2418 #make a blank matplotlib Figure: 2414 2419 from matplotlib.figure import Figure 2415 2420 figure = Figure(figsize) 2416 global do_verify2417 do_verify = True2418 2421 for i,g in zip(range(1, dims+1), glist): 2419 2422 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), 2422 2425 axes = axes, 2423 2426 **args)#only save if i==dims. 2424 2427 … … 2432 2435 self._render(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args) 2433 2436 2434 2437 def show(self, filename=None, dpi=DEFAULT_DPI, figsize=DEFAULT_FIGSIZE, 2435 axes = None, ** args):2438 axes = None, **kwds): 2436 2439 r""" 2437 2440 Show this graphics array using the default viewer. 2438 2441 … … 2464 2467 """ 2465 2468 if (figsize != DEFAULT_FIGSIZE): self.__set_figsize__(figsize) 2466 2469 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) 2469 2472 return 2470 2473 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) 2472 2475 return 2473 2476 if filename is None: 2474 2477 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) 2476 2479 os.system('%s %s 2>/dev/null 1>/dev/null &'%( 2477 2480 sage.misc.viewer.browser(), filename)) 2478 2481 -
sage/plot/primitive.py
diff -r af0cd051387b -r bdf61c76a98e sage/plot/primitive.py
a b 65 65 """ 66 66 Translate 2d plot options into 3d plot options. 67 67 """ 68 if options ==None:68 if options is None: 69 69 options = self.options() 70 70 options_3d = {} 71 71 if 'rgbcolor' in options: 72 options_3d['rgbcolor'] = options['rgbcolor'] 73 del options['rgbcolor'] 72 options_3d['rgbcolor'] = options.pop('rgbcolor') 74 73 if 'alpha' in options: 75 options_3d['opacity'] = options['alpha'] 76 del options['alpha'] 74 options_3d['opacity'] = options.pop('alpha') 77 75 if len(options) != 0: 78 76 raise NotImplementedError, "Unknown plot3d equivalent for %s" % ", ".join(options.keys()) 79 77 return options_3d … … 93 91 sage: GraphicPrimitive({}).options() 94 92 {} 95 93 """ 96 from sage.plot.plot import do_verify,hue94 from sage.plot.plot import hue 97 95 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) 115 110 116 111 if 'hue' in O: 117 112 t = O['hue']