Ticket #12827: trac_12827_animate3d_save_image.patch

File trac_12827_animate3d_save_image.patch, 24.3 KB (added by niles, 14 months ago)

a rudimentary save_image method

  • sage/plot/all.py

    # HG changeset patch
    # User Niles Johnson
    # Date 1334691602 14400
    # Node ID 5d73e5413c6fe84d03e265308f75955e2cebdefd
    # Parent  c239be1054e01526a1b0b62da6691061b9dd5587
    Trac ticket 12827: implements save_image method for major graphics types to allow animating of those types
    
    diff --git a/sage/plot/all.py b/sage/plot/all.py
    a b  
    2626 
    2727from arc import arc 
    2828 
    29 from animate import Animation as animate 
     29from animate import animate 
    3030 
    3131from plot3d.tachyon import Tachyon 
    3232 
  • sage/plot/animate.py

    diff --git a/sage/plot/animate.py b/sage/plot/animate.py
    a b  
    1 """ 
     1r""" 
    22Animated plots 
    33 
     4Animations are generated from a list (or other iteratable) of graphics 
     5objects.  Images are produced by calling the ``save_image`` method on 
     6each input object, and using ImageMagick's ``convert`` program [IM] or 
     7``ffmpeg`` [FF] to generate an animation.  The output format is GIF by 
     8default, but can be any of the formats supported by ``convert`` or 
     9``ffmpeg``. 
     10 
     11.. Warning:: 
     12 
     13    Note that ImageMagick and FFmpeg are not included with Sage, and 
     14    must be installed by the user.  On unix systems, type ``which 
     15    convert`` at a command prompt to see if ``convert`` is installed. 
     16    If it is, you will be given its location.  See [IM] or [FF] for 
     17    installation instructions. 
     18 
     19 
    420EXAMPLES: 
    5 We plot a circle shooting up to the right:: 
    621 
    7     sage: a = animate([circle((i,i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],  
     22The sine function:: 
     23 
     24    sage: sines = [plot(c*sin(x), (-2*pi,2*pi), color=Color(c,0,0), ymin=-1,ymax=1) for c in sxrange(0,1,.2)] 
     25    sage: a = animate(sines) 
     26    sage: a 
     27    Animation with 5 frames 
     28    sage: a.show()  # not tested 
     29 
     30An animated :class:`sage.plot.plot.GraphicsArray` of rotating ellipses:: 
     31 
     32    sage: ellipses = (graphics_array([[ellipse((0,0),a,b,angle=t,xmin=-3,xmax=3)+circle((0,0),3,color='blue') for a in range(1,3)] for b in range(2,4)]) for t in sxrange(0,pi/4,.15)) 
     33    sage: E = animate(ellipses) 
     34    sage: E   # animations produced from a generator do not have a known length 
     35    Animation with unknown number of frames 
     36    sage: E.show()  # not tested 
     37 
     38A simple animation of a circle shooting up to the right:: 
     39 
     40    sage: shoot = animate([circle((i,i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],  
    841    ...               xmin=0,ymin=0,xmax=2,ymax=2,figsize=[2,2]) 
    9     sage: a.show() # optional -- ImageMagick 
     42    sage: shoot.show()  # optional -- ImageMagick 
     43 
     44An animation of 3d objects:: 
     45 
     46    sage: var('s,t') 
     47    (s, t) 
     48    sage: def sphere_and_plane(x): 
     49    ...    return sphere((0,0,0),1,color='red',opacity=.5)+parametric_plot3d([t,x,s],(s,-1,1),(t,-1,1),color='green',opacity=.7) 
     50    ... 
     51    sage: sp = animate([sphere_and_plane(x) for x in sxrange(-1,1,.3)]) 
     52    sage: sp[0]      # first frame 
     53    sage: sp[-1]     # last frame 
     54    sage: sp.show()  # not tested 
     55 
     56If the input objects do not have a ``save_image`` method, then the 
     57animation object attempts to make an image by calling its internal 
     58method :meth:`sage.plot.animate.Animation.make_image`.  This is 
     59illustrated by the following example:: 
     60 
     61 
     62    sage: t = var('t') 
     63    sage: a = animate((sin(c*pi*t) for c in sxrange(1,2,.2))) 
     64    sage: a.show()  # optional -- ImageMagick 
     65 
     66 
     67 
     68REFERENCES: 
     69 
     70.. [IM] http://www.imagemagick.org 
     71 
     72.. [FF]  http://www.ffmpeg.org 
     73 
    1074""" 
    1175 
    1276############################################################################ 
     
    2387import sage.misc.misc 
    2488import sage.misc.viewer 
    2589 
     90 
     91def animate(frames, **kwds): 
     92    r""" 
     93    Animate a list of frames by creating a 
     94    :class:`sage.plot.animate.Animation` object. 
     95 
     96    EXAMPLES:: 
     97 
     98        sage: t = var('t') 
     99        sage: a = animate((cos(c*pi*t) for c in sxrange(1,2,.2))) 
     100        sage: a.show()  # optional -- ImageMagick 
     101    """ 
     102    return Animation(frames, **kwds) 
     103 
    26104class Animation(SageObject): 
    27105    r""" 
    28106    Return an animation of a sequence of plots of objects. 
    29      
     107 
    30108    INPUT: 
    31109     
    32      
    33     -  ``v`` - list of Sage objects. These should 
    34        preferably be graphics objects, but if they aren't then plot is 
     110    - ``v`` - iterable of Sage objects. These should preferably be 
     111       graphics objects, but if they aren't then :meth:`make_image` is 
    35112       called on them. 
    36      
    37     -  ``xmin, xmax, ymin, ymax`` - the ranges of the x and 
    38        y axes. 
    39      
    40     -  ``**kwds`` - all additional inputs are passed onto 
    41        the rendering command. E.g., use figsize to adjust the resolution 
    42        and aspect ratio. 
    43      
     113                                                                                 
     114    - ``xmin, xmax, ymin, ymax`` - the ranges of the x and y axes.              
     115                                                                                 
     116    - ``**kwds`` - all additional inputs are passed onto the rendering           
     117       command. E.g., use figsize to adjust the resolution and aspect            
     118       ratio.     
    44119     
    45120    EXAMPLES:: 
    46121     
     
    53128        sage: a.show()          # optional -- ImageMagick 
    54129        sage: a[:5].show()      # optional -- ImageMagick 
    55130     
    56     The ``show`` function takes arguments to specify the 
     131    The :meth:`show` function takes arguments to specify the 
    57132    delay between frames (measured in hundredths of a second, default 
    58133    value 20) and the number of iterations (default value 0, which 
    59134    means to iterate forever). To iterate 4 times with half a second 
     
    72147        sage: a = animate(v, xmin=0, ymin=0) 
    73148        sage: a.show() # optional -- ImageMagick 
    74149        sage: show(L) 
     150 
     151 
     152    TESTS: 
    75153     
    76     TESTS: This illustrates that ticket #2066 is fixed (setting axes 
    77     ranges when an endpoint is 0):: 
     154    This illustrates that ticket #2066 is fixed (setting axes ranges 
     155    when an endpoint is 0):: 
    78156     
    79157        sage: animate([plot(sin, -1,1)], xmin=0, ymin=0)._Animation__kwds['xmin'] 
    80158        0 
     
    85163        ...            for k in srange(0,2*pi,0.3)]) 
    86164        sage: a.show() # optional -- ImageMagick 
    87165    """ 
    88     def __init__(self, v, **kwds): 
     166    def __init__(self, v=None, **kwds): 
    89167        r""" 
    90         Return an animation of a sequence of plots of objects. 
    91  
    92         See documentation of ``animate`` for more details and examples.  
     168        Return an animation of a sequence of plots of objects.  See 
     169        documentation of :func:`animate` for more details and examples. 
    93170 
    94171        EXAMPLES:: 
    95172 
    96173            sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.3)],  
    97             ...                xmin=0, xmax=2*pi, figsize=[2,1]) 
     174            ...                xmin=0, xmax=2*pi, figsize=[2,1])  # indirect doctest 
    98175            sage: a 
    99176            Animation with 21 frames 
    100177        """ 
    101         w = [] 
    102         for x in v: 
    103             if not isinstance(x, plot.Graphics): 
    104                 x = plot.plot(x, (kwds.get('xmin',0), kwds.get('xmax', 1))) 
    105             w.append(x) 
    106         if len(w) == 0: 
    107             w = [plot.Graphics()] 
    108         self.__frames = w 
     178        # if typecheck_input: 
     179        #     w = [] 
     180        #     for x in v: 
     181        #         if not is_graphic(x): 
     182        #             x = plot.plot(x, (kwds.get('xmin',0), kwds.get('xmax', 1))) 
     183        #         w.append(x) 
     184        #     if len(w) == 0: 
     185        #         w = [plot.Graphics()] 
     186        # else: 
     187        #     w = v 
     188        self.__frames = v 
    109189        self.__kwds = kwds 
    110190 
    111191    def _combine_kwds(self, *kwds_tuple): 
    112192        """ 
    113193        Returns a dictionary which is a combination of the all the 
    114         dictionaries in kwds_tuple. This also does the appropriate thing 
    115         for taking the mins and maxes of all of the x/y mins/maxes. 
    116          
     194        dictionaries in kwds_tuple. This also does the appropriate 
     195        thing for taking the mins and maxes of all of the x/y 
     196        mins/maxes. 
     197 
    117198        EXAMPLES:: 
    118199         
    119200            sage: a = animate([plot(sin, -1,1)], xmin=0, ymin=0) 
     
    143224        EXAMPLES:: 
    144225         
    145226            sage: a = animate([x, x^2, x^3, x^4]) 
    146             sage: a[2].show()       # optional -- ImageMagick 
     227            sage: frame2 = a[2]     # indirect doctest 
     228            sage: frame2.show()       # optional -- ImageMagick 
    147229        """ 
    148230        return self.__frames[i] 
    149231 
     
    158240            sage: a 
    159241            Animation with 10 frames 
    160242            sage: a.show() # optional -- ImageMagick 
    161             sage: a[3:7] 
     243            sage: a[3:7]   # indirect doctest 
    162244            Animation with 4 frames 
    163245            sage: a[3:7].show() # optional -- ImageMagick 
    164246        """ 
     
    177259            sage: a._repr_() 
    178260            'Animation with 10 frames' 
    179261        """ 
    180         return "Animation with %s frames"%(len(self.__frames)) 
     262        try: 
     263            num = len(self) 
     264        except TypeError: 
     265            num = "unknown number of" 
     266        return "Animation with %s frames"%num 
     267 
    181268 
    182269    def __add__(self, other): 
    183270        """ 
    184         Add two animations. This has the effect of superimposing the two 
    185         animations frame-by-frame. 
     271        Add two animations. This has the effect of superimposing the 
     272        two animations frame-by-frame. 
    186273         
    187         EXAMPLES: We add and multiply two animations. 
    188          
    189         :: 
     274        EXAMPLES:: 
    190275         
    191276            sage: a = animate([circle((i,0),1) for i in srange(0,2,0.4)],  
    192277            ...                xmin=0, ymin=-1, xmax=3, ymax=1, figsize=[2,1]) 
     
    194279            sage: b = animate([circle((0,i),1,hue=0) for i in srange(0,2,0.4)],  
    195280            ...                xmin=0, ymin=-1, xmax=1, ymax=3, figsize=[1,2]) 
    196281            sage: b.show() # optional 
    197             sage: (a*b).show()    # optional -- ImageMagick 
    198             sage: (a+b).show()    # optional -- ImageMagick 
     282            sage: s = a+b         # indirect doctest 
     283            sage: len(a), len(b) 
     284            (5, 5) 
     285            sage: len(s) 
     286            5 
     287            sage: s.show()    # optional -- ImageMagick 
    199288        """ 
    200289        if not isinstance(other, Animation): 
    201290            other = Animation(other) 
     
    211300 
    212301    def __mul__(self, other): 
    213302        """ 
    214         Multiply two animations. This has the effect of appending the two 
    215         animations (the second comes after the first). 
     303        Multiply two animations. This has the effect of appending the 
     304        two animations (the second comes after the first). 
    216305         
    217         EXAMPLES: We add and multiply two animations. 
    218          
    219         :: 
     306        EXAMPLES:: 
    220307         
    221308            sage: a = animate([circle((i,0),1,thickness=20*i) for i in srange(0,2,0.4)],  
    222309            ...                xmin=0, ymin=-1, xmax=3, ymax=1, figsize=[2,1], axes=False) 
    223310            sage: a.show()     # optional -- ImageMagick 
    224311            sage: b = animate([circle((0,i),1,hue=0,thickness=20*i) for i in srange(0,2,0.4)],  
    225312            ...                xmin=0, ymin=-1, xmax=1, ymax=3, figsize=[1,2], axes=False) 
    226             sage: b.show()             # optional -- ImageMagick 
    227             sage: (a*b).show()         # optional -- ImageMagick 
    228             sage: (a+b).show()         # optional -- ImageMagick 
     313            sage: p = a*b         # indirect doctest 
     314            sage: len(a), len(b) 
     315            (5, 5) 
     316            sage: len(p) 
     317            10 
     318            sage: p.show()    # optional -- ImageMagick 
    229319        """ 
    230320        if not isinstance(other, Animation): 
    231321            other = Animation(other) 
     
    234324         
    235325        return Animation(self.__frames + other.__frames, **kwds) 
    236326 
     327    def __len__(self): 
     328        """ 
     329        Length of self 
     330 
     331        EXAMPLES:: 
     332            sage: a = animate([circle((i,0),1,thickness=20*i) for i in srange(0,2,0.4)],  
     333            ...                xmin=0, ymin=-1, xmax=3, ymax=1, figsize=[2,1], axes=False) 
     334            sage: len(a) 
     335            5 
     336        """ 
     337        return len(self.__frames) 
     338     
     339    def duration(self): 
     340        """ 
     341        This method is not yet implemented. 
     342        Return duration of this animation as a Python ``timedelta``. 
     343 
     344        EXAMPLES:: 
     345 
     346            sage: a = animate([x, x^2, x^3, x^4]) 
     347            sage: a.duration()  # not tested 
     348        """ 
     349        #from datetime import timedelta 
     350        raise NotImplementedError('duration is not implemented') 
     351     
     352    def make_image(self, frame, filename, **kwds): 
     353        r""" 
     354        Given a frame which has no ``save_image()`` method, make a graphics 
     355        object and save it as an image with the given filename.  By default, this is 
     356        :meth:`sage.plot.plot.plot`.  To make animations of other objects, 
     357        override this method in a subclass. 
     358 
     359        EXAMPLES:: 
     360 
     361            sage: from sage.plot.animate import Animation 
     362            sage: class MyAnimation(Animation):  
     363            ...    def make_image(self, frame, filename, **kwds): 
     364            ...        P = parametric_plot(frame[0], frame[1], **frame[2]) 
     365            ...        P.save_image(filename,**kwds) 
     366             
     367            sage: t = var('t') 
     368            sage: x = lambda t: cos(t) 
     369            sage: y = lambda n,t: sin(t)/n 
     370            sage: B = MyAnimation([([x(t), y(i+1,t)],(t,0,1), {'color':Color((1,0,i/4)), 'aspect_ratio':1, 'ymax':1}) for i in range(4)]) 
     371 
     372            sage: d = B.png() 
     373            sage: v = os.listdir(d); v.sort(); v 
     374            ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] 
     375            sage: B.show()  #not tested 
     376 
     377            sage: class MyAnimation(Animation): 
     378            ...    def make_image(self, frame, filename, **kwds): 
     379            ...        G = frame.plot() 
     380            ...        G.set_axes_range(floor(G.xmin()),ceil(G.xmax()),floor(G.ymin()),ceil(G.ymax())) 
     381            ...        G.save_image(filename, **kwds) 
     382 
     383            sage: B = MyAnimation([graphs.CompleteGraph(n) for n in range(7,11)], figsize=5)  
     384            sage: d = B.png() 
     385            sage: v = os.listdir(d); v.sort(); v 
     386            ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] 
     387            sage: B.show()  #not tested 
     388 
     389        """ 
     390        p = plot.plot(frame) 
     391        p.save_image(filename, **kwds) 
     392 
    237393    def png(self, dir=None): 
    238         """ 
    239         Return the absolute path to a temp directory that contains the 
    240         rendered PNG's of all the images in this animation. 
     394        r""" 
     395        Render PNG images of the frames in this animation, saving them 
     396        in `dir`.  Return the absolute path to that directory.  If the 
     397        frames have been previously rendered, just return the 
     398        directory in which they are stored.  In this case, the input 
     399        `dir` is ignored. 
     400 
     401        INPUT:: 
     402 
     403            - dir -- Directory in which to store frames.  Default 
     404              `None`; in this case, a temporary directory will be 
     405              created for storing the frames. 
    241406         
    242407        EXAMPLES:: 
    243408         
     
    250415            return self.__png_dir 
    251416        except AttributeError: 
    252417            pass 
    253         d = sage.misc.misc.tmp_dir() 
     418        if dir is None: 
     419            d = sage.misc.misc.tmp_dir() 
     420        else: 
     421            d = dir 
    254422        G = self.__frames 
    255423        for i, frame in enumerate(self.__frames): 
     424            # if not is_graphic(frame): 
     425            #     frame = self.make_graphic(frame) 
    256426            filename = '%s/%s'%(d,sage.misc.misc.pad_zeros(i,8)) 
    257             frame.save(filename + '.png', **self.__kwds) 
     427            # frame.save(filename + '.png', **self.__kwds) 
     428            try: 
     429                frame.save_image(filename + '.png', **self.__kwds) 
     430            except AttributeError: 
     431                self.make_image(frame, filename + '.png', **self.__kwds) 
    258432        self.__png_dir = d 
    259433        return d 
    260434 
    261435    def graphics_array(self, ncols=3): 
    262436        """ 
    263         Return a graphics array with the given number of columns with plots 
    264         of the frames of this animation. 
     437        Return a :class:`sage.plot.plot.GraphicsArray` with plots of the 
     438        frames of this animation, using the given number of columns. 
     439        The frames must be acceptable inputs for 
     440        :class:`sage.plot.plot.GraphicsArray`. 
    265441         
    266442        EXAMPLES:: 
    267443         
     
    285461            sage: print g 
    286462            Graphics Array of size 2 x 2 
    287463            sage: g.show('sage.png')         # optional 
     464 
     465        :: 
     466 
     467            sage: T = animate([tetrahedron(size=s)+cube(size=1,opacity=.1) for s in sxrange(.2,.6,.15)]) 
     468            sage: d = T.png()  # long time 
     469            sage: T.graphics_array()  # long time 
     470            Traceback (most recent call last) 
     471            ... 
     472            TypeError: 'Graphics3dGroup' object is not iterable 
    288473        """ 
    289474        n = len(self.__frames) 
    290475        ncols = int(ncols) 
     
    299484        This function will only work if either (a) the ImageMagick 
    300485        software suite is installed, i.e., you have the ``convert`` 
    301486        command or (b) ``ffmpeg`` is installed.  See 
    302         www.imagemagick.org for more about ImageMagic, and see 
     487        www.imagemagick.org for more about ImageMagick, and see 
    303488        www.ffmpeg.org for more about ``ffmpeg``.  By default, this 
    304489        produces the gif using ``convert`` if it is present.  If this 
    305490        can't find ``convert`` or if ``use_ffmpeg`` is True, then it 
     
    322507        - ``use_ffmpeg`` - boolean (default: False); if True, use 
    323508          'ffmpeg' by default instead of 'convert'. 
    324509 
    325         If savefile is not specified: in notebook mode, display the 
     510        If ``savefile`` is not specified: in notebook mode, display the 
    326511        animation; otherwise, save it to a default file name. 
    327512 
    328513        EXAMPLES:: 
     
    388573                    print "Animation saved to file %s." % savefile 
    389574            except (CalledProcessError, OSError): 
    390575                msg = """ 
    391 Error: Neither ImageMagick nor ffmpeg appears to be installed. Saving an 
    392 animation to a GIF file or displaying an animation requires one of these 
    393 packages, so please install one of them and try again. 
     576Error: Cannot generate GIF animation.  Verify that convert (ImageMagick) or ffmpeg is installed, and that the objects passed to the animate command can be saved in PNG image format. 
    394577 
    395578See www.imagemagick.org and www.ffmpeg.org for more information.""" 
     579 
    396580                raise OSError, msg 
    397581 
    398582    def show(self, delay=20, iterations=0): 
     
    495679          suffix to use for the video.  This may be 'mpg', 'mpeg', 
    496680          'avi', 'gif', or any other format that ffmpeg can handle. 
    497681          If this is None and the user specifies ``savefile`` with a 
    498           suffix, say 'savefile=animation.avi', try to determine the 
     682          suffix, say ``savefile=animation.avi``, try to determine the 
    499683          format ('avi' in this case) from that file name.  If no file 
    500684          is specified or if the suffix cannot be determined, 'mpg' is 
    501685          used. 
     
    515699        - ``verbose`` - boolean (default: False); if True, print 
    516700          messages produced by the ffmpeg command. 
    517701 
    518         If savefile is not specified: in notebook mode, display the 
     702        If ``savefile`` is not specified: in notebook mode, display the 
    519703        animation; otherwise, save it to a default file name. 
    520704 
    521705        EXAMPLES:: 
  • sage/plot/plot.py

    diff --git a/sage/plot/plot.py b/sage/plot/plot.py
    a b  
    23852385        # free limits autoscale 
    23862386        #subplot.autoscale_view(tight=True)  
    23872387        return figure 
    2388          
     2388 
     2389 
     2390    def save_image(self, filename=None, *args, **kwds): 
     2391        r""" 
     2392        Save an image representation of self.  The image type is 
     2393        determined by the extension of the filename.  For example, 
     2394        this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``, 
     2395        ``.svg``.  Currently this is implemented by calling the 
     2396        :meth:`save` method of self, passing along all arguments and 
     2397        keywords. 
     2398 
     2399        .. Note:: 
     2400 
     2401            Not all image types are necessarily implemented for all 
     2402            graphics types.  See :meth:`save` for more details. 
     2403 
     2404        EXAMPLES:: 
     2405 
     2406            sage: c = circle((1,1), 1, color='red') 
     2407            sage: filename = os.path.join(SAGE_TMP, 'test.png') 
     2408            sage: c.save_image(filename, xmin=-1, xmax=3, ymin=-1, ymax=3) 
     2409        """ 
     2410        self.save(filename, *args, **kwds) 
     2411 
     2412 
    23892413    # ALLOWED_EXTENSIONS is the list of recognized formats. 
    23902414    # filename argument is written explicitly so that it can be used as a  
    23912415    # positional one, which is a very likely usage for this function. 
     
    38173841        g.save(filename, dpi=dpi, figure=figure, sub=subplot, 
    38183842               verify=do_verify, axes = axes, **args) 
    38193843 
     3844    def save_image(self, filename=None, *args, **kwds): 
     3845        r""" 
     3846        Save an image representation of self.  The image type is 
     3847        determined by the extension of the filename.  For example, 
     3848        this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``, 
     3849        ``.svg``.  Currently this is implemented by calling the 
     3850        :meth:`save` method of self, passing along all arguments and 
     3851        keywords. 
     3852 
     3853        .. Note:: 
     3854 
     3855            Not all image types are necessarily implemented for all 
     3856            graphics types.  See :meth:`save` for more details. 
     3857 
     3858        EXAMPLES:: 
     3859 
     3860            sage: plots = [[plot(m*cos(x + n*pi/4), (x,0, 2*pi)) for n in range(3)] for m in range(1,3)] 
     3861            sage: G = graphics_array(plots) 
     3862            sage: G.save_image(tmp_filename()+'.png') 
     3863        """ 
     3864        self.save(filename, *args, **kwds) 
     3865 
    38203866    def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, 
    38213867             axes = None, **args): 
    38223868        """ 
    3823         save the ``graphics_array`` to (for now) a png called 
     3869        Save the ``graphics_array`` to (for now) a png called 
    38243870        'filename'. 
     3871 
     3872        EXAMPLES:: 
     3873 
     3874            sage: plots = [[parametric_plot([(1/m)*cos(x),(1/n)*sin(x)], (x,0, 2*pi)) for n in range(1,3)] for m in range(1,3)] 
     3875            sage: G = graphics_array(plots) 
     3876            sage: G.save(tmp_filename()+'.png') 
    38253877        """ 
    38263878        if (figsize is not None): self.__set_figsize__(figsize)  
    38273879        self._render(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args) 
  • sage/plot/plot3d/base.pyx

    diff --git a/sage/plot/plot3d/base.pyx b/sage/plot/plot3d/base.pyx
    a b  
    11481148                pipes = "2>/dev/null 1>/dev/null &" 
    11491149            os.system('%s "%s.%s" %s' % (viewer_app, filename, ext, pipes)) 
    11501150 
     1151    def save_image(self, filename=None, *args, **kwds): 
     1152        r""" 
     1153        Save an image representation of self.  The image type is 
     1154        determined by the extension of the filename.  For example, 
     1155        this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``, 
     1156        ``.svg``.  Currently this is implemented by calling the 
     1157        :meth:`save` method of self, passing along all arguments and 
     1158        keywords. 
     1159 
     1160        .. Note:: 
     1161 
     1162            Not all image types are necessarily implemented for all 
     1163            graphics types.  See :meth:`save` for more details. 
     1164 
     1165        EXAMPLES:: 
     1166 
     1167            sage: f = tmp_filename() + '.png' 
     1168            sage: G = sphere() 
     1169            sage: G.save_image(f) 
     1170        """ 
     1171        self.save(filename, *args, **kwds) 
     1172 
    11511173    def save(self, filename, **kwds): 
    11521174        """ 
    11531175        Save the graphic to an image file (of type: PNG, BMP, GIF, PPM, or TIFF) 
  • sage/plot/plot3d/tachyon.py

    diff --git a/sage/plot/plot3d/tachyon.py b/sage/plot/plot3d/tachyon.py
    a b  
    252252        """ 
    253253        return self.str() 
    254254 
     255    def save_image(self, filename=None, *args, **kwds): 
     256        r""" 
     257        Save an image representation of self.  The image type is 
     258        determined by the extension of the filename.  For example, 
     259        this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``, 
     260        ``.svg``.  Currently this is implemented by calling the 
     261        :meth:`save` method of self, passing along all arguments and 
     262        keywords. 
     263 
     264        .. Note:: 
     265 
     266            Not all image types are necessarily implemented for all 
     267            graphics types.  See :meth:`save` for more details. 
     268 
     269        EXAMPLES:: 
     270 
     271            sage: q = Tachyon() 
     272            sage: q.light((1,1,11), 1,(1,1,1)) 
     273            sage: q.texture('s') 
     274            sage: q.sphere((0,-1,1),1,'s') 
     275            sage: tempname = tmp_filename() 
     276            sage: q.save_image(tempname) 
     277        """ 
     278        self.save(filename, *args, **kwds) 
     279 
    255280    def save(self, filename='sage.png', verbose=0, block=True, extra_opts=''): 
    256281        r""" 
    257282        INPUT: