├── .gitignore ├── Christmas.ipynb ├── LICENSE ├── Matascii.ipynb ├── README.md ├── matascii └── __init__.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .ipynb_checkpoints 3 | -------------------------------------------------------------------------------- /Christmas.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from matplotlib import pylab" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "import matascii\n", 23 | "pylab.switch_backend(\"module://matascii\")" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 3, 29 | "metadata": { 30 | "collapsed": true 31 | }, 32 | "outputs": [], 33 | "source": [ 34 | "from matplotlib import pylab\n", 35 | "from random import random" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 4, 41 | "metadata": { 42 | "collapsed": false 43 | }, 44 | "outputs": [ 45 | { 46 | "data": { 47 | "text/html": [ 48 | "
***************************************************************************************************************************************
                                                                                                                                       
                                                                                                                                       
                                                                                                                                       
               0**********************************************************************************************************             
                *          *     :              *     :        :     *                    *                    *         *             
                *  :       *   :                *                :   *                    *                    *    :    *             
                *  :       *                    *                    *   :                *                    *         *             
                *          *:                   *         :        :***  :     :  :       *         :          *         *             
                *:                                                **  **                                                 *             
                *      :                                        **      **  :                          :                 *             
               2****                 :                         **:        **                                       :  ****             
                *             :                              ** :           *                                            *             
                *                                          **  Merry Christmas!                                          *             
                *  :                                      *  :     *: **       **                                        *             
                *          :                             *************************                                       *             
                *             :   :                             *         *                                              *             
                *                                  :          **  :        *                  :    :   :  :              *             
               4:***              : :    :                  **              **                                        ****             
                *                             :           **                  **                       :                 *             
                *                                        **          *          *     :                                  *             
                *        :                             ** :        ** **         **  :                                   *             
                *                    :               :*          **     *          **                   :                *             
                *                            :    : *    :      *    :   *           **                                  *             
                *                                  ************************************ :                           :    *             
               60     :                                     **              **                                      :    *             
                ****                     :           :    **                  *               :                       ****             
                *                                  :     **          :         **                              :         *             
                *      :                         :     **        :          :    **:                             :       *             
                *                                    **                            **                                    *             
                :                     :             *                               **                                   *             
                *                     :            **                                 **             :                   *             
               80  :                    :        **                                     **                      :        *             
                ***:     :                      *:*****************************************      :              :     ***:             
                *                              :       :     *              *                    :  :                    *             
                *                                            *              *     :                        :             *             
                *                                            *      *       *:                                           *             
                *          *                    *            *       *      *             *                    *         *             
                *          *                    *            *       *      *             *                    *         *             
                *          *                    *            *       *      *             *                    *         *             
               100********************************************************************************************************             
                          −40                  −20                  0                    20                   40                       
                                                                                                                                       
                                                                                                                                       
                                                                                                                                       
" 49 | ], 50 | "text/plain": [ 51 | "" 52 | ] 53 | }, 54 | "metadata": {}, 55 | "output_type": "display_data" 56 | } 57 | ], 58 | "source": [ 59 | "pylab.plot([0, -11.5, 11.5, 0], [90, 70, 70, 90], \"g\")\n", 60 | "pylab.plot([0, -17., 17., 0], [75, 45, 45, 75], \"g\")\n", 61 | "pylab.plot([0, -20, 20, 0], [55, 20, 20, 55], \"g\")\n", 62 | "pylab.plot([-7, -7, 7, 7, -7], [20, 0, 0, 20, 20], \"y\")\n", 63 | "pylab.text(-1, 90, '*')\n", 64 | "pylab.text(-5, 25, \"Merry Christmas!\")\n", 65 | "\n", 66 | "for i in range(0, 90):\n", 67 | " x = 100.*random()-50.\n", 68 | " y = 90*random()\n", 69 | " pylab.text(x, y, ':')\n", 70 | "\n", 71 | "pylab.ylim(100, 0)\n", 72 | "pylab.xlim(-50, 50)\n", 73 | "pylab.show()" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 6, 79 | "metadata": { 80 | "collapsed": true 81 | }, 82 | "outputs": [], 83 | "source": [ 84 | "from math import sqrt" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 9, 90 | "metadata": { 91 | "collapsed": false 92 | }, 93 | "outputs": [ 94 | { 95 | "data": { 96 | "text/plain": [ 97 | "20.207259421636902" 98 | ] 99 | }, 100 | "execution_count": 9, 101 | "metadata": {}, 102 | "output_type": "execute_result" 103 | } 104 | ], 105 | "source": [ 106 | "35./sqrt(3)" 107 | ] 108 | } 109 | ], 110 | "metadata": { 111 | "kernelspec": { 112 | "display_name": "Python 2", 113 | "language": "python", 114 | "name": "python2" 115 | }, 116 | "language_info": { 117 | "codemirror_mode": { 118 | "name": "ipython", 119 | "version": 2 120 | }, 121 | "file_extension": ".py", 122 | "mimetype": "text/x-python", 123 | "name": "python", 124 | "nbconvert_exporter": "python", 125 | "pygments_lexer": "ipython2", 126 | "version": "2.7.10" 127 | } 128 | }, 129 | "nbformat": 4, 130 | "nbformat_minor": 0 131 | } 132 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domitry/matascii/5a6d503ca93f926e46d789348281e1284131256f/LICENSE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # matascii 2 | 3 | ![](https://i.gyazo.com/8f02fc6f267c2f0d2e9a15e05f071d5b.png) 4 | 5 | ASCII back-end for matplotlib 6 | 7 | ## Demo 8 | * [Demo1](https://github.com/domitry/matascii/blob/master/Matascii.ipynb) 9 | * [Christmas](https://github.com/domitry/matascii/blob/master/Christmas.ipynb) 10 | 11 | ## Installation 12 | ``` 13 | git clone https://github.com/domitry/matascii.git 14 | cd matascii 15 | python setup.py 16 | ``` 17 | 18 | ## How to use 19 | ```python 20 | from matplotlib import pylab 21 | import matascii 22 | pylab.switch_backend("module://matascii") 23 | 24 | # some lines here 25 | 26 | pylab.show() 27 | # or pylab.savefig("hoge.txt") 28 | ``` 29 | -------------------------------------------------------------------------------- /matascii/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a fully functional do nothing backend to provide a template to 3 | backend writers. It is fully functional in that you can select it as 4 | a backend with 5 | 6 | import matplotlib 7 | matplotlib.use('Template') 8 | 9 | and your matplotlib scripts will (should!) run without error, though 10 | no output is produced. This provides a nice starting point for 11 | backend writers because you can selectively implement methods 12 | (draw_rectangle, draw_lines, etc...) and slowly see your figure come 13 | to life w/o having to have a full blown implementation before getting 14 | any results. 15 | 16 | Copy this to backend_xxx.py and replace all instances of 'template' 17 | with 'xxx'. Then implement the class methods and functions below, and 18 | add 'xxx' to the switchyard in matplotlib/backends/__init__.py and 19 | 'xxx' to the backends list in the validate_backend methon in 20 | matplotlib/__init__.py and you're off. You can use your backend with:: 21 | 22 | import matplotlib 23 | matplotlib.use('xxx') 24 | from pylab import * 25 | plot([1,2,3]) 26 | show() 27 | 28 | matplotlib also supports external backends, so you can place you can 29 | use any module in your PYTHONPATH with the syntax:: 30 | 31 | import matplotlib 32 | matplotlib.use('module://my_backend') 33 | 34 | where my_backend.py is your module name. This syntax is also 35 | recognized in the rc file and in the -d argument in pylab, e.g.,:: 36 | 37 | python simple_plot.py -dmodule://my_backend 38 | 39 | If your backend implements support for saving figures (i.e. has a print_xyz() 40 | method) you can register it as the default handler for a given file type 41 | 42 | from matplotlib.backend_bases import register_backend 43 | register_backend('xyz', 'my_backend', 'XYZ File Format') 44 | ... 45 | plt.savefig("figure.xyz") 46 | 47 | The files that are most relevant to backend_writers are 48 | 49 | matplotlib/backends/backend_your_backend.py 50 | matplotlib/backend_bases.py 51 | matplotlib/backends/__init__.py 52 | matplotlib/__init__.py 53 | matplotlib/_pylab_helpers.py 54 | 55 | Naming Conventions 56 | 57 | * classes Upper or MixedUpperCase 58 | 59 | * varables lower or lowerUpper 60 | 61 | * functions lower or underscore_separated 62 | 63 | """ 64 | 65 | from __future__ import (absolute_import, division, print_function, 66 | unicode_literals) 67 | 68 | 69 | import matplotlib 70 | from matplotlib._pylab_helpers import Gcf 71 | from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ 72 | FigureManagerBase, FigureCanvasBase 73 | from matplotlib.transforms import CompositeAffine2D 74 | from matplotlib.figure import Figure 75 | from matplotlib.transforms import Bbox 76 | import numpy as np 77 | 78 | class RendererTemplate(RendererBase): 79 | """ 80 | The renderer handles drawing/rendering operations. 81 | 82 | This is a minimal do-nothing class that can be used to get started when 83 | writing a new backend. Refer to backend_bases.RendererBase for 84 | documentation of the classes methods. 85 | """ 86 | def __init__(self, width, height, dpi): 87 | self.width = width 88 | self.height = height 89 | self.dw = dpi*width 90 | self.dh = dpi*height 91 | arr = [" "]*self.dw 92 | self.arr = [arr[:] for i in xrange(0, self.dh)] 93 | self.colors = {} 94 | self.dpi = dpi 95 | 96 | def __draw_point(self, p, color="rgb(0,0,0)"): 97 | from math import floor 98 | x = int(floor(p[0])) 99 | y = int(floor(p[1])) 100 | if x<0 or x>=self.dw or y<0 or y>=self.dh: 101 | return 102 | self.arr[y][x] = '*' 103 | self.colors[(x, y)] = color 104 | 105 | def __draw_line(self, p1, p2, color="rgb(0,0,0)"): 106 | v = p2 - p1 107 | l = np.linalg.norm(v) 108 | dr = v/l 109 | r = np.zeros(v.shape[0]) 110 | while np.linalg.norm(r) < l: 111 | self.__draw_point(p1+r, color) 112 | r = r+dr 113 | 114 | def __draw_bezier3(self, p1, p2, p3, N=100, color="rgb(0,0,0)"): 115 | dt = 1./N 116 | t = 0. 117 | p = p1 118 | while t <= 1.: 119 | t += dt 120 | p4 = p1 + (p2 - p1)*t 121 | p5 = p2 + (p3 - p2)*t 122 | n = p4 + (p5 - p4)*t 123 | self.__draw_line(p, n, color) 124 | p = n 125 | 126 | def __draw_bezier4(self, p1, p2, p3, p4, N=100, color="rgb(0,0,0)"): 127 | dt = 1./N 128 | t = 0. 129 | p = p1 130 | while t <= 1.: 131 | t += dt 132 | p5 = p1 + (p2 - p1)*t 133 | p6 = p2 + (p3 - p2)*t 134 | p7 = p3 + (p4 - p3)*t 135 | p8 = p5 + (p6 - p5)*t 136 | p9 = p6 + (p7 - p6)*t 137 | n = p8 + (p9 - p8)*t 138 | self.__draw_line(p, n, color) 139 | p = n 140 | 141 | def draw_path(self, gc, path, transform, rgbFace=None): 142 | def to_code(color): 143 | return "rgb(" + ",".join([str(int(255*color[i])) for i in range(0, 3)]) + ")" 144 | 145 | mat = transform.get_matrix() 146 | c = to_code(gc.get_rgb()) 147 | 148 | def apply_affine(p): 149 | p = np.array(list(p) + [1]) 150 | return mat.dot(p) 151 | 152 | cp = np.zeros(4, dtype=np.float32) 153 | arr = [] 154 | 155 | if path.codes is None: 156 | l = len(path.vertices) 157 | for i2 in range(1, l): 158 | p1 = apply_affine(path.vertices[i2-1]) 159 | p2 = apply_affine(path.vertices[i2]) 160 | self.__draw_line(p1, p2, color=c) 161 | else: 162 | for i, code in enumerate(path.codes): 163 | p = apply_affine(path.vertices[i]) 164 | 165 | if code == 1: 166 | # moveto 167 | cp = p 168 | elif code == 2: 169 | # lineto 170 | self.__draw_line(cp, p, color=c) 171 | cp = p 172 | elif code == 3: 173 | # curve3 174 | arr.append(p) 175 | if len(arr) == 2: 176 | self.__draw_bezier3(cp, arr[0], arr[1], color=c) 177 | arr = [] 178 | cp = p 179 | 180 | elif code == 4: 181 | # curve4 182 | arr.append(p) 183 | if len(arr)==3: 184 | self.__draw_bezier4(cp, arr[0], arr[1], arr[2], color=c) 185 | arr = [] 186 | cp = p 187 | 188 | elif code == 79: 189 | arr = [] 190 | else: 191 | raise Exception("parse error") 192 | pass 193 | 194 | # draw_markers is optional, and we get more correct relative 195 | # timings by leaving it out. backend implementers concerned with 196 | # performance will probably want to implement it 197 | # def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): 198 | # pass 199 | 200 | # draw_path_collection is optional, and we get more correct 201 | # relative timings by leaving it out. backend implementers concerned with 202 | # performance will probably want to implement it 203 | # def draw_path_collection(self, gc, master_transform, paths, 204 | # all_transforms, offsets, offsetTrans, facecolors, 205 | # edgecolors, linewidths, linestyles, 206 | # antialiaseds): 207 | # pass 208 | 209 | # draw_quad_mesh is optional, and we get more correct 210 | # relative timings by leaving it out. backend implementers concerned with 211 | # performance will probably want to implement it 212 | # def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, 213 | # coordinates, offsets, offsetTrans, facecolors, 214 | # antialiased, edgecolors): 215 | # pass 216 | 217 | def draw_image(self, gc, x, y, im): 218 | pass 219 | 220 | def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): 221 | from math import floor 222 | x_ = int(floor(x)) 223 | y_ = int(floor(y)) 224 | for i, c in enumerate(s): 225 | nx = x_ + i 226 | if x<0 or x>=self.dw or y<0 or y>=self.dh: 227 | return 228 | self.arr[y_][nx] = c 229 | 230 | def flipy(self): 231 | return True 232 | 233 | def get_canvas_width_height(self): 234 | return self.dw, self.dh 235 | 236 | def get_text_width_height_descent(self, s, prop, ismath): 237 | return 1, 1, 1 238 | 239 | def new_gc(self): 240 | return GraphicsContextBase() 241 | 242 | def points_to_pixels(self, points): 243 | # if backend doesn't have dpi, e.g., postscript or svg 244 | return points 245 | # elif backend assumes a value for pixels_per_inch 246 | #return points/72.0 * self.dpi.get() * pixels_per_inch/72.0 247 | # else 248 | #return points/72.0 * self.dpi.get() 249 | 250 | 251 | class GraphicsContextTemplate(GraphicsContextBase): 252 | """ 253 | The graphics context provides the color, line styles, etc... See the gtk 254 | and postscript backends for examples of mapping the graphics context 255 | attributes (cap styles, join styles, line widths, colors) to a particular 256 | backend. In GTK this is done by wrapping a gtk.gdk.GC object and 257 | forwarding the appropriate calls to it using a dictionary mapping styles 258 | to gdk constants. In Postscript, all the work is done by the renderer, 259 | mapping line styles to postscript calls. 260 | 261 | If it's more appropriate to do the mapping at the renderer level (as in 262 | the postscript backend), you don't need to override any of the GC methods. 263 | If it's more appropriate to wrap an instance (as in the GTK backend) and 264 | do the mapping here, you'll need to override several of the setter 265 | methods. 266 | 267 | The base GraphicsContext stores colors as a RGB tuple on the unit 268 | interval, e.g., (0.5, 0.0, 1.0). You may need to map this to colors 269 | appropriate for your backend. 270 | """ 271 | pass 272 | 273 | 274 | 275 | ######################################################################## 276 | # 277 | # The following functions and classes are for pylab and implement 278 | # window/figure managers, etc... 279 | # 280 | ######################################################################## 281 | 282 | def draw_if_interactive(): 283 | """ 284 | For image backends - is not required 285 | For GUI backends - this should be overriden if drawing should be done in 286 | interactive python mode 287 | """ 288 | pass 289 | 290 | def show(): 291 | from IPython.core.display import display, HTML 292 | """ 293 | For image backends - is not required 294 | For GUI backends - show() is usually the last line of a pylab script and 295 | tells the backend that it is time to draw. In interactive mode, this may 296 | be a do nothing func. See the GTK backend for an example of how to handle 297 | interactive versus batch mode 298 | """ 299 | try: 300 | for manager in Gcf.get_all_fig_managers(): 301 | canvas = manager.canvas 302 | canvas.draw() 303 | colors = canvas.colors 304 | 305 | string = canvas.to_str("
", color=True) 306 | display(HTML("
" + string + "
")) 307 | finally: 308 | #if close and Gcf.get_all_fig_managers(): 309 | # matplotlib.pyplot.close('all') 310 | pass 311 | 312 | def new_figure_manager(num, *args, **kwargs): 313 | """ 314 | Create a new figure manager instance 315 | """ 316 | # if a main-level app must be created, this (and 317 | # new_figure_manager_given_figure) is the usual place to 318 | # do it -- see backend_wx, backend_wxagg and backend_tkagg for 319 | # examples. Not all GUIs require explicit instantiation of a 320 | # main-level app (egg backend_gtk, backend_gtkagg) for pylab 321 | FigureClass = kwargs.pop('FigureClass', Figure) 322 | thisFig = FigureClass(*args, **kwargs) 323 | return new_figure_manager_given_figure(num, thisFig) 324 | 325 | def new_figure_manager_given_figure(num, figure): 326 | """ 327 | Create a new figure manager instance for the given figure. 328 | """ 329 | canvas = FigureCanvasTemplate(figure) 330 | manager = FigureManagerTemplate(canvas, num) 331 | return manager 332 | 333 | class FigureCanvasTemplate(FigureCanvasBase): 334 | """ 335 | The canvas the figure renders into. Calls the draw and print fig 336 | methods, creates the renderers, etc... 337 | 338 | Public attribute 339 | 340 | figure - A Figure instance 341 | 342 | Note GUI templates will want to connect events for button presses, 343 | mouse movements and key presses to functions that call the base 344 | class methods button_press_event, button_release_event, 345 | motion_notify_event, key_press_event, and key_release_event. See, 346 | e.g., backend_gtk.py, backend_wx.py and backend_tkagg.py 347 | """ 348 | 349 | def draw(self): 350 | """ 351 | Draw the figure using the renderer 352 | """ 353 | from math import ceil 354 | width, height = 9., 3. 355 | self.figure.set_size_inches(width, height) 356 | self.dpi = 15 357 | self.figure.set_dpi(self.dpi) 358 | renderer = RendererTemplate(int(ceil(width)), int(ceil(height)), self.dpi) 359 | self.figure.draw(renderer) 360 | self.arr = renderer.arr 361 | self.colors = renderer.colors 362 | 363 | def to_str(self, sep="\n", color=None): 364 | arrs = [[' ' if c==" " else c for c in arr] for arr in self.arr] 365 | 366 | if color is not None: 367 | for xy, c in self.colors.items(): 368 | x, y = xy 369 | arrs[y][x] = "" + arrs[y][x] + "" 370 | 371 | return sep.join(["".join(arr) for arr in arrs]) 372 | 373 | # You should provide a print_xxx function for every file format 374 | # you can write. 375 | 376 | # If the file type is not in the base set of filetypes, 377 | # you should add it to the class-scope filetypes dictionary as follows: 378 | # filetypes = FigureCanvasBase.filetypes.copy() 379 | filetypes = {} 380 | filetypes['txt'] = 'Text format' 381 | 382 | def print_txt(self, filename, *args, **kwargs): 383 | """ 384 | Write out format foo. The dpi, facecolor and edgecolor are restored 385 | to their original values after this call, so you don't need to 386 | save and restore them. 387 | """ 388 | self.draw() 389 | f = open(filename, 'w') 390 | f.write(self.to_str()) 391 | f.close() 392 | 393 | def get_default_filetype(self): 394 | return 'txt' 395 | 396 | class FigureManagerTemplate(FigureManagerBase): 397 | """ 398 | Wrap everything up into a window for the pylab interface 399 | 400 | For non interactive backends, the base class does all the work 401 | """ 402 | pass 403 | 404 | ######################################################################## 405 | # 406 | # Now just provide the standard names that backend.__init__ is expecting 407 | # 408 | ######################################################################## 409 | 410 | FigureCanvas = FigureCanvasTemplate 411 | FigureManager = FigureManagerTemplate 412 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | setup (name = 'Matascii', 4 | version = '1.0', 5 | description = 'ASCII back-end for matplotlib', 6 | author="Naoki Nishida", 7 | author_email="domitry@gmail.com", 8 | packages = ["matascii"], 9 | package_data = {"matascii":["*.ipynb"]} 10 | ) 11 | --------------------------------------------------------------------------------