数学动画生成manim.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. from toolbox import CatchException, update_ui, gen_time_str
  2. from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
  3. from .crazy_utils import input_clipping
  4. def inspect_dependency(chatbot, history):
  5. # 尝试导入依赖,如果缺少依赖,则给出安装建议
  6. try:
  7. import manim
  8. return True
  9. except:
  10. chatbot.append(["导入依赖失败", "使用该模块需要额外依赖,安装方法:```pip install manim manimgl```"])
  11. yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
  12. return False
  13. def eval_manim(code):
  14. import subprocess, sys, os, shutil
  15. with open('gpt_log/MyAnimation.py', 'w', encoding='utf8') as f:
  16. f.write(code)
  17. def get_class_name(class_string):
  18. import re
  19. # Use regex to extract the class name
  20. class_name = re.search(r'class (\w+)\(', class_string).group(1)
  21. return class_name
  22. class_name = get_class_name(code)
  23. try:
  24. subprocess.check_output([sys.executable, '-c', f"from gpt_log.MyAnimation import {class_name}; {class_name}().render()"])
  25. shutil.move('media/videos/1080p60/{class_name}.mp4', f'gpt_log/{class_name}-{gen_time_str()}.mp4')
  26. return f'gpt_log/{gen_time_str()}.mp4'
  27. except subprocess.CalledProcessError as e:
  28. output = e.output.decode()
  29. print(f"Command returned non-zero exit status {e.returncode}: {output}.")
  30. return f"Evaluating python script failed: {e.output}."
  31. except:
  32. print('generating mp4 failed')
  33. return "Generating mp4 failed."
  34. def get_code_block(reply):
  35. import re
  36. pattern = r"```([\s\S]*?)```" # regex pattern to match code blocks
  37. matches = re.findall(pattern, reply) # find all code blocks in text
  38. if len(matches) != 1:
  39. raise RuntimeError("GPT is not generating proper code.")
  40. return matches[0].strip('python') # code block
  41. @CatchException
  42. def 动画生成(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
  43. """
  44. txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
  45. llm_kwargs gpt模型参数,如温度和top_p等,一般原样传递下去就行
  46. plugin_kwargs 插件模型的参数,暂时没有用武之地
  47. chatbot 聊天显示框的句柄,用于显示给用户
  48. history 聊天历史,前情提要
  49. system_prompt 给gpt的静默提醒
  50. web_port 当前软件运行的端口号
  51. """
  52. # 清空历史,以免输入溢出
  53. history = []
  54. # 基本信息:功能、贡献者
  55. chatbot.append([
  56. "函数插件功能?",
  57. "生成数学动画, 此插件处于开发阶段, 建议暂时不要使用, 作者: binary-husky, 插件初始化中 ..."
  58. ])
  59. yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
  60. # 尝试导入依赖, 如果缺少依赖, 则给出安装建议
  61. dep_ok = yield from inspect_dependency(chatbot=chatbot, history=history) # 刷新界面
  62. if not dep_ok: return
  63. # 输入
  64. i_say = f'Generate a animation to show: ' + txt
  65. demo = ["Here is some examples of manim", examples_of_manim()]
  66. _, demo = input_clipping(inputs="", history=demo, max_token_limit=2560)
  67. # 开始
  68. gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
  69. inputs=i_say, inputs_show_user=i_say,
  70. llm_kwargs=llm_kwargs, chatbot=chatbot, history=demo,
  71. sys_prompt=
  72. r"Write a animation script with 3blue1brown's manim. "+
  73. r"Please begin with `from manim import *`. " +
  74. r"Answer me with a code block wrapped by ```."
  75. )
  76. chatbot.append(["开始生成动画", "..."])
  77. history.extend([i_say, gpt_say])
  78. yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新
  79. # 将代码转为动画
  80. code = get_code_block(gpt_say)
  81. res = eval_manim(code)
  82. chatbot.append(("生成的视频文件路径", res))
  83. yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新
  84. # 在这里放一些网上搜集的demo,辅助gpt生成代码
  85. def examples_of_manim():
  86. return r"""
  87. ```
  88. class MovingGroupToDestination(Scene):
  89. def construct(self):
  90. group = VGroup(Dot(LEFT), Dot(ORIGIN), Dot(RIGHT, color=RED), Dot(2 * RIGHT)).scale(1.4)
  91. dest = Dot([4, 3, 0], color=YELLOW)
  92. self.add(group, dest)
  93. self.play(group.animate.shift(dest.get_center() - group[2].get_center()))
  94. self.wait(0.5)
  95. ```
  96. ```
  97. class LatexWithMovingFramebox(Scene):
  98. def construct(self):
  99. text=MathTex(
  100. "\\frac{d}{dx}f(x)g(x)=","f(x)\\frac{d}{dx}g(x)","+",
  101. "g(x)\\frac{d}{dx}f(x)"
  102. )
  103. self.play(Write(text))
  104. framebox1 = SurroundingRectangle(text[1], buff = .1)
  105. framebox2 = SurroundingRectangle(text[3], buff = .1)
  106. self.play(
  107. Create(framebox1),
  108. )
  109. self.wait()
  110. self.play(
  111. ReplacementTransform(framebox1,framebox2),
  112. )
  113. self.wait()
  114. ```
  115. ```
  116. class PointWithTrace(Scene):
  117. def construct(self):
  118. path = VMobject()
  119. dot = Dot()
  120. path.set_points_as_corners([dot.get_center(), dot.get_center()])
  121. def update_path(path):
  122. previous_path = path.copy()
  123. previous_path.add_points_as_corners([dot.get_center()])
  124. path.become(previous_path)
  125. path.add_updater(update_path)
  126. self.add(path, dot)
  127. self.play(Rotating(dot, radians=PI, about_point=RIGHT, run_time=2))
  128. self.wait()
  129. self.play(dot.animate.shift(UP))
  130. self.play(dot.animate.shift(LEFT))
  131. self.wait()
  132. ```
  133. ```
  134. # do not use get_graph, this funciton is deprecated
  135. class ExampleFunctionGraph(Scene):
  136. def construct(self):
  137. cos_func = FunctionGraph(
  138. lambda t: np.cos(t) + 0.5 * np.cos(7 * t) + (1 / 7) * np.cos(14 * t),
  139. color=RED,
  140. )
  141. sin_func_1 = FunctionGraph(
  142. lambda t: np.sin(t) + 0.5 * np.sin(7 * t) + (1 / 7) * np.sin(14 * t),
  143. color=BLUE,
  144. )
  145. sin_func_2 = FunctionGraph(
  146. lambda t: np.sin(t) + 0.5 * np.sin(7 * t) + (1 / 7) * np.sin(14 * t),
  147. x_range=[-4, 4],
  148. color=GREEN,
  149. ).move_to([0, 1, 0])
  150. self.add(cos_func, sin_func_1, sin_func_2)
  151. ```
  152. """