bouncing ball inside square

This article records how I created a bouncing square video using manim,

the video is requiring below a few items.

  1. speeding is raised when hit the boundry
  2. the sound effect is seperated from this video, need to create sound based on the animation
  3. at the begining first N frames, the squre doesn’t move. this is usually my hahit rendering the amination.
  4. 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__))

By Admin

Think-Math Website