This article records how I created a bouncing square video using manim,
the video is requiring below a few items.
- speeding is raised when hit the boundry
- the sound effect is seperated from this video, need to create sound based on the animation
- at the begining first N frames, the squre doesn’t move. this is usually my hahit rendering the amination.
- after collision, the sound volum is getting lower.
from manim import *
from os import system
import random
import numpy as np
from colour import Color
config.media_dir = "E:\PythonMedia\media"
config.frame_width = 9
config.frame_height = 16
#config.frame_size = (720,1280)#(3840,2160)#(2560,1440)#(1920,1080)#
config.frame_size = (720, 1280)
config.frame_rate = 40
config.background_color = GREY_C
def hex_to_RGB(hex_str):
""" #FFFFFF -> [255,255,255]"""
#Pass 16 to the integer function for change of base
return [int(hex_str[i:i+2], 16) for i in range(1,6,2)]
def get_color_gradient(c1, c2, n):
"""
Given two hex colors, returns a color gradient
with n colors.
"""
assert n > 1
c1_rgb = np.array(hex_to_RGB(c1))/255
c2_rgb = np.array(hex_to_RGB(c2))/255
mix_pcts = [x/(n-1) for x in range(n)]
rgb_colors = [((1-mix)*c1_rgb + (mix*c2_rgb)) for mix in mix_pcts]
return [Color("#" + "".join([format(int(round(val*255)), "02x") for val in item])) for item in rgb_colors]
class Myscreen(Rectangle):
def __init__(self,height=12):
width=height
height = height + 0.46
#height = height
Rectangle.__init__(self,width=width,height=height, stroke_width=2, fill_opacity=0)
def get_right(self):
return self.width/2
def get_left(self):
return -self.width/2
def get_top(self):
return self.height/2
def get_down(self):
return -self.height/2
class No10_BouncingSquare(Scene):
side_length = 0.5
count = 0
countIndex = 0
soundVols = []
vol = 120
frameIndex = 1
frameIndexBook = []
ballPlay = []
controlIndex = 10
def construct(self):
bases = ["#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff",
"#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff",
"#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff",
"#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff" ,
"#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff",
"#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff",
"#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff",
"#be0aff", "#580aff", "#147df5", "#0aefff", "#0aff99",
"#a1ff0a", "#deff0a", "#ffd300", "#ff8700", "#ff0000", "#be0aff"]
colorssss = []
for index in range(len(bases)):
if index < len(bases) - 1:
start = bases[index]
end = bases[index + 1]
colorssss.append([])
colors = get_color_gradient(start, end, 100)
for c in colors:
colorssss[index].append(c)
print(len(colorssss))
sizeColors = len(colorssss)
def update(mob, dt):
mob.shift(mob.velocity * dt * round(1* (1.02)**self.count, 2))
#print("{}".format(dt))
# self.add(Square(side_length=0.02, stroke_color=WHITE,fill_opacity=0, stroke_width=1).move_to(mob.get_center()))
handle_with_box(mob, screen, dt)
# handle_ball_collisions(mob)
self.frameIndex = self.frameIndex + 1
def handle_with_box(mob, scren, dt):
t1 = False
t2 = False
ball = []
beat = self.count
if self.frameIndex > 10:
if mob.get_right()[0] > scren.get_right() or mob.get_left()[0] < scren.get_left():
gap = 0
mob.velocity[0] = -mob.velocity[0]
while mob.get_right()[0] > scren.get_right():
gap = mob.get_right()[0] - scren.get_right()
if gap > scren.get_right():
mob.shift(scren.get_right() * LEFT)
else:
mob.shift(2*gap * LEFT)
beat = -1
print("{}".format(beat))
while mob.get_left()[0] < scren.get_left():
gap = mob.get_left()[0] - scren.get_left()
if abs(gap) > abs(screen.get_left()):
mob.shift(screen.get_left() * LEFT)
else:
mob.shift( 2 * gap * LEFT)
beat = -2
print("{}".format(beat))
t1 = True
if mob.get_top()[1] > scren.get_top() or mob.get_bottom()[1] < scren.get_down():
mob.velocity[1] = -mob.velocity[1]
gap = 0
while mob.get_top()[1] > scren.get_top():
gap = mob.get_top()[1] - scren.get_top()
if gap > scren.get_top():
mob.shift(scren.get_top() * DOWN)
else:
mob.shift(2 * gap * DOWN)
beat = -3
print("{}".format(beat))
while mob.get_bottom()[1] < scren.get_down():
gap = mob.get_bottom()[1] - scren.get_down()
if abs(gap) > abs(scren.get_down()):
mob.shift(scren.get_down() * DOWN)
else:
mob.shift(2 * gap * DOWN)
beat = -4
print("{}".format(beat))
t2 = True
self.countIndex = self.countIndex + 1
self.vol = self.vol - 2
if (t1 or t2 ) and self.frameIndex > 10:
self.count = self.count+1
self.countIndex = 0
self.vol = 100
ball.append(beat)
self.ballPlay.append(ball)
self.frameIndexBook.append(self.frameIndex)
self.soundVols.append(self.vol)
"""
self.add(
Square(side_length=self.side_length, stroke_color=BLACK, fill_opacity=1, color=colorssss[self.count % len(colorssss)][self.countIndex], stroke_width=1).move_to(
mob.get_center()))
"""
def compute_velocity(v1, v2, m1, m2, x1, x2):
return v1 - (2 * m2 / (m1 + m2)) * np.dot(v1 - v2, x1 - x2) / np.linalg.norm(x1 - x2) ** 2 * (x1 - x2)
screen = Myscreen(height=7)
#screen_copy = Myscreen(height=7.03)
#screen.move_to(UP*1)
author = Text(text="Wangbw", color=WHITE).move_to(DOWN*5)
speedText = Text("speeding = ", color=WHITE).move_to(UP*5).shift(LEFT*0.6)
speedNum = Text("{}".format(round(1 * (1.02) ** self.count, 2)), color=WHITE
).next_to(speedText,RIGHT,buff=0.5)
self.add(author)
self.add(speedText)
self.add(speedNum)
def changeNum(mob):
mob.become(Text("{}".format(round(1* (1.02)**self.count, 2)), color=WHITE).next_to(speedText,RIGHT,buff=0.5))
speedNum.add_updater(changeNum)
running = Square(side_length=self.side_length, stroke_color=WHITE, fill_opacity=1, color=ORANGE, stroke_width=1)
running.velocity = (3 * RIGHT + (3) * UP )
copy_rec = Rectangle(height=6, width=5.5, fill_opacity=0.8, stroke_width=0, stroke_color=BLACK)
#self.add(copy_rec)
running.move_to(screen.get_corner(DL)).shift(self.side_length/2 * RIGHT + self.side_length/2 * UP) # -3.22142857 + self.side_length/2 * RIGHT + self.side_length/2 * UP
#self.add(screen_copy)
self.add(screen)
running.add_updater(update)
self.add(running)
self.wait(59 + 1/config.frame_rate)
print(self.frameIndexBook)
print(self.ballPlay)
print(self.soundVols)
if __name__ == '__main__':
system("manim -ql {} No10_BouncingSquare ".format(__file__))
