apollonius.py


SUBMITTED BY: Eupator

DATE: March 2, 2017, 12:44 p.m.

UPDATED: March 2, 2017, 12:45 p.m.

FORMAT: Text only

SIZE: 3.6 kB

HITS: 1320

  1. import mobius, cairo, math, numpy, cmath, time
  2. IMAGE_WIDTH = 1920
  3. IMAGE_HEIGHT = 1080
  4. SCALE = 225.0 # pixels_per_unit
  5. FPS = 60
  6. DURATION = 29
  7. NUM_FRAMES = FPS*DURATION
  8. def draw_frame(path, circles, depth):
  9. # cairo stuff
  10. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, IMAGE_WIDTH, IMAGE_HEIGHT)
  11. cr = cairo.Context(surface)
  12. cr.set_line_width(1.0)
  13. # define our transforms
  14. generators = []
  15. for circle in circles:
  16. generators.append(mobius.circle_involution(circle[0], circle[1]))
  17. # drawing routine
  18. cr.set_source_rgb(0.0, 1.0, 0.0)
  19. draw_word_action(cr, circles, generators, [], depth)
  20. surface.write_to_png(path)
  21. def draw_word_action(cr, circles, generators, word, depth):
  22. "Apply the given word to circles and draw, then compose a generator and recur."
  23. # apply current word to points and draw
  24. circle_images = [x for x in circles]
  25. for index in word:
  26. circle_images = [generators[index].of_circle(x[0], x[1]) for x in circle_images]
  27. circle_images = [x for x in circle_images if x[1] != 0]
  28. for circle in circle_images:
  29. cr.new_path()
  30. center, radius = circle
  31. x = IMAGE_WIDTH/2 + center.real*SCALE
  32. y = IMAGE_HEIGHT/2 - center.imag*SCALE
  33. cr.arc(x, y, radius*SCALE, 0.0, 2.0*math.pi)
  34. cr.stroke()
  35. if depth > 0:
  36. # recur at smaller depth
  37. for i in range(len(generators)):
  38. gen = generators[i]
  39. set_color_from_palette(cr, i)
  40. if word == [] or word[-1] != i:
  41. draw_word_action(cr, circles, generators, word + [i], depth - 1)
  42. def set_color_from_palette(cr, i):
  43. "Picks the current color based on a pre-set palette."
  44. palette = [(1.0, 0.0, 0.0),
  45. (0.0, 1.0, 0.0),
  46. (0.0, 0.0, 1.0),
  47. (1.0, 1.0, 0.0),
  48. (1.0, 0.0, 1.0),
  49. (0.0, 1.0, 1.0)]
  50. i = i % len(palette)
  51. (r, g, b) = palette[i]
  52. cr.set_source_rgb(r, g, b)
  53. def circles_converge(num_circles, radius, depth):
  54. start_time = time.clock()
  55. radius = 1.0
  56. for frame in range(NUM_FRAMES):
  57. # pick circle centers, radii
  58. distance_from_origin = radius*1.5*(NUM_FRAMES - 2.0*frame)/float(NUM_FRAMES)
  59. circles = []
  60. for i in range(num_circles):
  61. center = cmath.exp(float(i)/num_circles*2j*math.pi)*distance_from_origin
  62. circles.append((center, radius))
  63. report_progress(frame, start_time)
  64. draw_frame(get_filename(frame), circles, depth)
  65. def get_filename(t):
  66. "Takes frame and names it appropriately."
  67. full_digits = len(str(NUM_FRAMES))
  68. digits = len(str(t))
  69. leading_zeroes = ""
  70. for i in range(full_digits - digits):
  71. leading_zeroes = leading_zeroes + "0"
  72. return "frame" + leading_zeroes + str(t) + ".png"
  73. def report_progress(frame_index, start_time):
  74. print "Rendering frame " + str(frame_index) + " of " + str(NUM_FRAMES)
  75. elapsed = time.clock() - start_time
  76. print "Time elapsed: " + str(elapsed)
  77. if frame_index > 0:
  78. seconds_remaining = int(float(NUM_FRAMES - frame_index)*elapsed/frame_index)
  79. seconds = seconds_remaining%60
  80. minutes = (seconds_remaining/60)%60
  81. hours = seconds_remaining/3600
  82. remaining = str(hours) + "h" + str(minutes) + "m" + str(seconds) + "s"
  83. else:
  84. remaining = "(unknown)"
  85. print "Estimated time remaining: " + str(remaining)
  86. circles_converge(3, 1.0, 9)

comments powered by Disqus