dr-sandeep commited on
Commit
ce55d98
·
verified ·
1 Parent(s): a461c64

Create app.py

Browse files

initial commit app.py

Files changed (1) hide show
  1. app.py +472 -0
app.py ADDED
@@ -0,0 +1,472 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import subprocess
3
+ import tempfile
4
+ import os
5
+ import shutil
6
+ from pathlib import Path
7
+ import time
8
+
9
+ # Default example code
10
+ DEFAULT_CODE = '''from manim import *
11
+
12
+ class SimpleAnimation(Scene):
13
+ def construct(self):
14
+ # Create a simple text animation
15
+ title = Text("Welcome to Manimator!", font_size=48, color=BLUE)
16
+ subtitle = Text("Mathematical Animation Renderer", font_size=24)
17
+ subtitle.next_to(title, DOWN)
18
+
19
+ # Create geometric shapes
20
+ circle = Circle(radius=1, color=RED)
21
+ square = Square(side_length=2, color=GREEN)
22
+ triangle = Triangle(color=YELLOW)
23
+
24
+ # Position shapes
25
+ circle.shift(LEFT * 3)
26
+ square.shift(RIGHT * 3)
27
+ triangle.shift(DOWN * 2)
28
+
29
+ # Animate!
30
+ self.play(Write(title), run_time=2)
31
+ self.wait(0.5)
32
+ self.play(Write(subtitle), run_time=1.5)
33
+ self.wait(1)
34
+
35
+ # Show shapes
36
+ self.play(
37
+ Create(circle),
38
+ Create(square),
39
+ Create(triangle),
40
+ run_time=2
41
+ )
42
+ self.wait(1)
43
+
44
+ # Transform shapes
45
+ self.play(
46
+ circle.animate.set_color(PURPLE),
47
+ square.animate.rotate(PI/4),
48
+ triangle.animate.scale(1.5),
49
+ run_time=2
50
+ )
51
+ self.wait(1)
52
+
53
+ # Group and move
54
+ everything = Group(title, subtitle, circle, square, triangle)
55
+ self.play(everything.animate.shift(UP * 0.5), run_time=1.5)
56
+ self.wait(2)
57
+ '''
58
+
59
+ EXAMPLE_CODES = {
60
+ "Simple Animation": DEFAULT_CODE,
61
+
62
+ "Moving Shapes": '''from manim import *
63
+
64
+ class MovingShapes(Scene):
65
+ def construct(self):
66
+ # Create shapes
67
+ circle = Circle(color=BLUE)
68
+ square = Square(color=RED).shift(RIGHT * 2)
69
+
70
+ # Animations
71
+ self.play(Create(circle))
72
+ self.play(circle.animate.shift(RIGHT * 2))
73
+ self.play(Transform(circle, square))
74
+ self.play(square.animate.scale(2).rotate(PI/4))
75
+ self.wait()
76
+ ''',
77
+
78
+ "Text Animation": '''from manim import *
79
+
80
+ class TextAnimation(Scene):
81
+ def construct(self):
82
+ # Create text
83
+ text1 = Text("This is Manim", font_size=72)
84
+ text2 = Text("Create Amazing Animations!", font_size=48, color=YELLOW)
85
+ text2.next_to(text1, DOWN)
86
+
87
+ # Animate
88
+ self.play(Write(text1))
89
+ self.wait()
90
+ self.play(text1.animate.scale(0.7).shift(UP))
91
+ self.play(FadeIn(text2, shift=UP))
92
+ self.wait(2)
93
+ ''',
94
+
95
+ "Graph Example": '''from manim import *
96
+
97
+ class GraphExample(Scene):
98
+ def construct(self):
99
+ # Create axes
100
+ axes = Axes(
101
+ x_range=[-3, 3, 1],
102
+ y_range=[-3, 3, 1],
103
+ axis_config={"color": BLUE}
104
+ )
105
+
106
+ # Create graph
107
+ graph = axes.plot(lambda x: x**2, color=RED)
108
+ graph_label = axes.get_graph_label(graph, label='f(x) = x^2')
109
+
110
+ # Animate
111
+ self.play(Create(axes))
112
+ self.play(Create(graph), Write(graph_label))
113
+ self.wait(2)
114
+ ''',
115
+
116
+ "Dot Movement": '''from manim import *
117
+
118
+ class DotMovement(Scene):
119
+ def construct(self):
120
+ # Create dots
121
+ dot = Dot(color=RED)
122
+
123
+ # Create path
124
+ path = VMobject()
125
+ path.set_points_as_corners([
126
+ LEFT * 2,
127
+ UP * 2,
128
+ RIGHT * 2,
129
+ DOWN * 2,
130
+ LEFT * 2
131
+ ])
132
+ path.set_color(BLUE)
133
+
134
+ # Animate
135
+ self.play(Create(path))
136
+ self.play(MoveAlongPath(dot, path), run_time=4)
137
+ self.wait()
138
+ ''',
139
+
140
+ "Math Formulas (Text)": '''from manim import *
141
+
142
+ class MathFormulas(Scene):
143
+ def construct(self):
144
+ # Math using Text() - works without LaTeX!
145
+ title = Text("Famous Formulas", font_size=48, color=BLUE)
146
+ title.to_edge(UP)
147
+
148
+ # Einstein's equation
149
+ einstein = Text("E = mc²", font_size=60, color=YELLOW)
150
+
151
+ # Pythagorean theorem
152
+ pythagoras = Text("a² + b² = c²", font_size=60, color=GREEN)
153
+ pythagoras.next_to(einstein, DOWN, buff=1)
154
+
155
+ # Euler's identity
156
+ euler = Text("e^(iπ) + 1 = 0", font_size=60, color=RED)
157
+ euler.next_to(pythagoras, DOWN, buff=1)
158
+
159
+ # Animate
160
+ self.play(Write(title))
161
+ self.wait(0.5)
162
+ self.play(Write(einstein), run_time=2)
163
+ self.wait(1)
164
+ self.play(Write(pythagoras), run_time=2)
165
+ self.wait(1)
166
+ self.play(Write(euler), run_time=2)
167
+ self.wait(2)
168
+ ''',
169
+
170
+ "3D Shapes": '''from manim import *
171
+
172
+ class ThreeDShapes(Scene):
173
+ def construct(self):
174
+ # Note: 3D requires ThreeDScene for rotation
175
+ # This shows basic 3D objects in 2D view
176
+
177
+ title = Text("3D Objects", font_size=48)
178
+ title.to_edge(UP)
179
+
180
+ # Create 3D shapes (shown in 2D projection)
181
+ sphere = Sphere(radius=1, color=BLUE).shift(LEFT * 3)
182
+ cube = Cube(side_length=2, color=RED)
183
+ torus = Torus(color=GREEN).shift(RIGHT * 3).scale(0.7)
184
+
185
+ # Animate
186
+ self.play(Write(title))
187
+ self.wait(0.5)
188
+ self.play(Create(sphere), Create(cube), Create(torus), run_time=3)
189
+ self.wait(2)
190
+ ''',
191
+ }
192
+
193
+ def extract_scene_class(code):
194
+ """Extract the Scene class name from code"""
195
+ lines = code.split('\n')
196
+ for line in lines:
197
+ line = line.strip()
198
+ if line.startswith('class ') and '(Scene)' in line:
199
+ class_name = line.split('class ')[1].split('(')[0].strip()
200
+ return class_name
201
+ return None
202
+
203
+ def render_manim(code, quality, progress=gr.Progress()):
204
+ """Render Manim animation from code"""
205
+
206
+ if not code.strip():
207
+ return None, "❌ Error: Please enter some code!"
208
+
209
+ # Create temp directory
210
+ temp_dir = tempfile.mkdtemp(prefix="gradio_manim_")
211
+
212
+ try:
213
+ progress(0.1, desc="Saving code...")
214
+
215
+ # Save code to file
216
+ temp_file = os.path.join(temp_dir, "animation.py")
217
+ with open(temp_file, 'w', encoding='utf-8') as f:
218
+ f.write(code)
219
+
220
+ # Extract scene class
221
+ scene_class = extract_scene_class(code)
222
+ if not scene_class:
223
+ return None, "❌ Error: No Scene class found in code!\n\nMake sure your code has a class that inherits from Scene, like:\nclass MyAnimation(Scene):"
224
+
225
+ progress(0.2, desc=f"Found scene: {scene_class}")
226
+
227
+ # Prepare command
228
+ quality_map = {
229
+ "Low (Fast)": "l",
230
+ "Medium": "m",
231
+ "High": "h",
232
+ "Production": "p"
233
+ }
234
+ quality_flag = quality_map.get(quality, "l")
235
+
236
+ output_dir = os.path.join(temp_dir, "media")
237
+
238
+ # Use Python -m manim for cross-platform compatibility
239
+ import sys
240
+ cmd = [
241
+ sys.executable, "-m", "manim", "render",
242
+ temp_file, scene_class,
243
+ "-q", quality_flag,
244
+ "--media_dir", output_dir,
245
+ "--disable_caching"
246
+ ]
247
+
248
+ progress(0.3, desc="Starting render...")
249
+
250
+ print(f"Running command: {' '.join(cmd)}")
251
+
252
+ # Run manim
253
+ result = subprocess.run(
254
+ cmd,
255
+ capture_output=True,
256
+ text=True,
257
+ timeout=300, # 5 minute timeout
258
+ cwd=temp_dir # Set working directory
259
+ )
260
+
261
+ print(f"Cmd run complete. Return code: {result.returncode}")
262
+
263
+ progress(0.8, desc="Processing output...")
264
+
265
+ # Combine stdout and stderr for output
266
+ output_text = result.stdout + "\n" + result.stderr
267
+
268
+ if result.returncode != 0:
269
+ error_msg = f"❌ Rendering failed!\n\n{output_text}"
270
+ return None, error_msg
271
+
272
+ progress(0.9, desc="Finding video file...")
273
+
274
+ # Find the generated video
275
+ video_file = None
276
+ videos_dir = os.path.join(output_dir, "videos", "animation")
277
+
278
+ if os.path.exists(videos_dir):
279
+ for root, dirs, files in os.walk(videos_dir):
280
+ for file in files:
281
+ if file.endswith('.mp4'):
282
+ video_file = os.path.join(root, file)
283
+ break
284
+ if video_file:
285
+ break
286
+
287
+ if not video_file or not os.path.exists(video_file):
288
+ # Check for static image
289
+ images_dir = os.path.join(output_dir, "images", "animation")
290
+ if os.path.exists(images_dir):
291
+ for root, dirs, files in os.walk(images_dir):
292
+ for file in files:
293
+ if file.endswith(('.png', '.jpg')):
294
+ return None, f"⚠️ Generated static image instead of video.\n\nYour scene has no animations!\nAdd self.play(...) commands to create animated videos.\n\nOutput:\n{output_text}"
295
+
296
+ return None, f"❌ Could not find output file.\n\n{output_text}"
297
+
298
+ progress(1.0, desc="Done!")
299
+
300
+ # Success message
301
+ file_size = os.path.getsize(video_file) / (1024 * 1024) # MB
302
+ success_msg = f"✅ Animation rendered successfully!\n\n📊 Details:\n• Scene: {scene_class}\n• Quality: {quality}\n• File size: {file_size:.2f} MB"
303
+
304
+ return video_file, success_msg
305
+
306
+ except subprocess.TimeoutExpired:
307
+ return None, "❌ Error: Rendering took too long (>5 minutes). Try reducing animation length or quality."
308
+ except Exception as e:
309
+ return None, f"❌ Error: {str(e)}"
310
+ finally:
311
+ # Cleanup is handled by Gradio, but we keep temp dir for video access
312
+ pass
313
+
314
+ def load_example(example_name):
315
+ """Load example code"""
316
+ return EXAMPLE_CODES.get(example_name, DEFAULT_CODE)
317
+
318
+ def clear_all():
319
+ """Clear all fields"""
320
+ return "", None, "Ready to render!"
321
+
322
+ # Create Gradio interface
323
+ with gr.Blocks(title="Manim Animation Editor", theme=gr.themes.Soft()) as demo:
324
+ gr.Markdown("""
325
+ # 🎬 Manim Animation Editor
326
+
327
+ Create mathematical animations using Manim! Write your code, click render, and watch your animation come to life.
328
+
329
+ **📚 Quick Tips:**
330
+ - Use `self.play(...)` to create animations (required for videos!)
331
+ - Use `self.wait()` to pause
332
+ - Use `Text()` for formulas (works everywhere!)
333
+ - ⚠️ `MathTex()` requires LaTeX (may not work on hosted versions)
334
+ - Check the examples below to get started
335
+ """)
336
+
337
+ with gr.Row():
338
+ with gr.Column(scale=2):
339
+ # Code editor
340
+ code_input = gr.Code(
341
+ value=DEFAULT_CODE,
342
+ language="python",
343
+ label="📝 Manim Code",
344
+ lines=25
345
+ )
346
+
347
+ with gr.Row():
348
+ # Quality selector
349
+ quality_input = gr.Radio(
350
+ choices=["Low (Fast)", "Medium", "High", "Production"],
351
+ value="Low (Fast)",
352
+ label="🎥 Quality",
353
+ info="Higher quality = longer render time"
354
+ )
355
+
356
+ with gr.Row():
357
+ # Action buttons
358
+ render_btn = gr.Button("▶️ Render Animation", variant="primary", size="lg")
359
+ clear_btn = gr.Button("🗑️ Clear", size="lg")
360
+
361
+ # Examples
362
+ gr.Markdown("### 📚 Example Animations")
363
+ example_dropdown = gr.Dropdown(
364
+ choices=list(EXAMPLE_CODES.keys()),
365
+ label="Load Example",
366
+ value="Simple Animation"
367
+ )
368
+ load_example_btn = gr.Button("Load Example Code")
369
+
370
+ with gr.Column(scale=2):
371
+ # Video output
372
+ video_output = gr.Video(
373
+ label="🎬 Animation Output",
374
+ height=400
375
+ )
376
+
377
+ # Status/output text
378
+ output_text = gr.Textbox(
379
+ label="📋 Status & Console Output",
380
+ value="Ready to render! Write your code and click 'Render Animation'.",
381
+ lines=15,
382
+ max_lines=15
383
+ )
384
+
385
+ # Info section
386
+ with gr.Accordion("ℹ️ Help & Documentation", open=False):
387
+ gr.Markdown("""
388
+ ### Getting Started
389
+
390
+ **Basic Structure:**
391
+ ```python
392
+ from manim import *
393
+
394
+ class MyAnimation(Scene):
395
+ def construct(self):
396
+ # Create objects
397
+ text = Text("Hello!")
398
+
399
+ # Animate them
400
+ self.play(Write(text))
401
+ self.wait()
402
+ ```
403
+
404
+ **Common Commands:**
405
+ - `Write(obj)` - Write text or formula
406
+ - `Create(obj)` - Draw shapes
407
+ - `FadeIn(obj)` - Fade in object
408
+ - `Transform(obj1, obj2)` - Transform one object to another
409
+ - `obj.animate.move_to(...)` - Move object
410
+ - `self.wait(seconds)` - Pause for seconds
411
+
412
+ **Common Objects:**
413
+ - `Text("...")` - Text
414
+ - `Circle()` - Circle
415
+ - `Square()` - Square
416
+ - `Line(start, end)` - Line
417
+ - `Dot()` - Dot/Point
418
+
419
+ **Colors:**
420
+ - `RED`, `BLUE`, `GREEN`, `YELLOW`, `PURPLE`, `ORANGE`, `WHITE`, `BLACK`
421
+
422
+ **Positions:**
423
+ - `UP`, `DOWN`, `LEFT`, `RIGHT`
424
+ - `ORIGIN` - Center (0, 0, 0)
425
+
426
+ ### Requirements
427
+ - Manim Community must be installed: `pip install manim`
428
+ - For math formulas (MathTex), LaTeX must be installed
429
+
430
+ ### Troubleshooting
431
+ - **"No Scene class found"** - Make sure you have `class YourName(Scene):`
432
+ - **"Static image generated"** - Add `self.play(...)` commands for animations
433
+ - **LaTeX errors** - Use `Text()` instead of `MathTex()` or install LaTeX
434
+ """)
435
+
436
+ # Event handlers
437
+ render_btn.click(
438
+ fn=render_manim,
439
+ inputs=[code_input, quality_input],
440
+ outputs=[video_output, output_text]
441
+ )
442
+
443
+ load_example_btn.click(
444
+ fn=load_example,
445
+ inputs=[example_dropdown],
446
+ outputs=[code_input]
447
+ )
448
+
449
+ clear_btn.click(
450
+ fn=clear_all,
451
+ outputs=[code_input, video_output, output_text]
452
+ )
453
+
454
+ # Footer
455
+ gr.Markdown("""
456
+ ---
457
+ 💡 **Pro Tip:** Start with examples, then modify them to learn!
458
+
459
+ 📖 [Manim Documentation](https://docs.manim.community/) |
460
+ 🎓 [Manim Tutorial](https://docs.manim.community/en/stable/tutorials.html)
461
+ """)
462
+
463
+ # Launch the app
464
+ if __name__ == "__main__":
465
+ print("🚀 Starting Manim Animation Editor...")
466
+ print("📱 Access from mobile: Use the public URL (with share=True)")
467
+ print("💻 Local access: http://localhost:7860")
468
+
469
+ demo.launch(
470
+ # share=False, # Set to True for public URL
471
+ # debug=True
472
+ )