├── .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 | 
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 |
--------------------------------------------------------------------------------