├── .gitignore ├── minimap.png ├── autoload ├── drawille │ ├── docs │ │ └── images │ │ │ ├── usage.png │ │ │ ├── xkcd.png │ │ │ ├── turtle.png │ │ │ ├── drawille_01.png │ │ │ ├── rotating_cube.gif │ │ │ └── sine_tracking.gif │ ├── examples │ │ ├── turtle.py │ │ ├── speed_test.py │ │ ├── sine_tracking.py │ │ ├── basic.py │ │ ├── xkcd.py │ │ ├── rotating_cube.py │ │ ├── image2term.py │ │ └── flappy_birds.py │ ├── setup.py │ ├── tests.py │ ├── README.md │ └── drawille.py ├── minimap.vim └── minimap.py ├── plugin └── minimap.vim ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /minimap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/severin-lemaignan/vim-minimap/HEAD/minimap.png -------------------------------------------------------------------------------- /autoload/drawille/docs/images/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/severin-lemaignan/vim-minimap/HEAD/autoload/drawille/docs/images/usage.png -------------------------------------------------------------------------------- /autoload/drawille/docs/images/xkcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/severin-lemaignan/vim-minimap/HEAD/autoload/drawille/docs/images/xkcd.png -------------------------------------------------------------------------------- /autoload/drawille/docs/images/turtle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/severin-lemaignan/vim-minimap/HEAD/autoload/drawille/docs/images/turtle.png -------------------------------------------------------------------------------- /autoload/drawille/docs/images/drawille_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/severin-lemaignan/vim-minimap/HEAD/autoload/drawille/docs/images/drawille_01.png -------------------------------------------------------------------------------- /autoload/drawille/docs/images/rotating_cube.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/severin-lemaignan/vim-minimap/HEAD/autoload/drawille/docs/images/rotating_cube.gif -------------------------------------------------------------------------------- /autoload/drawille/docs/images/sine_tracking.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/severin-lemaignan/vim-minimap/HEAD/autoload/drawille/docs/images/sine_tracking.gif -------------------------------------------------------------------------------- /autoload/drawille/examples/turtle.py: -------------------------------------------------------------------------------- 1 | from drawille import Turtle 2 | 3 | t = Turtle() 4 | 5 | for _ in range(36): 6 | t.right(10) 7 | for _ in range(36): 8 | t.right(10) 9 | t.forward(8) 10 | 11 | print(t.frame()) 12 | -------------------------------------------------------------------------------- /autoload/drawille/examples/speed_test.py: -------------------------------------------------------------------------------- 1 | from drawille import Canvas 2 | from timeit import timeit 3 | 4 | c = Canvas() 5 | 6 | frames = 1000 * 10 7 | 8 | sizes = ((0, 0), 9 | (10, 10), 10 | (20, 20), 11 | (20, 40), 12 | (40, 20), 13 | (40, 40), 14 | (100, 100)) 15 | 16 | for x, y in sizes: 17 | c.set(0, 0) 18 | 19 | for i in range(y): 20 | c.set(x, i) 21 | 22 | r = timeit(c.frame, number=frames) 23 | print('{0}x{1}\t{2}'.format(x, y, r)) 24 | c.clear() 25 | -------------------------------------------------------------------------------- /autoload/drawille/examples/sine_tracking.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from drawille import Canvas, line, animate 3 | import math 4 | 5 | def __main__(): 6 | i = 0 7 | height = 40 8 | 9 | while True: 10 | frame = [] 11 | 12 | frame.extend([coords for coords in 13 | line(0, 14 | height, 15 | 180, 16 | math.sin(math.radians(i)) * height + height)]) 17 | 18 | frame.extend([(x/2, height + math.sin(math.radians(x+i)) * height) 19 | for x in range(0, 360, 2)]) 20 | 21 | yield frame 22 | 23 | i += 2 24 | 25 | 26 | 27 | if __name__ == '__main__': 28 | animate(Canvas(), __main__, 1./60) 29 | -------------------------------------------------------------------------------- /autoload/drawille/examples/basic.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from drawille import Canvas 3 | import math 4 | 5 | 6 | s = Canvas() 7 | 8 | for x in range(1800): 9 | s.set(x/10, math.sin(math.radians(x)) * 10) 10 | 11 | print(s.frame()) 12 | 13 | s.clear() 14 | 15 | for x in range(0, 1800, 10): 16 | s.set(x/10, 10 + math.sin(math.radians(x)) * 10) 17 | s.set(x/10, 10 + math.cos(math.radians(x)) * 10) 18 | 19 | print(s.frame()) 20 | 21 | s.clear() 22 | 23 | for x in range(0, 3600, 20): 24 | s.set(x/20, 4 + math.sin(math.radians(x)) * 4) 25 | 26 | print(s.frame()) 27 | 28 | s.clear() 29 | 30 | for x in range(0, 360, 4): 31 | s.set(x/4, 30 + math.sin(math.radians(x)) * 30) 32 | 33 | for x in range(30): 34 | for y in range(30): 35 | s.set(x,y) 36 | s.toggle(x+30, y+30) 37 | s.toggle(x+60, y) 38 | 39 | print(s.frame()) 40 | -------------------------------------------------------------------------------- /plugin/minimap.vim: -------------------------------------------------------------------------------- 1 | if exists('loaded_minimap') 2 | finish 3 | endif 4 | 5 | let loaded_minimap = 1 6 | 7 | command! MinimapToggle call minimap#ToggleMinimap() 8 | command! Minimap call minimap#ShowMinimap() 9 | command! MinimapClose call minimap#CloseMinimap() 10 | command! MinimapUpdate call minimap#UpdateMinimap() 11 | 12 | let g:minimap_show = 13 | \ get( g:, 'minimap_show', 'mm' ) 14 | let g:minimap_update = 15 | \ get( g:, 'minimap_update', 'mu' ) 16 | let g:minimap_close = 17 | \ get( g:, 'minimap_close', 'mc' ) 18 | let g:minimap_toggle = 19 | \ get( g:, 'minimap_toggle', 'mt' ) 20 | 21 | execute "nnoremap " . " " . 22 | \ g:minimap_show . " :Minimap" 23 | execute "nnoremap " . " " . 24 | \ g:minimap_update . " :MinimapUpdate" 25 | execute "nnoremap " . " " . 26 | \ g:minimap_close . " :MinimapClose" 27 | execute "nnoremap " . " " . 28 | \ g:minimap_toggle . " :MinimapToggle" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016 Séverin Lemaignan and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /autoload/drawille/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name = 'drawille', 5 | version = '0.0.4', 6 | author = 'Adam Tauber', 7 | author_email = 'asciimoo@gmail.com', 8 | description = ('Drawing in terminal with unicode braille characters'), 9 | license = 'AGPLv3+', 10 | keywords = "terminal braille drawing canvas console", 11 | url = 'https://github.com/asciimoo/drawille', 12 | scripts = [], 13 | py_modules = ['drawille'], 14 | packages = find_packages(), 15 | install_requires = [], 16 | download_url = 'https://github.com/asciimoo/drawille/tarball/master', 17 | # TODO 18 | #entry_points={ 19 | # "console_scripts": ["drawille=drawille:__main__"] 20 | #}, 21 | classifiers = [ 22 | "Development Status :: 4 - Beta", 23 | "Topic :: Utilities", 24 | 'Environment :: Console', 25 | 'License :: OSI Approved :: GNU Affero General Public License v3', 26 | 'Intended Audience :: Developers', 27 | 'Natural Language :: English', 28 | 'Programming Language :: Python', 29 | 'Programming Language :: Python :: 2.6', 30 | 'Programming Language :: Python :: 2.7', 31 | 'Programming Language :: Python :: 3', 32 | 'Programming Language :: Python :: 3.3', 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /autoload/minimap.vim: -------------------------------------------------------------------------------- 1 | " vim-minimap is free software: you can redistribute it and/or modify 2 | " it under the terms of the GNU Affero General Public License as published by 3 | " the Free Software Foundation, either version 3 of the License, or 4 | " (at your option) any later version. 5 | " 6 | " vim-minimap is distributed in the hope that it will be useful, 7 | " but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | " GNU Affero General Public License for more details. 10 | " 11 | " You should have received a copy of the GNU Affero General Public License 12 | " along with vim-minimap. If not, see < http://www.gnu.org/licenses/ >. 13 | " 14 | " (C) 2014- by Séverin Lemaignan for the VIM integration, 15 | " (C) 2014- by Adam Tauber for the Drawille part, 16 | 17 | if has('python') || has('python3') 18 | " By default Highlight the current screen as a visual selection. 19 | if !exists('g:minimap_highlight') 20 | let g:minimap_highlight = 'Visual' 21 | endif 22 | 23 | let python_module = fnameescape(globpath(&runtimepath, 'autoload/minimap.py')) 24 | if has('python') 25 | exe 'pyfile ' . python_module 26 | elseif has('python3') 27 | exe 'py3file ' . python_module 28 | endif 29 | end 30 | 31 | function! minimap#ShowMinimap() 32 | if has('python') 33 | python showminimap() 34 | elseif has('python3') 35 | python3 showminimap() 36 | endif 37 | endfunction 38 | 39 | function! minimap#UpdateMinimap() 40 | if has('python') 41 | python updateminimap() 42 | elseif has('python3') 43 | python3 updateminimap() 44 | endif 45 | endfunction 46 | 47 | function! minimap#CloseMinimap() 48 | if has('python') 49 | python closeminimap() 50 | elseif has('python3') 51 | python3 closeminimap() 52 | endif 53 | endfunction 54 | 55 | function! minimap#ToggleMinimap() 56 | if has('python') 57 | python toggleminimap() 58 | elseif has('python3') 59 | python3 toggleminimap() 60 | endif 61 | endfunction 62 | 63 | -------------------------------------------------------------------------------- /autoload/drawille/examples/xkcd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from sys import argv 4 | try: 5 | from PIL import Image 6 | except: 7 | from sys import stderr 8 | stderr.write('[E] PIL not installed') 9 | exit(1) 10 | from StringIO import StringIO 11 | import urllib2 12 | import re 13 | from drawille import Canvas 14 | 15 | 16 | def getTerminalSize(): 17 | import os 18 | env = os.environ 19 | 20 | def ioctl_GWINSZ(fd): 21 | import fcntl 22 | import termios 23 | import struct 24 | cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) 25 | return cr 26 | cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 27 | if not cr: 28 | try: 29 | fd = os.open(os.ctermid(), os.O_RDONLY) 30 | cr = ioctl_GWINSZ(fd) 31 | os.close(fd) 32 | except: 33 | pass 34 | if not cr: 35 | cr = (env.get('LINES', 25), env.get('COLUMNS', 80)) 36 | return int(cr[1]), int(cr[0]) 37 | 38 | 39 | def usage(): 40 | print('Usage: %s ') 41 | exit() 42 | 43 | if __name__ == '__main__': 44 | if len(argv) < 2: 45 | url = 'http://xkcd.com/' 46 | elif argv[1] in ['-h', '--help']: 47 | usage() 48 | elif argv[1].startswith('http'): 49 | url = argv[1] 50 | else: 51 | url = 'http://xkcd.com/%s/' % argv[1] 52 | c = urllib2.urlopen(url).read() 53 | img_url = re.findall('http:\/\/imgs.xkcd.com\/comics\/[^"\']+', c)[0] 54 | i = Image.open(StringIO(urllib2.urlopen(img_url).read())).convert('L') 55 | w, h = i.size 56 | tw, th = getTerminalSize() 57 | tw *= 2 58 | th *= 2 59 | if tw < w: 60 | ratio = tw / float(w) 61 | w = tw 62 | h = int(h * ratio) 63 | i = i.resize((w, h), Image.ANTIALIAS) 64 | can = Canvas() 65 | x = y = 0 66 | 67 | try: 68 | i_converted = i.tobytes() 69 | except AttributeError: 70 | i_converted = i.tostring() 71 | 72 | for pix in i_converted: 73 | if ord(pix) < 128: 74 | can.set(x, y) 75 | x += 1 76 | if x >= w: 77 | y += 1 78 | x = 0 79 | print(can.frame()) 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A code minimap for Vim 2 | ====================== 3 | 4 | The Sublime text-editor can display an useful overview of the code as a 5 | *minimap* sidebar. 6 | 7 | We can implement the same thing in Vim, relying on the [Drawille 8 | library](https://github.com/asciimoo/drawille) to 'draw' in text mode. 9 | 10 | ![minimap in action](http://picdrop.t3lab.com/qqpdtsbTow.gif) 11 | 12 | This code is made available under a MIT license. See [LICENSE](LICENSE) for 13 | details. 14 | 15 | Features 16 | -------- 17 | 18 | - displays the minimap of the currently active buffer (and updates when 19 | switching to a different buffer) 20 | - synchronized scrolling 21 | - live update while typing 22 | 23 | Installation 24 | ------------ 25 | 26 | Note that this extension requires Vim with Python support. 27 | 28 | ### Vundle 29 | 30 | With [vundle](https://github.com/gmarik/Vundle.vim), simply add: `Plugin 31 | 'severin-lemaignan/vim-minimap'` to your `.vimrc` and run `:PluginInstall` from 32 | vim. 33 | 34 | ### Janus 35 | 36 | With Janus just clone inside ```.janus```. 37 | 38 | ``` 39 | cd ~/.janus 40 | git clone https://github.com/severin-lemaignan/vim-minimap.git vim-minimap 41 | ``` 42 | 43 | ### AUR 44 | 45 | AUR just has [vim-minimap-git](https://aur.archlinux.org/packages/vim-minimap-git/) package. 46 | 47 | Usage 48 | ----- 49 | 50 | `:Minimap` to show the minimap, `:MinimapClose` to hide it. 51 | 52 | Default mappings: `mm` to display the minimap, `mc` to close it. 53 | 54 | To overwrite the default keybindings, using following settings in ``.vimrc'': 55 | 56 | ``` 57 | let g:minimap_show='ms' 58 | let g:minimap_update='mu' 59 | let g:minimap_close='gc' 60 | let g:minimap_toggle='gt' 61 | ``` 62 | 63 | Settings 64 | -------- 65 | 66 | You can customize the color of the highlighting by setting `g:minimap_highlight` in your vimrc: 67 | 68 | `let g:minimap_highlight='Visual'` 69 | 70 | Note: To find out which highlights are available on your vim installation use :hi to get the list. 71 | 72 | Troubleshooting 73 | --------------- 74 | 75 | 76 | ### Weird display 77 | 78 | > **Problem**: 79 | > 80 | > Certain fonts do not display plain dots and empty spaces, but 81 | > plain dots and circles for braille characters. 82 | > 83 | > For example, with `Inconsolata`: 84 | > 85 | > ![image](https://cloud.githubusercontent.com/assets/7250745/8083430/c48e5c44-0f84-11e5-9cba-20d7e2eac0c5.png) 86 | > 87 | > **Solution**: 88 | > 89 | > As a result, you may want to use any other font that display 90 | > braille characters in a way that suit the minimap plugin, 91 | > like `Ubuntu Mono`, or `Droid Sans Mono`. 92 | > 93 | > With `Ubuntu Mono`: 94 | > 95 | > ![image](https://cloud.githubusercontent.com/assets/7250745/8083436/d4aaf9d4-0f84-11e5-9383-cb02bba384bc.png) 96 | > 97 | -------------------------------------------------------------------------------- /autoload/drawille/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from drawille import Canvas, line, Turtle 3 | from unittest import TestCase, main 4 | 5 | 6 | class CanvasTestCase(TestCase): 7 | 8 | 9 | def test_set(self): 10 | c = Canvas() 11 | c.set(0, 0) 12 | self.assertTrue(0 in c.chars and 0 in c.chars[0]) 13 | 14 | 15 | def test_unset_empty(self): 16 | c = Canvas() 17 | c.set(1, 1) 18 | c.unset(1, 1) 19 | self.assertEqual(len(c.chars), 0) 20 | 21 | 22 | def test_unset_nonempty(self): 23 | c = Canvas() 24 | c.set(0, 0) 25 | c.set(0, 1) 26 | c.unset(0, 1) 27 | self.assertEqual(c.chars[0][0], 1) 28 | 29 | 30 | def test_clear(self): 31 | c = Canvas() 32 | c.set(1, 1) 33 | c.clear() 34 | self.assertEqual(c.chars, dict()) 35 | 36 | 37 | def test_toggle(self): 38 | c = Canvas() 39 | c.toggle(0, 0) 40 | self.assertEqual(c.chars, {0: {0: 1}}) 41 | c.toggle(0, 0) 42 | self.assertEqual(c.chars, dict()) 43 | 44 | 45 | def test_set_text(self): 46 | c = Canvas() 47 | c.set_text(0, 0, "asdf") 48 | self.assertEqual(c.frame(), "asdf") 49 | 50 | 51 | def test_frame(self): 52 | c = Canvas() 53 | self.assertEqual(c.frame(), '') 54 | c.set(0, 0) 55 | self.assertEqual(c.frame(), '⠁') 56 | 57 | 58 | def test_max_min_limits(self): 59 | c = Canvas() 60 | c.set(0, 0) 61 | self.assertEqual(c.frame(min_x=2), '') 62 | self.assertEqual(c.frame(max_x=0), '') 63 | 64 | 65 | def test_get(self): 66 | c = Canvas() 67 | self.assertEqual(c.get(0, 0), False) 68 | c.set(0, 0) 69 | self.assertEqual(c.get(0, 0), True) 70 | self.assertEqual(c.get(0, 1), False) 71 | self.assertEqual(c.get(1, 0), False) 72 | self.assertEqual(c.get(1, 1), False) 73 | 74 | 75 | class LineTestCase(TestCase): 76 | 77 | 78 | def test_single_pixel(self): 79 | self.assertEqual(list(line(0, 0, 0, 0)), [(0, 0)]) 80 | 81 | 82 | def test_row(self): 83 | self.assertEqual(list(line(0, 0, 1, 0)), [(0, 0), (1, 0)]) 84 | 85 | 86 | def test_column(self): 87 | self.assertEqual(list(line(0, 0, 0, 1)), [(0, 0), (0, 1)]) 88 | 89 | 90 | def test_diagonal(self): 91 | self.assertEqual(list(line(0, 0, 1, 1)), [(0, 0), (1, 1)]) 92 | 93 | 94 | class TurtleTestCase(TestCase): 95 | 96 | 97 | def test_position(self): 98 | t = Turtle() 99 | self.assertEqual(t.pos_x, 0) 100 | self.assertEqual(t.pos_y, 0) 101 | t.move(1, 1) 102 | self.assertEqual(t.pos_x, 1) 103 | self.assertEqual(t.pos_y, 1) 104 | 105 | 106 | def test_rotation(self): 107 | t = Turtle() 108 | self.assertEqual(t.rotation, 0) 109 | t.right(30) 110 | self.assertEqual(t.rotation, 30) 111 | t.left(30) 112 | self.assertEqual(t.rotation, 0) 113 | 114 | 115 | def test_brush(self): 116 | t = Turtle() 117 | self.assertFalse(t.get(t.pos_x, t.pos_y)) 118 | t.forward(1) 119 | self.assertTrue(t.get(0, 0)) 120 | self.assertTrue(t.get(t.pos_x, t.pos_y)) 121 | t.up() 122 | t.move(2, 0) 123 | self.assertFalse(t.get(t.pos_x, t.pos_y)) 124 | t.down() 125 | t.move(3, 0) 126 | self.assertTrue(t.get(t.pos_x, t.pos_y)) 127 | 128 | 129 | if __name__ == '__main__': 130 | main() 131 | -------------------------------------------------------------------------------- /autoload/drawille/examples/rotating_cube.py: -------------------------------------------------------------------------------- 1 | from drawille import Canvas, line 2 | import curses 3 | import math 4 | from time import sleep 5 | import locale 6 | 7 | locale.setlocale(locale.LC_ALL,"") 8 | 9 | stdscr = curses.initscr() 10 | stdscr.refresh() 11 | 12 | class Point3D: 13 | def __init__(self, x = 0, y = 0, z = 0): 14 | self.x, self.y, self.z = float(x), float(y), float(z) 15 | 16 | def rotateX(self, angle): 17 | """ Rotates the point around the X axis by the given angle in degrees. """ 18 | rad = angle * math.pi / 180 19 | cosa = math.cos(rad) 20 | sina = math.sin(rad) 21 | y = self.y * cosa - self.z * sina 22 | z = self.y * sina + self.z * cosa 23 | return Point3D(self.x, y, z) 24 | 25 | def rotateY(self, angle): 26 | """ Rotates the point around the Y axis by the given angle in degrees. """ 27 | rad = angle * math.pi / 180 28 | cosa = math.cos(rad) 29 | sina = math.sin(rad) 30 | z = self.z * cosa - self.x * sina 31 | x = self.z * sina + self.x * cosa 32 | return Point3D(x, self.y, z) 33 | 34 | def rotateZ(self, angle): 35 | """ Rotates the point around the Z axis by the given angle in degrees. """ 36 | rad = angle * math.pi / 180 37 | cosa = math.cos(rad) 38 | sina = math.sin(rad) 39 | x = self.x * cosa - self.y * sina 40 | y = self.x * sina + self.y * cosa 41 | return Point3D(x, y, self.z) 42 | 43 | def project(self, win_width, win_height, fov, viewer_distance): 44 | """ Transforms this 3D point to 2D using a perspective projection. """ 45 | factor = fov / (viewer_distance + self.z) 46 | x = self.x * factor + win_width / 2 47 | y = -self.y * factor + win_height / 2 48 | return Point3D(x, y, 1) 49 | 50 | 51 | vertices = [ 52 | Point3D(-20,20,-20), 53 | Point3D(20,20,-20), 54 | Point3D(20,-20,-20), 55 | Point3D(-20,-20,-20), 56 | Point3D(-20,20,20), 57 | Point3D(20,20,20), 58 | Point3D(20,-20,20), 59 | Point3D(-20,-20,20) 60 | ] 61 | 62 | # Define the vertices that compose each of the 6 faces. These numbers are 63 | # indices to the vertices list defined above. 64 | faces = [(0,1,2,3),(1,5,6,2),(5,4,7,6),(4,0,3,7),(0,4,5,1),(3,2,6,7)] 65 | 66 | 67 | def __main__(stdscr, projection=False): 68 | angleX, angleY, angleZ = 0, 0, 0 69 | c = Canvas() 70 | while 1: 71 | # Will hold transformed vertices. 72 | t = [] 73 | 74 | for v in vertices: 75 | # Rotate the point around X axis, then around Y axis, and finally around Z axis. 76 | p = v.rotateX(angleX).rotateY(angleY).rotateZ(angleZ) 77 | if projection: 78 | # Transform the point from 3D to 2D 79 | p = p.project(50, 50, 50, 50) 80 | #Put the point in the list of transformed vertices 81 | t.append(p) 82 | 83 | for f in faces: 84 | for x,y in line(t[f[0]].x, t[f[0]].y, t[f[1]].x, t[f[1]].y): 85 | c.set(x,y) 86 | for x,y in line(t[f[1]].x, t[f[1]].y, t[f[2]].x, t[f[2]].y): 87 | c.set(x,y) 88 | for x,y in line(t[f[2]].x, t[f[2]].y, t[f[3]].x, t[f[3]].y): 89 | c.set(x,y) 90 | for x,y in line(t[f[3]].x, t[f[3]].y, t[f[0]].x, t[f[0]].y): 91 | c.set(x,y) 92 | 93 | f = c.frame(-40, -40, 80, 80) 94 | stdscr.addstr(0, 0, '{0}\n'.format(f)) 95 | stdscr.refresh() 96 | 97 | angleX += 2 98 | angleY += 3 99 | angleZ += 5 100 | sleep(1.0/20) 101 | c.clear() 102 | 103 | if __name__ == '__main__': 104 | from sys import argv 105 | projection = False 106 | if '-p' in argv: 107 | projection = True 108 | curses.wrapper(__main__, projection) 109 | -------------------------------------------------------------------------------- /autoload/drawille/examples/image2term.py: -------------------------------------------------------------------------------- 1 | # example: 2 | # $ PYTHONPATH=`pwd` python examples/image2term.py http://fc00.deviantart.net/fs71/f/2011/310/5/a/giant_nyan_cat_by_daieny-d4fc8u1.png -t 100 -r 0.01 3 | 4 | try: 5 | from PIL import Image 6 | except: 7 | from sys import stderr 8 | stderr.write('[E] PIL not installed\n') 9 | exit(1) 10 | from drawille import Canvas 11 | from StringIO import StringIO 12 | import urllib2 13 | 14 | 15 | def getTerminalSize(): 16 | import os 17 | env = os.environ 18 | 19 | def ioctl_GWINSZ(fd): 20 | import fcntl 21 | import termios 22 | import struct 23 | cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) 24 | return cr 25 | cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 26 | if not cr: 27 | try: 28 | fd = os.open(os.ctermid(), os.O_RDONLY) 29 | cr = ioctl_GWINSZ(fd) 30 | os.close(fd) 31 | except: 32 | pass 33 | if not cr: 34 | cr = (env.get('LINES', 25), env.get('COLUMNS', 80)) 35 | return int(cr[1]), int(cr[0]) 36 | 37 | 38 | def image2term(image, threshold=128, ratio=None, invert=False): 39 | if image.startswith('http://') or image.startswith('https://'): 40 | i = Image.open(StringIO(urllib2.urlopen(image).read())).convert('L') 41 | else: 42 | i = Image.open(open(image)).convert('L') 43 | w, h = i.size 44 | if ratio: 45 | w = int(w * ratio) 46 | h = int(h * ratio) 47 | i = i.resize((w, h), Image.ANTIALIAS) 48 | else: 49 | tw = getTerminalSize()[0] 50 | tw *= 2 51 | if tw < w: 52 | ratio = tw / float(w) 53 | w = tw 54 | h = int(h * ratio) 55 | i = i.resize((w, h), Image.ANTIALIAS) 56 | can = Canvas() 57 | x = y = 0 58 | 59 | try: 60 | i_converted = i.tobytes() 61 | except AttributeError: 62 | i_converted = i.tostring() 63 | 64 | for pix in i_converted: 65 | if invert: 66 | if ord(pix) > threshold: 67 | can.set(x, y) 68 | else: 69 | if ord(pix) < threshold: 70 | can.set(x, y) 71 | x += 1 72 | if x >= w: 73 | y += 1 74 | x = 0 75 | return can.frame(0, 0) 76 | 77 | 78 | def argparser(): 79 | import argparse 80 | from sys import stdout 81 | argp = argparse.ArgumentParser(description='drawille - image to terminal example script') 82 | argp.add_argument('-o', '--output' 83 | ,help = 'Output file - default is STDOUT' 84 | ,metavar = 'FILE' 85 | ,default = stdout 86 | ,type = argparse.FileType('w') 87 | ) 88 | argp.add_argument('-r', '--ratio' 89 | ,help = 'Image resize ratio' 90 | ,default = None 91 | ,action = 'store' 92 | ,type = float 93 | ,metavar = 'N' 94 | ) 95 | argp.add_argument('-t', '--threshold' 96 | ,help = 'Color threshold' 97 | ,default = 128 98 | ,action = 'store' 99 | ,type = int 100 | ,metavar = 'N' 101 | ) 102 | argp.add_argument('-i', '--invert' 103 | ,help = 'Invert colors' 104 | ,default = False 105 | ,action = 'store_true' 106 | ) 107 | argp.add_argument('image' 108 | ,metavar = 'FILE' 109 | ,help = 'Image file path/url' 110 | ) 111 | return vars(argp.parse_args()) 112 | 113 | 114 | def __main__(): 115 | args = argparser() 116 | args['output'].write(image2term(args['image'], args['threshold'], args['ratio'], args['invert'])) 117 | args['output'].write('\n') 118 | 119 | 120 | if __name__ == '__main__': 121 | __main__() 122 | -------------------------------------------------------------------------------- /autoload/drawille/README.md: -------------------------------------------------------------------------------- 1 | DRAWILLE 2 | ======== 3 | 4 | 5 | Drawing in terminal with Unicode [Braille][] characters 6 | 7 | [Braille]: http://en.wikipedia.org/wiki/Braille 8 | 9 | [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=asciimoo&url=https://github.com/asciimoo/drawille&title=drawille&language=&tags=github&category=software) 10 | 11 | ![Drawille](docs/images/drawille_01.png) 12 | 13 | ![Drawille](docs/images/xkcd.png) 14 | 15 | ![Drawille](docs/images/sine_tracking.gif) 16 | 17 | ![Drawille](docs/images/rotating_cube.gif) 18 | 19 | 20 | ### USAGE 21 | 22 | ```python 23 | from __future__ import print_function 24 | from drawille import Canvas 25 | from math import sin, radians 26 | 27 | c = Canvas() 28 | 29 | for x in range(0, 1800, 10): 30 | c.set(x / 10, 10 + sin(radians(x)) * 10) 31 | 32 | print(c.frame()) 33 | ``` 34 | 35 | ![Usage](docs/images/usage.png) 36 | 37 | ```python 38 | from drawille import Turtle 39 | 40 | t = Turtle() 41 | 42 | for _ in range(36): 43 | t.right(10) 44 | for _ in range(36): 45 | t.right(10) 46 | t.forward(8) 47 | 48 | print(t.frame()) 49 | ``` 50 | 51 | ![Turtle](docs/images/turtle.png) 52 | 53 | 54 | ### Installation 55 | 56 | To install drawille, simply: 57 | 58 | ```bash 59 | $ pip install drawille 60 | ``` 61 | 62 | or 63 | 64 | ```bash 65 | $ easy_install drawille 66 | ``` 67 | 68 | 69 | ### Bugs 70 | 71 | Bugs or suggestions? Visit the [issue tracker](https://github.com/asciimoo/drawille/issues). 72 | 73 | 74 | Tested fonts 75 | 76 | | Font | Works | 77 | | ----- | ----- | 78 | | Fixed | Yes | 79 | 80 | 81 | Tested terminals 82 | 83 | | Terminal | Works | 84 | | ------------ | ----- | 85 | | rxvt-unicode | Yes | 86 | 87 | 88 | 89 | ### LICENSE 90 | 91 | ``` 92 | drawille is free software: you can redistribute it and/or modify 93 | it under the terms of the GNU Affero General Public License as published by 94 | the Free Software Foundation, either version 3 of the License, or 95 | (at your option) any later version. 96 | 97 | drawille is distributed in the hope that it will be useful, 98 | but WITHOUT ANY WARRANTY; without even the implied warranty of 99 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 100 | GNU Affero General Public License for more details. 101 | 102 | You should have received a copy of the GNU Affero General Public License 103 | along with drawille. If not, see < http://www.gnu.org/licenses/ >. 104 | 105 | (C) 2014- by Adam Tauber, 106 | ``` 107 | 108 | 109 | ### Other implementations / similar projects 110 | 111 | * [https://github.com/madbence/node-drawille](https://github.com/madbence/node-drawille) (nodejs) 112 | * [https://github.com/exrook/drawille-go](https://github.com/exrook/drawille-go) (go) 113 | * [https://github.com/maerch/ruby-drawille](https://github.com/maerch/ruby-drawille) (ruby) 114 | * [https://github.com/sunetos/TextPlots.jl](https://github.com/sunetos/TextPlots.jl) (julia) 115 | * [https://github.com/mkremins/drawille-clj](https://github.com/mkremins/drawille-clj) (clojure) 116 | * [https://github.com/mydzor/bash-drawille](https://github.com/mydzor/bash-drawille) (bash) 117 | * [https://github.com/hoelzro/term-drawille](https://github.com/hoelzro/term-drawille) (perl 5) 118 | * [https://github.com/whatthejeff/php-drawille](https://github.com/whatthejeff/php-drawille) (PHP) 119 | * [https://github.com/yamadapc/haskell-drawille](https://github.com/yamadapc/haskell-drawille) (haskell) 120 | * [https://github.com/P1start/drawille-rs](https://github.com/P1start/drawille-rs) (rust) 121 | * [https://github.com/liam-middlebrook/drawille-sharp](https://github.com/liam-middlebrook/drawille-sharp) (C#) 122 | * [https://github.com/asciimoo/lua-drawille](https://github.com/asciimoo/lua-drawille) (Lua) 123 | 124 | 125 | ### Further reading 126 | 127 | * [HackerNews](https://news.ycombinator.com/item?id=7776112) 128 | * [Reddit](http://www.reddit.com/r/programming/comments/263opn/drawille_pixel_graphics_in_a_terminal_using/) 129 | * [ohloh](http://www.ohloh.net/p/drawille) 130 | * [xkcd comics in Braille using drawille](http://blog.yjl.im/2014/04/xkcd-comics-in-braille-using-drawille.html) 131 | * [Braille unicode pixelation](http://blog.jverkamp.com/2014/05/30/braille-unicode-pixelation/) 132 | -------------------------------------------------------------------------------- /autoload/drawille/examples/flappy_birds.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import curses 3 | from drawille import Canvas, line 4 | from time import sleep 5 | from thread import start_new_thread 6 | from Queue import Queue 7 | import locale 8 | from random import randint 9 | 10 | locale.setlocale(locale.LC_ALL,"") 11 | 12 | stdscr = curses.initscr() 13 | stdscr.refresh() 14 | 15 | keys = Queue() 16 | 17 | speed = 0.0 18 | fps = 20 19 | frame_no = 0 20 | score = 0 21 | delta = frame_no / fps 22 | 23 | height = 100 24 | width = 100 25 | position = height / 2 26 | 27 | bird_map = [ 28 | #1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 29 | [0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0], #1 30 | [0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0], #2 31 | [0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0], #3 32 | [0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0], #4 33 | [0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0], #5 34 | [0,1,1,1,1,1,1,1,1,0,1,0,0,0,0,1,0,0,1,0,0], #6 35 | [1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0], #7 36 | [1,0,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,0], #8 37 | [1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1], #9 38 | [1,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0], #0 39 | [0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0], #1 40 | [0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0], #2 41 | [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0], #3 42 | ] 43 | bird = [] 44 | for y, row in enumerate(bird_map): 45 | for x,col in enumerate(row): 46 | if col: 47 | bird.append((x, y)) 48 | 49 | def read_keys(stdscr): 50 | while 1: 51 | c = stdscr.getch() 52 | keys.put(c) 53 | 54 | 55 | class Bar(): 56 | 57 | 58 | def __init__(self, bar_width, cap_height=4, space=3*13): 59 | self.height = randint(cap_height+space+1, height-1-cap_height) 60 | self.width = bar_width 61 | self.cap_height = cap_height 62 | self.x = width - bar_width - 1 63 | self.space = space 64 | 65 | 66 | def draw(self): 67 | for x,y in line(self.x, 68 | self.height, 69 | self.x+self.width, 70 | self.height): 71 | yield x, y 72 | for x,y in line(self.x, 73 | self.height, 74 | self.x, 75 | self.height+self.cap_height): 76 | yield x, y 77 | for x,y in line(self.x+self.width, 78 | self.height, 79 | x+self.width, 80 | self.height+self.cap_height): 81 | yield x, y 82 | for x,y in line(self.x, 83 | self.height+self.cap_height, 84 | self.x+2, 85 | self.height+self.cap_height): 86 | yield x, y 87 | for x,y in line(self.x+self.width-2, 88 | self.height+self.cap_height, 89 | self.x+self.width, 90 | self.height+self.cap_height): 91 | yield x, y 92 | for x,y in line(self.x+2, 93 | self.height+self.cap_height, 94 | self.x+2, 95 | height): 96 | yield x, y 97 | for x,y in line(self.x+self.width-2, 98 | self.height+self.cap_height, 99 | self.x+self.width-2, 100 | height): 101 | yield x, y 102 | 103 | for x,y in line(self.x, 104 | self.height-self.space, 105 | self.x+self.width, 106 | self.height-self.space): 107 | yield x, y 108 | for x,y in line(self.x, 109 | self.height-self.space, 110 | self.x, 111 | self.height-self.cap_height-self.space): 112 | yield x, y 113 | for x,y in line(self.x+self.width, 114 | self.height-self.space, 115 | x+self.width, 116 | self.height-self.cap_height-self.space): 117 | yield x, y 118 | for x,y in line(self.x, 119 | self.height-self.cap_height-self.space, 120 | self.x+2, 121 | self.height-self.cap_height-self.space): 122 | yield x, y 123 | for x,y in line(self.x+self.width-2, 124 | self.height-self.cap_height-self.space, 125 | self.x+self.width, 126 | self.height-self.cap_height-self.space): 127 | yield x, y 128 | for x,y in line(self.x+2, 129 | self.height-self.cap_height-self.space, 130 | self.x+2, 131 | 0): 132 | yield x, y 133 | for x,y in line(self.x+self.width-2, 134 | self.height-self.cap_height-self.space, 135 | self.x+self.width-2, 136 | 0): 137 | yield x, y 138 | 139 | def check_collision(bird_pos, bar): 140 | # TODO more efficient collision detection 141 | if bar.x > 21: 142 | return False 143 | if bar.height <= bird_pos-13 and bar.height+bar.space > bird_pos: 144 | return False 145 | for bar_x, bar_y in bar.draw(): 146 | for bird_x, bird_y in bird: 147 | if int(bird_x) == int(bar_x) and int(bird_y+bird_pos) == int(bar_y): 148 | return True 149 | return False 150 | 151 | def main(stdscr): 152 | global frame_no, speed, position, score 153 | c = Canvas() 154 | bar_width = 16 155 | bars = [Bar(bar_width)] 156 | stdscr.refresh() 157 | 158 | while True: 159 | frame_no += 1 160 | for bar in bars: 161 | if check_collision(position, bar): 162 | return 163 | while not keys.empty(): 164 | if keys.get() == 113: 165 | return 166 | speed = 32.0 167 | 168 | c.set(0,0) 169 | c.set(width, height) 170 | if frame_no % 50 == 0: 171 | bars.append(Bar(bar_width)) 172 | for x,y in bird: 173 | c.set(x,y+position) 174 | for bar_index, bar in enumerate(bars): 175 | if bar.x < 1: 176 | bars.pop(bar_index) 177 | score += 1 178 | else: 179 | bars[bar_index].x -= 1 180 | for x,y in bar.draw(): 181 | c.set(x,y) 182 | f = c.frame()+'\n' 183 | stdscr.addstr(0, 0, f) 184 | stdscr.addstr(height/4+1, 0, 'score: {0}'.format(score)) 185 | stdscr.refresh() 186 | c.clear() 187 | 188 | speed -= 2 189 | 190 | position -= speed/10 191 | 192 | if position < 0: 193 | position = 0 194 | speed = 0.0 195 | elif position > height-13: 196 | position = height-13 197 | speed = 0.0 198 | 199 | 200 | sleep(1.0/fps) 201 | 202 | 203 | if __name__ == '__main__': 204 | start_new_thread(read_keys, (stdscr,)) 205 | curses.wrapper(main) 206 | print('Final score: {0}'.format(score)) 207 | 208 | -------------------------------------------------------------------------------- /autoload/minimap.py: -------------------------------------------------------------------------------- 1 | # -* coding: utf-8 -*- 2 | # vim-minimap is free software: you can redistribute it and/or modify 3 | # it under the terms of the GNU Affero General Public License as published by 4 | # the Free Software Foundation, either version 3 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # vim-minimap is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU Affero General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU Affero General Public License 13 | # along with vim-minimap. If not, see < http://www.gnu.org/licenses/ >. 14 | # 15 | # (C) 2014- by Séverin Lemaignan for the VIM integration, 16 | # (C) 2014- by Adam Tauber for the Drawille part, 17 | 18 | import os 19 | import sys 20 | PY3 = sys.version_info[0] == 3 21 | 22 | import vim 23 | 24 | # Add the library to the Python path. 25 | for p in vim.eval("&runtimepath").split(','): 26 | plugin_dir = os.path.join(p, "autoload", "drawille") 27 | if os.path.exists(plugin_dir): 28 | if plugin_dir not in sys.path: 29 | sys.path.append(plugin_dir) 30 | break 31 | 32 | from drawille import * 33 | 34 | WIDTH = 20 35 | MINIMAP = "vim-minimap" 36 | 37 | def getmmwindow(): 38 | for b in vim.buffers: 39 | if b.name.endswith(MINIMAP): 40 | for w in vim.windows: 41 | if w.buffer == b: 42 | return w 43 | return None 44 | 45 | def getmainwindow(): 46 | for b in vim.buffers: 47 | if not b.name.endswith(MINIMAP) and not "NERD_tree" in b.name: 48 | for w in vim.windows: 49 | if w.buffer == b: 50 | return w 51 | return None 52 | 53 | def setmmautocmd(clear = False): 54 | vim.command(":augroup minimap_group") 55 | vim.command(":autocmd!") 56 | if not clear: 57 | # Properly close the minimap when quitting VIM (ie, when minimap is the last remaining window 58 | vim.command(":autocmd WinEnter if winnr('$') == 1|q|endif") 59 | vim.command(':autocmd CursorMoved,CursorMovedI,TextChanged,TextChangedI,BufWinEnter * MinimapUpdate') 60 | vim.command(":augroup END") 61 | 62 | def toggleminimap(): 63 | minimap = getmmwindow() 64 | if minimap: 65 | closeminimap() 66 | else: 67 | showminimap() 68 | 69 | def showminimap(): 70 | minimap = getmmwindow() 71 | 72 | # If the minimap window does not yet exist, create it 73 | if not minimap: 74 | # Save the currently active window to restore it later 75 | src = vim.current.window 76 | 77 | vim.command(":botright vnew %s" % MINIMAP) 78 | # make the new buffer 'temporary' 79 | vim.command(":setlocal buftype=nofile bufhidden=wipe noswapfile nobuflisted") 80 | # make ensure our buffer is uncluttered 81 | vim.command(":setlocal nonumber norelativenumber nolist nospell") 82 | 83 | # set all autocmds in a group 84 | setmmautocmd() 85 | 86 | minimap = vim.current.window 87 | 88 | minimap.width = WIDTH 89 | 90 | # fixed size 91 | vim.command(":set wfw") 92 | 93 | # Restore the active window 94 | vim.current.window = src 95 | 96 | vim.command(":call minimap#UpdateMinimap()") 97 | 98 | def updateminimap(): 99 | minimap = getmmwindow() 100 | src = vim.current.window 101 | 102 | HORIZ_SCALE = 0.2 103 | 104 | 105 | if not hasattr(src, 'buffer'): 106 | return 107 | 108 | # Ignore NERD_tree Buffers 109 | # TODO make configurable 110 | if "NERD_tree" in src.buffer.name: 111 | return 112 | 113 | if minimap and src.buffer == minimap.buffer: 114 | 115 | mainwindow = getmainwindow() 116 | if mainwindow is None: 117 | return 118 | 119 | if src.buffer != mainwindow.buffer: 120 | position_in_minimap = src.cursor[0] 121 | 122 | ratio = float(len(minimap.buffer)) / float(len(mainwindow.buffer)) 123 | 124 | new_position = int(float(position_in_minimap) / ratio) 125 | if new_position > len(mainwindow.buffer): 126 | new_position = len(mainwindow.buffer) 127 | 128 | mainwindow.cursor = (new_position, 0) # move to top left 129 | vim.current.window = mainwindow 130 | updateminimap() 131 | 132 | if minimap and src.buffer != minimap.buffer: 133 | 134 | mode = vim.eval("mode()") 135 | cursor = src.cursor 136 | 137 | vim.command("normal! H") 138 | topline = src.cursor[0] 139 | vim.command("normal! L") 140 | bottomline = src.cursor[0] 141 | 142 | def draw(lengths,indents, startline=0): 143 | 144 | c = Canvas() 145 | 146 | for y, l in enumerate(lengths): 147 | indent = int(indents[y] * HORIZ_SCALE) 148 | for x in range(2 * min(int(l * HORIZ_SCALE), WIDTH)): 149 | if(x>=indent): 150 | c.set(x, y) 151 | 152 | # pad with spaces to ensure uniform block highlighting 153 | if PY3: 154 | return [line.ljust(WIDTH, u'\u00A0') for line in c.rows()] 155 | else: 156 | return [unicode(line).ljust(WIDTH, u'\u00A0') for line in c.rows()] 157 | 158 | if minimap: 159 | 160 | vim.current.window = minimap 161 | highlight_group = vim.eval("g:minimap_highlight") 162 | lengths = [] 163 | indents = [] 164 | 165 | bufferlen = len(src.buffer) 166 | more_on_top = 0 167 | more_on_bottom = 0 168 | if bufferlen > 160 + bottomline - topline: 169 | if topline < 80: 170 | more_on_bottom = 80 - topline 171 | if bottomline + 80 > bufferlen: 172 | more_on_top = bottomline + 80 - bufferlen 173 | first = max(topline - 80 - more_on_top, 1) 174 | last = min(bottomline + 80 + more_on_bottom, bufferlen) 175 | for line in range(first, last): 176 | linestring = src.buffer[line] 177 | indents.append(len(linestring) - len(linestring.lstrip())) 178 | lengths.append(len(linestring)) 179 | 180 | vim.command(":setlocal modifiable") 181 | 182 | minimap.buffer[:] = draw(lengths,indents) 183 | # Highlight the current visible zone 184 | top = int((topline - first) / 4) 185 | bottom = int((bottomline -first)/ 4 + 1) 186 | vim.command("match {0} /\\%>0v\\%<{1}v\\%>{2}l\\%<{3}l./".format( 187 | highlight_group, WIDTH + 1, top, bottom)) 188 | 189 | # center the highlighted zone 190 | height = int(vim.eval("winheight(0)")) 191 | # first, put the cursor at the top of the buffer 192 | vim.command("normal! gg") 193 | # then, jump so that the active zone is centered 194 | if (top + (bottom - top) / 2) > height / 2: 195 | jump = min(top + (bottom - top) / 2 + height / 2, len(minimap.buffer)) 196 | vim.command("normal! %dgg" % jump) 197 | 198 | # prevent any further modification 199 | vim.command(":setlocal nomodifiable") 200 | 201 | vim.current.window = src 202 | 203 | # restore the current selection if we were in visual mode. 204 | if mode in ('v', 'V', '\026'): 205 | vim.command("normal! gv") 206 | 207 | src.cursor = cursor 208 | 209 | def closeminimap(): 210 | minimap = getmmwindow() 211 | src = vim.current.window 212 | if minimap: 213 | vim.current.window = minimap 214 | # clear the minimap autocmds 215 | setmmautocmd(True) 216 | vim.command(":quit!") 217 | # try the last window, but sometimes this one was already closed 218 | # (ex. tagbar toggle) which will lead to an exception 219 | try: 220 | vim.current.window = src 221 | except: 222 | vim.current.window = vim.windows[0] 223 | 224 | -------------------------------------------------------------------------------- /autoload/drawille/drawille.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # drawille is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # drawille is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with drawille. If not, see < http://www.gnu.org/licenses/ >. 15 | # 16 | # (C) 2014- by Adam Tauber, 17 | 18 | import math 19 | import os 20 | from sys import version_info 21 | from collections import defaultdict 22 | from time import sleep 23 | 24 | IS_PY3 = version_info[0] == 3 25 | 26 | if IS_PY3: 27 | unichr = chr 28 | 29 | """ 30 | 31 | http://www.alanwood.net/unicode/braille_patterns.html 32 | 33 | dots: 34 | ,___, 35 | |1 4| 36 | |2 5| 37 | |3 6| 38 | |7 8| 39 | ````` 40 | """ 41 | 42 | pixel_map = ((0x01, 0x08), 43 | (0x02, 0x10), 44 | (0x04, 0x20), 45 | (0x40, 0x80)) 46 | 47 | # braille unicode characters starts at 0x2800 48 | braille_char_offset = 0x2800 49 | 50 | 51 | # http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python 52 | def getTerminalSize(): 53 | """Returns terminal width, height 54 | """ 55 | env = os.environ 56 | 57 | def ioctl_GWINSZ(fd): 58 | try: 59 | import fcntl, termios, struct 60 | cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) 61 | except: 62 | return 63 | return cr 64 | 65 | cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 66 | 67 | if not cr: 68 | try: 69 | fd = os.open(os.ctermid(), os.O_RDONLY) 70 | cr = ioctl_GWINSZ(fd) 71 | os.close(fd) 72 | except: 73 | pass 74 | 75 | if not cr: 76 | cr = (env.get('LINES', 25), env.get('COLUMNS', 80)) 77 | 78 | return int(cr[1]), int(cr[0]) 79 | 80 | 81 | def normalize(coord): 82 | coord_type = type(coord) 83 | 84 | if coord_type == int: 85 | return coord 86 | elif coord_type == float: 87 | return int(round(coord)) 88 | else: 89 | raise TypeError("Unsupported coordinate type <{0}>".format(type(coord))) 90 | 91 | 92 | def intdefaultdict(): 93 | return defaultdict(int) 94 | 95 | 96 | def get_pos(x, y): 97 | """Convert x, y to cols, rows""" 98 | return normalize(x) // 2, normalize(y) // 4 99 | 100 | 101 | class Canvas(object): 102 | """This class implements the pixel surface.""" 103 | 104 | def __init__(self, line_ending=os.linesep): 105 | super(Canvas, self).__init__() 106 | self.clear() 107 | self.line_ending = line_ending 108 | 109 | 110 | def clear(self): 111 | """Remove all pixels from the :class:`Canvas` object.""" 112 | self.chars = defaultdict(intdefaultdict) 113 | 114 | 115 | def set(self, x, y): 116 | """Set a pixel of the :class:`Canvas` object. 117 | 118 | :param x: x coordinate of the pixel 119 | :param y: y coordinate of the pixel 120 | """ 121 | x = normalize(x) 122 | y = normalize(y) 123 | col, row = get_pos(x, y) 124 | 125 | if type(self.chars[row][col]) != int: 126 | return 127 | 128 | self.chars[row][col] |= pixel_map[y % 4][x % 2] 129 | 130 | 131 | def unset(self, x, y): 132 | """Unset a pixel of the :class:`Canvas` object. 133 | 134 | :param x: x coordinate of the pixel 135 | :param y: y coordinate of the pixel 136 | """ 137 | x = normalize(x) 138 | y = normalize(y) 139 | col, row = get_pos(x, y) 140 | 141 | if type(self.chars[row][col]) == int: 142 | self.chars[row][col] &= ~pixel_map[y % 4][x % 2] 143 | 144 | if type(self.chars[row][col]) != int or self.chars[row][col] == 0: 145 | del(self.chars[row][col]) 146 | 147 | if not self.chars.get(row): 148 | del(self.chars[row]) 149 | 150 | 151 | def toggle(self, x, y): 152 | """Toggle a pixel of the :class:`Canvas` object. 153 | 154 | :param x: x coordinate of the pixel 155 | :param y: y coordinate of the pixel 156 | """ 157 | x = normalize(x) 158 | y = normalize(y) 159 | col, row = get_pos(x, y) 160 | 161 | if type(self.chars[row][col]) != int or self.chars[row][col] & pixel_map[y % 4][x % 2]: 162 | self.unset(x, y) 163 | else: 164 | self.set(x, y) 165 | 166 | 167 | def set_text(self, x, y, text): 168 | """Set text to the given coords. 169 | 170 | :param x: x coordinate of the text start position 171 | :param y: y coordinate of the text start position 172 | """ 173 | col, row = get_pos(x, y) 174 | 175 | for i,c in enumerate(text): 176 | self.chars[row][col+i] = c 177 | 178 | 179 | def get(self, x, y): 180 | """Get the state of a pixel. Returns bool. 181 | 182 | :param x: x coordinate of the pixel 183 | :param y: y coordinate of the pixel 184 | """ 185 | x = normalize(x) 186 | y = normalize(y) 187 | dot_index = pixel_map[y % 4][x % 2] 188 | col, row = get_pos(x, y) 189 | char = self.chars.get(row, {}).get(col) 190 | 191 | if not char: 192 | return False 193 | 194 | if type(char) != int: 195 | return True 196 | 197 | return bool(char & dot_index) 198 | 199 | 200 | def rows(self, min_x=None, min_y=None, max_x=None, max_y=None): 201 | """Returns a list of the current :class:`Canvas` object lines. 202 | 203 | :param min_x: (optional) minimum x coordinate of the canvas 204 | :param min_y: (optional) minimum y coordinate of the canvas 205 | :param max_x: (optional) maximum x coordinate of the canvas 206 | :param max_y: (optional) maximum y coordinate of the canvas 207 | """ 208 | 209 | if not self.chars.keys(): 210 | return [] 211 | 212 | minrow = min_y // 4 if min_y != None else min(self.chars.keys()) 213 | maxrow = (max_y - 1) // 4 if max_y != None else max(self.chars.keys()) 214 | mincol = min_x // 2 if min_x != None else min(min(x.keys()) for x in self.chars.values()) 215 | maxcol = (max_x - 1) // 2 if max_x != None else max(max(x.keys()) for x in self.chars.values()) 216 | ret = [] 217 | 218 | for rownum in range(minrow, maxrow+1): 219 | if not rownum in self.chars: 220 | ret.append('') 221 | continue 222 | 223 | maxcol = (max_x - 1) // 2 if max_x != None else max(self.chars[rownum].keys()) 224 | row = [] 225 | 226 | for x in range(mincol, maxcol+1): 227 | char = self.chars[rownum].get(x) 228 | 229 | if not char: 230 | row.append(' ') 231 | elif type(char) != int: 232 | row.append(char) 233 | else: 234 | row.append(unichr(braille_char_offset+char)) 235 | 236 | ret.append(''.join(row)) 237 | 238 | return ret 239 | 240 | 241 | def frame(self, min_x=None, min_y=None, max_x=None, max_y=None): 242 | """String representation of the current :class:`Canvas` object pixels. 243 | 244 | :param min_x: (optional) minimum x coordinate of the canvas 245 | :param min_y: (optional) minimum y coordinate of the canvas 246 | :param max_x: (optional) maximum x coordinate of the canvas 247 | :param max_y: (optional) maximum y coordinate of the canvas 248 | """ 249 | ret = self.line_ending.join(self.rows(min_x, min_y, max_x, max_y)) 250 | 251 | if IS_PY3: 252 | return ret 253 | 254 | return ret.encode('utf-8') 255 | 256 | 257 | def line(x1, y1, x2, y2): 258 | """Returns the coords of the line between (x1, y1), (x2, y2) 259 | 260 | :param x1: x coordinate of the startpoint 261 | :param y1: y coordinate of the startpoint 262 | :param x2: x coordinate of the endpoint 263 | :param y2: y coordinate of the endpoint 264 | """ 265 | 266 | x1 = normalize(x1) 267 | y1 = normalize(y1) 268 | x2 = normalize(x2) 269 | y2 = normalize(y2) 270 | 271 | xdiff = max(x1, x2) - min(x1, x2) 272 | ydiff = max(y1, y2) - min(y1, y2) 273 | xdir = 1 if x1 <= x2 else -1 274 | ydir = 1 if y1 <= y2 else -1 275 | 276 | r = max(xdiff, ydiff) 277 | 278 | for i in range(r+1): 279 | x = x1 280 | y = y1 281 | 282 | if ydiff: 283 | y += (float(i) * ydiff) / r * ydir 284 | if xdiff: 285 | x += (float(i) * xdiff) / r * xdir 286 | 287 | yield (x, y) 288 | 289 | 290 | def polygon(center_x=0, center_y=0, sides=4, radius=4): 291 | degree = float(360) / sides 292 | 293 | for n in range(sides): 294 | a = n * degree 295 | b = (n + 1) * degree 296 | x1 = (center_x + math.cos(math.radians(a))) * (radius + 1) / 2 297 | y1 = (center_y + math.sin(math.radians(a))) * (radius + 1) / 2 298 | x2 = (center_x + math.cos(math.radians(b))) * (radius + 1) / 2 299 | y2 = (center_y + math.sin(math.radians(b))) * (radius + 1) / 2 300 | 301 | for x, y in line(x1, y1, x2, y2): 302 | yield x, y 303 | 304 | 305 | class Turtle(Canvas): 306 | """Turtle graphics interface 307 | http://en.wikipedia.org/wiki/Turtle_graphics 308 | """ 309 | 310 | def __init__(self, pos_x=0, pos_y=0): 311 | self.pos_x = pos_x 312 | self.pos_y = pos_y 313 | self.rotation = 0 314 | self.brush_on = True 315 | super(Turtle, self).__init__() 316 | 317 | 318 | def up(self): 319 | """Pull the brush up.""" 320 | self.brush_on = False 321 | 322 | 323 | def down(self): 324 | """Push the brush down.""" 325 | self.brush_on = True 326 | 327 | 328 | def forward(self, step): 329 | """Move the turtle forward. 330 | 331 | :param step: Integer. Distance to move forward. 332 | """ 333 | x = self.pos_x + math.cos(math.radians(self.rotation)) * step 334 | y = self.pos_y + math.sin(math.radians(self.rotation)) * step 335 | prev_brush_state = self.brush_on 336 | self.brush_on = True 337 | self.move(x, y) 338 | self.brush_on = prev_brush_state 339 | 340 | 341 | def move(self, x, y): 342 | """Move the turtle to a coordinate. 343 | 344 | :param x: x coordinate 345 | :param y: y coordinate 346 | """ 347 | if self.brush_on: 348 | for lx, ly in line(self.pos_x, self.pos_y, x, y): 349 | self.set(lx, ly) 350 | 351 | self.pos_x = x 352 | self.pos_y = y 353 | 354 | 355 | def right(self, angle): 356 | """Rotate the turtle (positive direction). 357 | 358 | :param angle: Integer. Rotation angle in degrees. 359 | """ 360 | self.rotation += angle 361 | 362 | 363 | def left(self, angle): 364 | """Rotate the turtle (negative direction). 365 | 366 | :param angle: Integer. Rotation angle in degrees. 367 | """ 368 | self.rotation -= angle 369 | 370 | 371 | def back(self, step): 372 | """Move the turtle backwards. 373 | 374 | :param step: Integer. Distance to move backwards. 375 | """ 376 | self.forward(-step) 377 | 378 | 379 | # aliases 380 | pu = up 381 | pd = down 382 | fd = forward 383 | mv = move 384 | rt = right 385 | lt = left 386 | bk = back 387 | 388 | 389 | def animate(canvas, fn, delay=1./24, *args, **kwargs): 390 | """Animation automatition function 391 | 392 | :param canvas: :class:`Canvas` object 393 | :param fn: Callable. Frame coord generator 394 | :param delay: Float. Delay between frames. 395 | :param *args, **kwargs: optional fn parameters 396 | """ 397 | import curses 398 | 399 | # python2 unicode curses fix 400 | if not IS_PY3: 401 | import locale 402 | locale.setlocale(locale.LC_ALL, "") 403 | 404 | def animation(stdscr): 405 | 406 | for frame in fn(*args, **kwargs): 407 | for x,y in frame: 408 | canvas.set(x,y) 409 | 410 | f = canvas.frame() 411 | stdscr.addstr(0, 0, '{0}\n'.format(f)) 412 | stdscr.refresh() 413 | if delay: 414 | sleep(delay) 415 | canvas.clear() 416 | 417 | curses.wrapper(animation) 418 | --------------------------------------------------------------------------------