def __init__( self, fill_opacity=0, stroke_width=3, length=DEFAULT_ARROW_TIP_LENGTH, start_angle=PI, **kwargs, ): self.start_angle = start_angle Circle.__init__( self, fill_opacity=fill_opacity, stroke_width=stroke_width, **kwargs ) self.width = length self.stretch_to_fit_height(length)
def test_animationbuilder_in_group(using_opengl_renderer): sqr = Square() circ = Circle() animation_group = AnimationGroup( sqr.animate.shift(DOWN).scale(2), FadeIn(circ)) assert all( isinstance(anim, Animation) for anim in animation_group.animations) succession = Succession(sqr.animate.shift(DOWN).scale(2), FadeIn(circ)) assert all(isinstance(anim, Animation) for anim in succession.animations)
def add_bases(self): """Adds the end caps of the cylinder.""" color = self.color if config["renderer"] == "opengl" else self.fill_color opacity = self.opacity if config["renderer"] == "opengl" else self.fill_opacity self.base_top = Circle( radius=self.radius, color=color, fill_opacity=opacity, shade_in_3d=True, stroke_width=0, ) self.base_top.shift(self.u_range[1] * IN) self.base_bottom = Circle( radius=self.radius, color=color, fill_opacity=opacity, shade_in_3d=True, stroke_width=0, ) self.base_bottom.shift(self.u_range[0] * IN) self.add(self.base_top, self.base_bottom)
def __init__(self, mobject: Mobject, shape: Type = Rectangle, fade_in=False, fade_out=False, time_width=0.3, buff: float = SMALL_BUFF, color: Color = YELLOW, run_time=1, stroke_width=DEFAULT_STROKE_WIDTH, **kwargs): if shape is Rectangle: frame = SurroundingRectangle( mobject, color, buff, stroke_width=stroke_width, ) elif shape is Circle: frame = Circle(color=color, stroke_width=stroke_width).surround( mobject, buffer_factor=1, ) radius = frame.width / 2 frame.scale((radius + buff) / radius) else: raise ValueError("shape should be either Rectangle or Circle.") if fade_in and fade_out: super().__init__( FadeIn(frame, run_time=run_time / 2), FadeOut(frame, run_time=run_time / 2), **kwargs, ) elif fade_in: frame.reverse_direction() super().__init__( FadeIn(frame, run_time=run_time / 2), Uncreate(frame, run_time=run_time / 2), **kwargs, ) elif fade_out: super().__init__( Create(frame, run_time=run_time / 2), FadeOut(frame, run_time=run_time / 2), **kwargs, ) else: super().__init__( ShowPassingFlash(frame, time_width, run_time=run_time), **kwargs)
def test_animationgroup_is_passing_remover_to_animations( animation_remover, animation_group_remover): scene = Scene() sqr_animation = Create(Square(), remover=animation_remover) circ_animation = Write(Circle(), remover=animation_remover) animation_group = AnimationGroup(sqr_animation, circ_animation, remover=animation_group_remover) scene.play(animation_group) scene.wait(0.1) assert sqr_animation.remover assert circ_animation.remover
def __init__( self, base_radius=1, height=1, direction=Z_AXIS, show_base=False, v_range=[0, TAU], u_min=0, checkerboard_colors=False, **kwargs, ): self.direction = direction self.theta = PI - np.arctan(base_radius / height) super().__init__( self.func, v_range=v_range, u_range=[u_min, np.sqrt(base_radius**2 + height**2)], checkerboard_colors=checkerboard_colors, **kwargs, ) # used for rotations self._current_theta = 0 self._current_phi = 0 if show_base: self.base_circle = Circle( radius=base_radius, color=self.fill_color, fill_opacity=self.fill_opacity, stroke_width=0, ) self.base_circle.shift(height * IN) self.add(self.base_circle) self._rotate_to_direction()
def test_animationgroup_is_passing_remover_to_nested_animationgroups(): scene = Scene() sqr_animation = Create(Square()) circ_animation = Write(Circle(), remover=True) polygon_animation = Create(RegularPolygon(5)) animation_group = AnimationGroup( AnimationGroup(sqr_animation, polygon_animation), circ_animation, remover=True, ) scene.play(animation_group) scene.wait(0.1) assert sqr_animation.remover assert circ_animation.remover assert polygon_animation.remover
def _circle_to_mobject(self, circle_element: MinidomElement, style: dict): """Creates a Circle VMobject from a SVG <circle> command. Parameters ---------- circle_element : :class:`minidom.Element` A SVG circle path command. style : :class:`dict` Style specification, using the SVG names for properties. Returns ------- Circle A Circle VMobject """ x, y, r = (self._attribute_to_float(circle_element.getAttribute(key)) if circle_element.hasAttribute(key) else 0.0 for key in ("cx", "cy", "r")) return Circle(radius=r, **parse_style(style)).shift(x * RIGHT + y * DOWN)
def __init__(self, dark_theme: bool = True): super().__init__() logo_green = "#81b29a" logo_blue = "#454866" logo_red = "#e07a5f" m_height_over_anim_height = 0.75748 self.font_color = "#ece6e2" if dark_theme else "#343434" self.scale_factor = 1 self.M = MathTex(r"\mathbb{M}").scale(7).set_color(self.font_color) self.M.shift(2.25 * LEFT + 1.5 * UP) self.circle = Circle(color=logo_green, fill_opacity=1).shift(LEFT) self.square = Square(color=logo_blue, fill_opacity=1).shift(UP) self.triangle = Triangle(color=logo_red, fill_opacity=1).shift(RIGHT) self.shapes = VGroup(self.triangle, self.square, self.circle) self.add(self.shapes, self.M) self.move_to(ORIGIN) anim = VGroup() for i, ch in enumerate("anim"): tex = Tex( "\\textbf{" + ch + "}", tex_template=TexFontTemplates.gnu_freeserif_freesans, ) if i != 0: tex.next_to(anim, buff=0.01) tex.align_to(self.M, DOWN) anim.add(tex) anim.set_color(self.font_color) anim.height = m_height_over_anim_height * self.M.height # Note: "anim" is only shown in the expanded state # and thus not yet added to the submobjects of self. self.anim = anim
class Cylinder(Surface): """A cylinder, defined by its height, radius and direction, Examples --------- .. manim:: ExampleCylinder :save_last_frame: class ExampleCylinder(ThreeDScene): def construct(self): axes = ThreeDAxes() cylinder = Cylinder(radius=2, height=3) self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES) self.add(axes, cylinder) Parameters --------- radius : :class:`float` The radius of the cylinder. height : :class:`float` The height of the cylinder. direction : :class:`numpy.array` The direction of the central axis of the cylinder. v_range : :class:`Sequence[float]` The height along the height axis (given by direction) to start and end on. show_ends : :class:`bool` Whether to show the end caps or not. """ def __init__( self, radius=1, height=2, direction=Z_AXIS, v_range=[0, TAU], show_ends=True, resolution=(24, 24), **kwargs, ): self._height = height self.radius = radius super().__init__( self.func, resolution=resolution, u_range=[-self._height / 2, self._height / 2], v_range=v_range, **kwargs, ) if show_ends: self.add_bases() self._current_phi = 0 self._current_theta = 0 self.set_direction(direction) def func(self, u, v): """Converts from cylindrical coordinates to cartesian. Parameters --------- u : :class:`float` The height. v : :class:`float` The azimuthal angle. """ height = u phi = v r = self.radius return np.array([r * np.cos(phi), r * np.sin(phi), height]) def add_bases(self): """Adds the end caps of the cylinder.""" color = self.color if config["renderer"] == "opengl" else self.fill_color opacity = self.opacity if config["renderer"] == "opengl" else self.fill_opacity self.base_top = Circle( radius=self.radius, color=color, fill_opacity=opacity, shade_in_3d=True, stroke_width=0, ) self.base_top.shift(self.u_range[1] * IN) self.base_bottom = Circle( radius=self.radius, color=color, fill_opacity=opacity, shade_in_3d=True, stroke_width=0, ) self.base_bottom.shift(self.u_range[0] * IN) self.add(self.base_top, self.base_bottom) def _rotate_to_direction(self): x, y, z = self.direction r = np.sqrt(x**2 + y**2 + z**2) if r > 0: theta = np.arccos(z / r) else: theta = 0 if x == 0: if y == 0: # along the z axis phi = 0 else: # along the x axis phi = np.arctan(np.inf) if y < 0: phi += PI else: phi = np.arctan(y / x) if x < 0: phi += PI # undo old rotation (in reverse direction) self.rotate(-self._current_phi, Z_AXIS, about_point=ORIGIN) self.rotate(-self._current_theta, Y_AXIS, about_point=ORIGIN) # do new rotation self.rotate(theta, Y_AXIS, about_point=ORIGIN) self.rotate(phi, Z_AXIS, about_point=ORIGIN) # store new values self._current_theta = theta self._current_phi = phi def set_direction(self, direction): # if get_norm(direction) is get_norm(self.direction): # pass self.direction = direction self._rotate_to_direction() def get_direction(self): """Returns the direction of the central axis of the cylinder.""" return self.direction
class Cone(Surface): """A circular cone. Can be defined using 2 parameters: its height, and its base radius. The polar angle, theta, can be calculated using arctan(base_radius / height) The spherical radius, r, is calculated using the pythagorean theorem. Examples -------- .. manim:: ExampleCone :save_last_frame: class ExampleCone(ThreeDScene): def construct(self): axes = ThreeDAxes() cone = Cone(direction=X_AXIS+Y_AXIS+2*Z_AXIS) self.set_camera_orientation(phi=5*PI/11, theta=PI/9) self.add(axes, cone) Parameters -------- base_radius : :class:`float` The base radius from which the cone tapers. height : :class:`float` The height measured from the plane formed by the base_radius to the apex of the cone. direction : :class:`numpy.array` The direction of the apex. show_base : :class:`bool` Whether to show the base plane or not. v_range : :class:`Sequence[float]` The azimuthal angle to start and end at. u_min : :class:`float` The radius at the apex. checkerboard_colors : :class:`bool` Show checkerboard grid texture on the cone. """ def __init__( self, base_radius=1, height=1, direction=Z_AXIS, show_base=False, v_range=[0, TAU], u_min=0, checkerboard_colors=False, **kwargs, ): self.direction = direction self.theta = PI - np.arctan(base_radius / height) super().__init__( self.func, v_range=v_range, u_range=[u_min, np.sqrt(base_radius**2 + height**2)], checkerboard_colors=checkerboard_colors, **kwargs, ) # used for rotations self._current_theta = 0 self._current_phi = 0 if show_base: self.base_circle = Circle( radius=base_radius, color=self.fill_color, fill_opacity=self.fill_opacity, stroke_width=0, ) self.base_circle.shift(height * IN) self.add(self.base_circle) self._rotate_to_direction() def func(self, u, v): """Converts from spherical coordinates to cartesian. Parameters --------- u : :class:`float` The radius. v : :class:`float` The azimuthal angle. """ r = u phi = v return np.array( [ r * np.sin(self.theta) * np.cos(phi), r * np.sin(self.theta) * np.sin(phi), r * np.cos(self.theta), ], ) def _rotate_to_direction(self): x, y, z = self.direction r = np.sqrt(x**2 + y**2 + z**2) if r > 0: theta = np.arccos(z / r) else: theta = 0 if x == 0: if y == 0: # along the z axis phi = 0 else: phi = np.arctan(np.inf) if y < 0: phi += PI else: phi = np.arctan(y / x) if x < 0: phi += PI # Undo old rotation (in reverse order) self.rotate(-self._current_phi, Z_AXIS, about_point=ORIGIN) self.rotate(-self._current_theta, Y_AXIS, about_point=ORIGIN) # Do new rotation self.rotate(theta, Y_AXIS, about_point=ORIGIN) self.rotate(phi, Z_AXIS, about_point=ORIGIN) # Store values self._current_theta = theta self._current_phi = phi def set_direction(self, direction): self.direction = direction self._rotate_to_direction() def get_direction(self): return self.direction