Generate a full analysis video from a single function term.
The renderer computes the symbolic results first and then turns them into an animated walkthrough. Enter a function, choose a render quality, and generate the video directly here again.
~/projects/analysis_generator/output.mp4
The video will appear here
0%
Please wait while the video is being rendered. This might take a couple of minutes, depending on quality.
~/projects/analysis_generator/input.py
About this project
This was my first project using the Manim library for Python, and also one of the projects I'm most proud of.
I got the idea for this tool while I was learning about calculus in school. Origininally, I just wanted a basic CLI to perhaps automate parts of my math homework, but after realizing that there was an open source version of the library that the YouTube 3Blue1Brown uses, I decided to use that instead.
The primary way that German students learn about calculus is through a process known as Kurvendiskussion, which translates to something like 'curve analysis' or 'curve sketching'. The basic idea is that you are given nothing but the term of a function, typically a polynomial at the beginning and later an exponential, say f(x)=3x³+2x²-1x+5, and are then expected to sketch the function in a graph, gathering the necessary information using calculus.
This is typically a very procedural process, starting with the derivatives, symmetry, x-intercepts, y-intercepts and so on, which makes it relatively straightforward to implement as an algorithm.
Due to the many factors that play into rendering a video, this project is also by far my least stable one, and it's almost impossible to ensure that every single function works 100% correct. I'm constantly trying to improve this though, and for now, I'm hoping that it will be a good enough tool to help visualize the analysis of a function.
Technical details
In order to preserve at least a little bit of structure, the caluclation of the various parts of the analysis is done before the actual rendering of the scene. The calculations themselves are mostly done by SymPy, a Python library for symbolic mathematics. This library proved to be invaluable in the development of this project, and was very easy to work with.
The script for the calculation is actually just one big function, which should usually be avoided, but I found for the purposes of this project there wasn't much clarity to be gained by splitting this function into dozens of smaller ones that would then only be called in one place anyway.
The results of this script are stored in a Python dictionary. Originally, I saved this data as a JSON file, but then I discovered that I could also pass the data in Python directly.
So, the first step looks something like this:
Python
defcalculate_results(expression) -> dict: """ Computes all mathematical results for the AnalysisScene. """ x = Symbol('x') function = parse_expression(expression) derivative = diff(function, x) ... # Do all the calculations here # Excerpt for stationary points: stationary_points = [x for x in solve(derivative) if x.is_real] ... return \ { "function": str(function), "derivative": str(derivate), "func_range": str(func_range), ... }
The results of this script are then passed to the AnalysisScene, which then renders the scene. The scene itself is a inherited from the Manim library; my constructor adds the required math data and the language that is to be used.
The Scene:
Python
classAnalysisScene(Scene): """ Scene to generate the analysis animation """ def__init__(self, math_data: dict, language: str): self.DATA = math_data self.l = language super().__init__()
I will restrain myself from showing all of the actual code of the rendering process, because it is quite messy, but to summarize, the values are parsed from the dictionary into their LaTex versions to be displayed. A couple of checks are performed to make sure everything fits on the screen and to make sure all the values make sense.
The last thing I'll mention is the language implementation. I simply use the language that is passed into the scene as an index to a dictionary that contains all the strings. It's a simple implementation but works well on this scale.