text: from manimlib.animation.animation import Animation
  from manimlib.animation.composition import AnimationGroup
  from manimlib.animation.fading import FadeOut
  from manimlib.animation.creation import ShowCreation
  from manimlib.animation.creation import Write
  from manimlib.animation.transform import ApplyMethod
  from manimlib.animation.transform import MoveToTarget
  from manimlib.constants import *
  from manimlib.for_3b1b_videos.pi_class import PiCreatureClass
  from manimlib.mobject.mobject import Group
  from manimlib.mobject.svg.drawings import SpeechBubble
  from manimlib.utils.config_ops import digest_config
  from manimlib.utils.rate_functions import squish_rate_func
  from manimlib.utils.rate_functions import there_and_back
  
  
  class Blink(ApplyMethod):
      CONFIG = {
          "rate_func": squish_rate_func(there_and_back)
      }
  
      def __init__(self, pi_creature, **kwargs):
          ApplyMethod.__init__(self, pi_creature.blink, **kwargs)
  
  
  class PiCreatureBubbleIntroduction(AnimationGroup):
      CONFIG = {
          "target_mode": "speaking",
          "bubble_class": SpeechBubble,
          "change_mode_kwargs": {},
          "bubble_creation_class": ShowCreation,
          "bubble_creation_kwargs": {},
          "bubble_kwargs": {},
          "content_introduction_class": Write,
          "content_introduction_kwargs": {},
          "look_at_arg": None,
      }
  
      def __init__(self, pi_creature, *content, **kwargs):
          digest_config(self, kwargs)
          bubble = pi_creature.get_bubble(
              *content,
              bubble_class=self.bubble_class,
              **self.bubble_kwargs
          )
          Group(bubble, bubble.content).shift_onto_screen()
  
          pi_creature.generate_target()
          pi_creature.target.change_mode(self.target_mode)
          if self.look_at_arg is not None:
              pi_creature.target.look_at(self.look_at_arg)
  
          change_mode = MoveToTarget(pi_creature, **self.change_mode_kwargs)
          bubble_creation = self.bubble_creation_class(
              bubble, **self.bubble_creation_kwargs
          )
          content_introduction = self.content_introduction_class(
              bubble.content, **self.content_introduction_kwargs
          )
          AnimationGroup.__init__(
              self, change_mode, bubble_creation, content_introduction,
              **kwargs
          )
  
  
  class PiCreatureSays(PiCreatureBubbleIntroduction):
      CONFIG = {
          "target_mode": "speaking",
          "bubble_class": SpeechBubble,
      }
  
  
  class RemovePiCreatureBubble(AnimationGroup):
      CONFIG = {
          "target_mode": "plain",
          "look_at_arg": None,
          "remover": True,
      }
  
      def __init__(self, pi_creature, **kwargs):
          assert hasattr(pi_creature, "bubble")
          digest_config(self, kwargs, locals())
  
          pi_creature.generate_target()
          pi_creature.target.change_mode(self.target_mode)
          if self.look_at_arg is not None:
              pi_creature.target.look_at(self.look_at_arg)
  
          AnimationGroup.__init__(
              self,
              MoveToTarget(pi_creature),
              FadeOut(pi_creature.bubble),
              FadeOut(pi_creature.bubble.content),
          )
  
      def clean_up_from_scene(self, scene=None):
          AnimationGroup.clean_up_from_scene(self, scene)
          self.pi_creature.bubble = None
          if scene is not None:
              scene.add(self.pi_creature)
  
  
  class FlashThroughClass(Animation):
      CONFIG = {
          "highlight_color": GREEN,
      }
  
      def __init__(self, mobject, mode="linear", **kwargs):
          if not isinstance(mobject, PiCreatureClass):
              raise Exception("FlashThroughClass mobject must be a PiCreatureClass")
          digest_config(self, kwargs)
          self.indices = list(range(mobject.height * mobject.width))
          if mode == "random":
              np.random.shuffle(self.indices)
          Animation.__init__(self, mobject, **kwargs)
  
      def interpolate_mobject(self, alpha):
          index = int(np.floor(alpha * self.mobject.height * self.mobject.width))
          for pi in self.mobject:
              pi.set_color(BLUE_E)
          if index < self.mobject.height * self.mobject.width:
              self.mobject[self.indices[index]].set_color(self.highlight_color)
  