This article records how I created a polyBeat animation using manim.
the video is requiring below a few items.
- in my below code, the beats are 3,4,6,8,12, the embed youtube beat rate is different but same renderring logic
- the sound effect is seperated
###PolyBeat 3,4,6,8,12
from manim import *
from os import system
import os
from colour import Color
config.media_dir = "E:\PythonMedia\media"
config.frame_width = 9
config.frame_height = 16
config.frame_size = (720,1280)
config.frame_rate = 50
config.background_color = BLACK
class CirclePolyBeat_3_4_6_8_12(Scene):
restart = True
ballPositions = []
pattern = []
lines = []
speedings = []
frameIndex = 1
frameIndexBook = []
ballPlay = []
def construct(self):
author = Text("Wangbw", color=WHITE).move_to(DOWN*5)
self.add(author)
colors = [Color("#be0aff"),Color("#580aff"),Color("#147df5"),Color("#0aefff"),Color("#0aff99"),
Color("#a1ff0a"),Color("#deff0a"),Color("#ffd300"),Color("#ff8700"),Color("#ff0000")]
circleFake = Circle(radius=4.2, stroke_color=WHITE, fill_opacity=0, stroke_width=10,
stroke_opacity=0.7 ).rotate(PI / 2)
self.add(circleFake)
circle = Circle(radius=4, stroke_color=WHITE, fill_opacity=0).rotate(PI/2)
#self.add(circle)
speeding = 0.01
def draw_collection():
count = len(self.pattern)
ballBook = []
if self.restart == True:
self.ballPositions = []
self.lines = []
self.speedings = []
for x in self.pattern:
self.ballPositions.append(0)
self.speedings.append(speeding * x)
for index in range(len(self.pattern)):
tmp = Line(start=circle.point_from_proportion(index * 1 / count), end=[0, 0, 0],
stroke_width=10, stroke_opacity=0.8)
line = Line(start=tmp.point_from_proportion(0), end=tmp.point_from_proportion(0.95),
stroke_width=10, stroke_opacity=0.8)
self.lines.append(line)
vg = VGroup()
for x in self.lines:
vg.add(x)
###run ball
for index in range(len(self.pattern)):
temp = self.ballPositions[index] + self.speedings[index]
width = 0
if temp<0:
temp = - temp
self.speedings[index] = -self.speedings[index]
if temp>1:
temp = 2 - temp
self.speedings[index] = -self.speedings[index]
##play sound
circleFake.set_style(stroke_color=colors[index])
ballBook.append(self.pattern[index])
if temp > 0.90:
width = 2
self.ballPositions[index] = temp
ball = Circle(radius=0.2, fill_opacity=1, color=colors[index],stroke_width=width, stroke_color=WHITE).move_to(self.lines[index].point_from_proportion(self.ballPositions[index]))
vg.add(ball)
centerCircle = Circle(radius=0.15, color=RED, fill_opacity=1)
vg.add(centerCircle)
self.restart= False
if len(ballBook)>0 :
if len(self.frameIndexBook)>0:
if self.frameIndex - self.frameIndexBook[len(self.frameIndexBook) -1 ] == 1:
print(self.frameIndex)
if len(ballBook) >= len(self.ballPlay[len(self.ballPlay)-1]):
self.frameIndexBook[len(self.frameIndexBook) -1 ] = self.frameIndex
for x in ballBook:
self.ballPlay[len(self.ballPlay) - 1].append(x)
else:
for x in ballBook:
self.ballPlay[len(self.ballPlay) - 1].append(x)
else:
self.ballPlay.append(ballBook)
self.frameIndexBook.append(self.frameIndex)
else:
self.ballPlay.append(ballBook)
self.frameIndexBook.append(self.frameIndex)
self.frameIndex = self.frameIndex + 1
return vg
vg_mobject = always_redraw(draw_collection)
self.add(vg_mobject)
playBook = [[3],[3,4],[3,4,6],[3,4,6,12],[3,4,8],[3,4,12],[3,4,6,8,12],[3,4,6]]
#playBook = [[3], [3, 4], [3, 4, 6]]
info = Text("{}".format(self.pattern)).move_to(UP * 5)
self.add(info)
for book in playBook:
self.restart = True
self.pattern = book
runTime = 0
if len(book)<=2:
runTime=2
if len(book)>=3:
runTime=60
self.remove(info)
info = Text("{}".format(self.pattern)).move_to(UP*5)
self.add(info)
self.play(Create(Text("").move_to(LEFT*6), run_time=runTime))
print(self.frameIndexBook)
print(self.ballPlay)
if __name__ == '__main__':
system("manim -ql {} CirclePolyBeat_3_4_6_8_12 --disable_caching".format(__file__))
