Mandelbrot set curses visualization program
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

pyasciimandel.py 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #!/usr/bin/env python3
  2. # Copyright 2019 Yann Weber <yannweb@member.fsf.org>
  3. #
  4. # pyasciimandel is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # any later version.
  8. #
  9. # pyasciimandel is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with pyasciimandel. If not, see <http://www.gnu.org/licenses/>.
  16. #
  17. import sys
  18. import os
  19. import random
  20. import curses
  21. import multiprocessing
  22. def in_set(x0, y0, colors, max_iter):
  23. """ Return True if given point is in mandel set"""
  24. c = y0 * 1j + x0
  25. z = 0.0j+0.0
  26. i = 0
  27. for i in range(max_iter):
  28. z = z**2 + c
  29. if z.imag ** 2 + z.real ** 2 >= 4:
  30. return colors[int(i * len(colors) / max_iter)]
  31. return ' '
  32. def process_line(args):
  33. point_list, colors, max_iter = args
  34. return ''.join([in_set(x, y, colors, max_iter) for y, x in point_list])
  35. def fill_screen_multi(stdscr, pool, zoom, center, colors, max_iter):
  36. lines, cols = stdscr.getmaxyx()
  37. lines-=1
  38. term_sz = lines, cols
  39. set_len = (2, 3)
  40. deltas = [ set_len[i] / zoom / term_sz[i] for i in range(2)]
  41. ranges = [ (int(-sz/2), int(sz/2)) for sz in term_sz]
  42. ys, xs = [ [center[c] + (i * deltas[c]) for i in range(*ranges[c])]
  43. for c in range(2)]
  44. points = [([(y, x) for x in xs], colors, max_iter) for y in ys]
  45. for i, line in enumerate(pool.imap(process_line, points)):
  46. stdscr.addstr(i,0, line)
  47. if __name__ == '__main__':
  48. center = [0,-0.5] # Mandelbrot Set center (y,x)
  49. # other color sets
  50. #std_colors = ".,:;-=+*oO8&@#"
  51. #std_colors = ".:-=+*#%@"
  52. #std_colors = '.\'`^",:;-=+*mwqpdbkhaoOIlXYUJCLQ0OZ><?][}{)(|\\/#MW&8%B@$'
  53. std_colors = '.\'`^",:;-=+*mwaoO><?][}{)(|\\/#MW&8%B@$'
  54. colors = list(std_colors)
  55. max_iter = 290
  56. zoom = 1
  57. decimal_mode = False
  58. pool = multiprocessing.Pool(os.cpu_count())
  59. stdscr = curses.initscr()
  60. curses.noecho()
  61. curses.cbreak()
  62. try:
  63. while True:
  64. iter2cols = lambda i, max_i: colors[int(i * len(colors) / max_iter)]
  65. fill_screen_multi(stdscr, pool, zoom, center, colors, max_iter)
  66. stdscr.refresh()
  67. k = stdscr.getkey()
  68. if k == '\x1b':
  69. if stdscr.getkey() != '[':
  70. continue
  71. stdscr.refresh()
  72. k = stdscr.getkey()
  73. kmap = {'D':'j', 'A':'i', 'B':'k', 'C':'l'}
  74. if k in kmap:
  75. k = kmap[k]
  76. else:
  77. print('\\1b[%r' % k, file=sys.stderr)
  78. sys.stderr.flush()
  79. continue
  80. delta_xmove = 0.7 / zoom
  81. delta_ymove = 0.5 / zoom
  82. delta_zoom = 1.2
  83. if k == 'q':
  84. exit(0)
  85. elif k == '+':
  86. zoom *= delta_zoom
  87. elif k == '-':
  88. if zoom >= delta_zoom:
  89. zoom /= delta_zoom
  90. elif k == 'j':
  91. center[1] -= delta_xmove
  92. elif k == 'l':
  93. center[1] += delta_xmove
  94. elif k == 'i':
  95. center[0] -= delta_ymove
  96. elif k == 'k':
  97. center[0] += delta_ymove
  98. elif k == 's':
  99. random.shuffle(colors)
  100. elif k == 'S':
  101. colors = list(std_colors)
  102. elif k == 'd':
  103. decimal_mode = not decimal_mode
  104. elif k == 'p':
  105. max_iter += 25
  106. elif k == 'm':
  107. max_iter -= 25
  108. elif k == 'I':
  109. lines, cols = stdscr.getmaxyx()
  110. win = curses.newwin(20, 40, (lines - 20)//2,
  111. (cols - 40)//2)
  112. win.addstr(1,12, 'Current status')
  113. win.addstr(2,12, '==============')
  114. win.addstr(5,0, '''\
  115. Max iteration = %d
  116. Zoom level = %f
  117. X = %f
  118. Y = %f''' % (max_iter, zoom, center[0], center[1]))
  119. win.border()
  120. win.refresh()
  121. k = stdscr.getkey()
  122. else:
  123. lines, cols = stdscr.getmaxyx()
  124. win = curses.newwin(22, 40, (lines - 20)//2,
  125. (cols - 40)//2)
  126. win.addstr(1,11, 'pyasciimandel')
  127. win.addstr(2,11, '=============')
  128. win.addstr(4,0, '''\
  129. + zoom in
  130. - zoom out
  131. j left
  132. l right
  133. i up
  134. k down
  135. s shuffle colors
  136. S standard colors
  137. d decimal mode (useless)
  138. p add 25 to maximum iterations
  139. m sub 25 to maximum iterations
  140. I information
  141. q quit
  142. h display this help
  143. ''')
  144. win.addstr(20, 1, 'Press any key to continue')
  145. win.border()
  146. win.refresh()
  147. k = stdscr.getkey()
  148. if k == 'q':
  149. exit(0)
  150. finally:
  151. curses.nocbreak()
  152. curses.echo()
  153. curses.endwin()
  154. exit()