# app/core/animations/text_animations.py from pathlib import Path from typing import Dict # Each function returns: {"scene_name": "SceneClass", "scene_code": ""} # The scene_code should import manim and define the requested Scene subclass. Pipeline will write it to disk. def fade_in(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: """ Premium Cinematic Fade-In (edge-safe, high contrast version). Glows in softly, with depth and smooth easing — looks natural, vivid, and perfect for logos or text reveals. """ scene_name = "FadeInScene" svg_name = svg_path.name scene_code = f""" from manim import * class {scene_name}(Scene): def construct(self): # Transparent background self.camera.background_color = None # Load SVG svg = SVGMobject(r"{svg_name}") svg.set(width=6) svg.set_stroke(width=0) svg.set_fill(opacity=1) # 🌟 Create glow aura (simulated blur via scaling layers) glow_layers = VGroup( *[ svg.copy() .set_opacity(0.15 - 0.03 * i) .scale(1.02 + i * 0.01) .set_z_index(-1) for i in range(4) ] ) svg_group = VGroup(glow_layers, svg) svg_group.move_to(ORIGIN) svg_group.set_opacity(0) self.add(svg_group) # ✨ Dramatic fade-in with pop self.play( svg_group.animate .scale(1.08) .shift(UP * 0.15) .set_opacity(1), rate_func=lambda t: smooth(t) ** 0.7, # nonlinear ease run_time=2.5 ) # 🌬️ Subtle cinematic recoil self.play( svg_group.animate.scale(0.98).shift(DOWN * 0.05), rate_func=there_and_back, run_time=1.5 ) # Hold on final frame self.wait(0.8) """ return {"scene_name": scene_name, "scene_code": scene_code} # def slide_in_left(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: # scene_name = "SlideInLeft" # scene_code = f''' # from manim import * # class {scene_name}(Scene): # def construct(self): # svg = SVGMobject("{svg_path.name}") # svg.set(width=6) # svg.shift(LEFT*8) # self.play(svg.animate.shift(RIGHT*8), run_time=1) # self.wait(0.5) # ''' # return {"scene_name": scene_name, "scene_code": scene_code} def slide_in_left(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "SlideInLeft" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): # Load SVG with high quality svg = SVGMobject("{svg_path.name}") svg.set(width=6) # adjust size svg.set_stroke(width=1.5) # make strokes visible but smooth svg.set_fill(opacity=1) # ensure colors are fully visible # Start off-screen left svg.shift(LEFT*8) # Slide in with smooth easing self.play( svg.animate.shift(RIGHT*8).set_opacity(1), run_time=1.5, rate_func=smooth ) # Optional small bounce for natural feel self.play( svg.animate.shift(LEFT*0.2), svg.animate.shift(RIGHT*0.2), run_time=0.3, rate_func=there_and_back ) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} # def pop_bounce(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: # scene_name = "PopBounce" # scene_code = f''' # from manim import * # class {scene_name}(Scene): # def construct(self): # svg = SVGMobject("{svg_path.name}") # svg.set(width=4) # self.play(svg.animate.scale(1.2), run_time=0.15) # self.play(svg.animate.scale(0.9), run_time=0.12) # self.play(svg.animate.scale(1.0), run_time=0.13) # self.wait(0.5) # ''' # return {"scene_name": scene_name, "scene_code": scene_code} def pop_bounce(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "PopBounce" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): # Load SVG with high quality svg = SVGMobject("{svg_path.name}") svg.set(width=4) svg.set_stroke(width=1.5) svg.set_fill(opacity=1) # Optional: start slightly smaller and transparent for better pop effect svg.scale(0.8) svg.set_opacity(0) # Fade in while popping self.play( svg.animate.set_opacity(1).scale(1.25), run_time=0.15, rate_func=smooth ) self.play( svg.animate.scale(0.95), run_time=0.12, rate_func=there_and_back ) self.play( svg.animate.scale(1.0), run_time=0.13, rate_func=smooth ) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} def zoom_in(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "ZoomIn" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): svg = SVGMobject("{svg_path.name}").scale(0.2) self.add(svg) self.play(svg.animate.scale(5), run_time=1) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} # def typewriter(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: # scene_name = "Typewriter" # scene_code = f''' # from manim import * # class {scene_name}(Scene): # def construct(self): # # For text-based typewriter you might want to use Text/MarkupText # txt = Text("Typewriter placeholder") # self.play(Write(txt), run_time=1.5) # self.wait(0.5) # ''' # return {"scene_name": scene_name, "scene_code": scene_code} # def typewriter(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: # scene_name = "Typewriter" # scene_code = f''' # from manim import * # class {scene_name}(Scene): # def construct(self): # # Load the uploaded SVG # svg = SVGMobject("{svg_path.name}") # svg.set(width=6) # svg.set_stroke(width=2) # svg.set_fill(opacity=1) # svg.move_to(ORIGIN) # # Break into submobjects to animate each path like typewriter strokes # for sub in svg: # sub.set_opacity(0) # start invisible # # Typewriter-style progressive drawing # for sub in svg: # sub.set_opacity(1) # self.play(Create(sub), run_time=0.3, rate_func=smooth) # self.wait(0.5) # ''' # return {"scene_name": scene_name, "scene_code": scene_code} # def typewriter(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: # scene_name = "Typewriter" # scene_code = f''' # from manim import * # class {scene_name}(Scene): # def construct(self): # # Load the uploaded SVG # svg = SVGMobject("{svg_path.name}") # svg.set(width=6) # svg.set_stroke(width=2) # svg.set_fill(opacity=1) # svg.move_to(ORIGIN) # # Start invisible # for sub in svg: # sub.set_opacity(0) # sub.scale(0.95) # start slightly smaller for cinematic pop # # Draw each path progressively # for sub in svg: # sub.set_opacity(1) # self.play( # Create(sub), # sub.animate.scale(1.05), # subtle overshoot # run_time=0.35, # rate_func=smooth # ) # # Scale back smoothly # self.play(sub.animate.scale(0.95), run_time=0.1, rate_func=there_and_back) # # Optional: add a blinking cursor at the end # cursor = Line(start=svg.get_right(), end=svg.get_right() + UP*0.5, stroke_width=2) # self.add(cursor) # for _ in range(6): # self.play(cursor.animate.set_opacity(0), run_time=0.3) # self.play(cursor.animate.set_opacity(1), run_time=0.3) # self.wait(0.5) # ''' # return {"scene_name": scene_name, "scene_code": scene_code} def typewriter(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "Typewriter" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): # Load the uploaded SVG svg = SVGMobject("{svg_path.name}") svg.set(width=6) svg.set_stroke(width=2) svg.set_fill(opacity=1) svg.move_to(ORIGIN) # Start almost invisible for smooth fade-in for sub in svg: sub.set_opacity(0.05) sub.scale(0.98) # subtle initial scale for cinematic feel # Animate each path like a cinematic typewriter for sub in svg: self.play( Create(sub), sub.animate.set_opacity(1).scale(1.0), # fade in while drawing run_time=0.35, rate_func=smooth ) # Optional: add a subtle blinking cursor at the end cursor = Line(start=svg.get_right(), end=svg.get_right() + UP*0.5, stroke_width=2) self.add(cursor) for _ in range(6): self.play(cursor.animate.set_opacity(0), run_time=0.3, rate_func=smooth) self.play(cursor.animate.set_opacity(1), run_time=0.3, rate_func=smooth) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} def wipe_mask(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "WipeMask" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): svg = SVGMobject("{svg_path.name}") bar = Rectangle(width=8, height=6).set_fill(BLACK, opacity=1).to_edge(LEFT) self.add(bar) self.play(bar.animate.shift(RIGHT*8), run_time=1) self.add(svg) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} def flip_rotate(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "FlipRotate" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): svg = SVGMobject("{svg_path.name}") self.play(Rotate(svg, angle=PI), run_time=0.8) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} def blur_in(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "BlurIn" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): svg = SVGMobject("{svg_path.name}") # TODO: Add blur-like effect (manim's blur is limited) self.play(FadeIn(svg), run_time=0.8) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} def scale_up(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "ScaleUp" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): svg = SVGMobject("{svg_path.name}") svg.scale(0.2) self.play(svg.animate.scale(5), run_time=1) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} # --- Premium / stylish placeholders --- def neon_glow(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "NeonGlow" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): svg = SVGMobject("{svg_path.name}") # TODO: Implement neon glow using strokes, duplications, and blurs self.play(FadeIn(svg), run_time=0.8) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} def gradient_fill(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "GradientFill" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): # TODO: Add gradient fill by generating shapes or using shaders svg = SVGMobject("{svg_path.name}") self.play(FadeIn(svg), run_time=0.8) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} def wave_ripple(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "WaveRipple" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): # TODO: Animate vertices or use transforms for wave effect svg = SVGMobject("{svg_path.name}") self.play(FadeIn(svg), run_time=0.8) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code} # Add more premium functions (split_text, stroke_draw, brush_reveal, liquid, 3d_rotate, sparkle) def split_text(svg_path: Path, working_dir: Path, task_meta: Dict) -> Dict: scene_name = "SplitTextReveal" scene_code = f''' from manim import * class {scene_name}(Scene): def construct(self): # TODO: Split text into letters and animate svg = SVGMobject("{svg_path.name}") self.play(FadeIn(svg), run_time=0.8) self.wait(0.5) ''' return {"scene_name": scene_name, "scene_code": scene_code}