diff --git a/divergence.py b/divergence.py new file mode 100644 index 00000000..4b80981d --- /dev/null +++ b/divergence.py @@ -0,0 +1,194 @@ +from mobject import Mobject +from mobject.image_mobject import MobjectFromRegion +from mobject.tex_mobject import TextMobject +from region import region_from_polygon_vertices +from topics.geometry import Arrow, Dot, Circle +from topics.number_line import NumberPlane +from scene import Scene +from animation.simple_animations import ShowCreation +from animation.transform import Transform, ApplyMethod, FadeOut + +from helpers import * + + +class DivergenceFlow(Scene): + DEFAULT_CONFIG = { + "arrow_spacing" : 1, + "dot_spacing" : 0.5, + "dot_color" : RED, + "text_color" : WHITE, + "arrow_color" : GREEN, + } + def use_function(self, function): + # def normalized_func(point): + # result = function(point) + # length = np.linalg.norm(result) + # if length > 0: + # result /= length + # # result *= self.arrow_spacing/2. + # return result + self.function = function + + def get_points(self, spacing): + x_radius, y_radius = [ + val-val%spacing + for val in SPACE_WIDTH, SPACE_HEIGHT + ] + return map(np.array, it.product( + np.arange(-x_radius, x_radius+spacing, spacing), + np.arange(-y_radius, y_radius+spacing, spacing), + [0] + )) + + + def add_plane(self): + self.add(NumberPlane().fade()) + + def add_dots(self): + points = self.get_points(self.dot_spacing) + self.dots = Mobject(*map(Dot, points)) + self.dots.highlight(self.dot_color) + self.play(ShowCreation(self.dots)) + self.dither() + + def add_arrows(self): + if not hasattr(self, "function"): + raise Exception("Must run use_function first") + points = self.get_points(self.arrow_spacing) + points = filter( + lambda p : np.linalg.norm(self.function(p)) > 0.01, + points + ) + angles = map(angle_of_vector, map(self.function, points)) + prototype = Arrow( + ORIGIN, RIGHT*self.arrow_spacing/2., + color = self.arrow_color, + tip_length = 0.1, + buff = 0 + ) + self.arrows = Mobject(*[ + prototype.copy().rotate(angle).shift(point) + for point, angle in zip(points, angles) + ]) + + self.play(ShowCreation(self.arrows)) + self.dither() + + + def flow(self): + if not hasattr(self, "function"): + raise Exception("Must run use_function first") + points = self.get_points(self.dot_spacing) + end_dots = Mobject(*[ + Dot(point+self.function(point)) + for point in points + ]) + end_dots.highlight(self.dot_color) + + self.play(Transform(self.dots, end_dots)) + self.dither() + + def label(self, text, time = 5): + mob = TextMobject(text) + mob.scale(1.5) + mob.to_edge(UP) + rectangle = region_from_polygon_vertices(*[ + mob.get_corner(vect) + 0.3*vect + for vect in [ + UP+RIGHT, + UP+LEFT, + DOWN+LEFT, + DOWN+RIGHT + ] + ]) + mob.highlight(self.text_color) + rectangle = MobjectFromRegion(rectangle, "#111111") + rectangle.point_thickness = 3 + self.add(rectangle, mob) + self.dither(time) + self.remove(mob, rectangle) + + + +class InwardFlow(DivergenceFlow): + def construct(self): + circle = Circle(color = YELLOW_C) + self.use_function( + lambda p : -p/(2*np.linalg.norm(0.5*p)**0.5+0.01) + ) + self.add_plane() + self.add_arrows() + self.play(ShowCreation(circle)) + self.label(""" + Notice that arrows point inward around the origin + """) + self.label(""" + Watch what that means as we let particles in \\\\ + space flow along the arrows + """) + self.remove(circle) + circle.scale(0.5) + self.add_dots() + self.flow() + self.remove(self.arrows) + self.play(ShowCreation(circle)) + self.label(""" + The density of points around \\\\ + the origin has become greater + """) + + self.label(""" + This means the divergence of the vector field \\\\ + is negative at the origin: + $\\nabla \\cdot \\vec{\\textbf{v}}(0, 0) < 0$ + """) + self.dither() + + +class OutwardFlow(DivergenceFlow): + def construct(self): + circle = Circle(color = YELLOW_C, radius = 2) + self.use_function( + lambda p : p/(2*np.linalg.norm(0.5*p)**0.5+0.01) + ) + self.add_plane() + self.add_arrows() + self.play(ShowCreation(circle)) + self.label(""" + On the other hand, when arrows \\\\ + indicate an outward flow\\dots + """) + self.remove(circle) + circle.scale(0.5) + self.add_dots() + self.flow() + self.remove(self.arrows) + self.play(ShowCreation(circle)) + self.label(""" + The density of points near \\\\ + the origin becomes smaller + """) + self.label(""" + This means the divergence of the vector field \\\\ + is positive at the origin: + $\\nabla \\cdot \\vec{\\textbf{v}}(0, 0) > 0$ + """) + self.dither() + +class ArticleExample(DivergenceFlow): + def construct(self): + def raw_function((x, y, z)): + return (2*x-y, x*x, 0) + def normalized_function(p): + result = raw_function(p) + return result/(np.linalg.norm(result)+0.01) + self.use_function(normalized_function) + + self.add_plane() + self.add_arrows() + self.add_dots() + self.flow() + self.remove(self.arrows) + self.dither() + +