Supabase MCP Server
- src
- video_editor_mcp
from manim import *
import numpy as np
import sys
import json
import os
from contextlib import redirect_stdout, redirect_stderr
class LineGraphAnimation(Scene):
def __init__(
self,
x_values=None,
y_values=None,
x_label="X-Axis",
y_label="Y-Axis",
title="Graph",
):
super().__init__()
# Default values if none provided
self.x_values = x_values if x_values is not None else list(range(6))
self.y_values = y_values if y_values is not None else [1, 4, 2, 8, 5, 7]
self.x_label = x_label
self.y_label = y_label
self.title = title
# Validate input
if len(self.x_values) != len(self.y_values):
raise ValueError("X and Y value lists must be the same length")
def create_axes(self):
# Calculate appropriate ranges for y-axis
y_min, y_max = min(self.y_values), max(self.y_values)
y_padding = (y_max - y_min) * 0.1 # 10% padding
# Store original x values and create numerical mapping if needed
self.original_x_values = self.x_values
if not all(isinstance(x, (int, float)) for x in self.x_values):
self.x_values = list(range(len(self.x_values)))
x_min, x_max = min(self.x_values), max(self.x_values)
x_padding = (x_max - x_min) * 0.1 if isinstance(x_min, (int, float)) else 0.5
# Adjust ranges to start at 0 for x-axis
x_range = [0, x_max + x_padding, (x_max - x_min) / 10 if x_max != x_min else 1]
y_range = [0, y_max + y_padding, (y_max - y_min) / 10 if y_max != y_min else 1]
axes = Axes(
x_range=x_range,
y_range=y_range,
axis_config={
"include_tip": True,
"tick_size": 0.1,
"color": self.black,
},
x_axis_config={
"include_numbers": isinstance(x_min, (int, float)),
"decimal_number_config": {"num_decimal_places": 1, "color": self.black},
},
y_axis_config={
"include_numbers": True,
"decimal_number_config": {"num_decimal_places": 1, "color": self.black},
},
tips=True,
x_length=10,
y_length=6,
)
# Ensure axes start at origin
axes.move_to(ORIGIN)
axes.to_corner(DOWN + LEFT)
return axes
def create_labels(self, axes):
# Create axis labels
x_label = Text(self.x_label, font_size=24).next_to(axes.x_axis, DOWN)
y_label = Text(self.y_label, font_size=24).next_to(axes.y_axis, LEFT)
# Create title
title = Text(self.title, font_size=36).to_edge(UP, buff=-0.25)
return x_label, y_label, title
def create_data_points(self, axes):
# Convert data points to coordinates
points = [
axes.coords_to_point(x, y) for x, y in zip(self.x_values, self.y_values)
]
# Create the graph line
graph = VMobject()
graph.set_points_smoothly([*points])
graph.set_color("#87c2a5")
# Create dots for each point
dots = VGroup(*[Dot(point, color="#525893") for point in points])
# Create labels for data points
value_labels = VGroup(
*[
Text(f"({x}, {y})", font_size=16, color=self.black).next_to(dot, UP)
for x, y, dot in zip(self.x_values, self.y_values, dots)
]
)
return graph, dots, value_labels
def construct(self):
# Create the elements
self.camera.frame_height = 10
self.camera.frame_width = 17
self.camera.background_color = "#ece6e2"
self.camera.frame_center = [-1.3, 0, 0]
self.black = "#343434"
Text.set_default(font="Helvetica", color=self.black)
axes = self.create_axes()
x_label, y_label, title = self.create_labels(axes)
graph, dots, value_labels = self.create_data_points(axes)
# Initial animations
self.play(Write(title))
self.play(Create(axes), Write(x_label), Write(y_label))
# Graph creation animation
self.play(Create(graph), run_time=2)
# Animate dots appearing
self.play(Create(dots))
# Animate value labels
self.play(Write(value_labels))
# Sequential point highlighting
for dot, label in zip(dots, value_labels):
self.play(
dot.animate.scale(1.5).set_color("#e07a5f"),
label.animate.scale(1.2),
rate_func=there_and_back,
run_time=0.5,
)
# Final pause
self.wait(2)
class BarChartAnimation(Scene):
def __init__(
self,
x_values=None,
y_values=None,
x_label="Categories",
y_label="Values",
title="Bar Chart",
):
super().__init__()
self.x_values = x_values if x_values is not None else ["A", "B", "C", "D", "E"]
self.y_values = y_values if y_values is not None else [4, 8, 2, 6, 5]
self.x_label = x_label
self.y_label = y_label
self.title = title
self.bar_color = BLUE
self.bar_width = 0.5
def construct(self):
# Calculate ranges
self.camera.frame_height = 10
self.camera.frame_width = 17
self.camera.background_color = "#ece6e2"
self.camera.frame_center = [-1.3, 0, 0]
self.black = "#343434"
y_max = max(self.y_values)
y_padding = y_max * 0.2
Text.set_default(font="Helvetica", color=self.black)
# Create axes with adjusted ranges and position
axes = Axes(
x_range=[0, len(self.x_values), 1], # Start from 0
y_range=[0, y_max + y_padding, y_max / 5],
axis_config={
"include_tip": True,
"tip_width": 0.2,
"tip_height": 0.2,
"color": BLACK,
},
x_length=8,
y_length=6,
).to_corner(DL, buff=1) # Align to bottom left with padding
# Shift the entire axes right to create space after y-axis
axes.shift(RIGHT * 1)
# Create bars using axes coordinates
bars = VGroup()
labels = VGroup()
for i, value in enumerate(self.y_values):
# Calculate bar position and height
bar_bottom = axes.c2p(i + 0.5, 0) # Add 0.5 to center on tick marks
bar_top = axes.c2p(i + 0.5, value)
bar_height = bar_top[1] - bar_bottom[1]
bar = Rectangle(
width=self.bar_width,
height=bar_height,
color=self.bar_color,
fill_opacity=0.8,
).move_to(bar_bottom, aligned_edge=DOWN)
# Create value label
label = Text(f"{value}", font_size=24)
label.next_to(bar, UP, buff=0.1)
bars.add(bar)
labels.add(label)
# Create axis labels
x_labels = VGroup()
for i, label_text in enumerate(self.x_values):
label = Text(label_text, font_size=24)
label.next_to(
axes.c2p(i + 0.5, 0), DOWN, buff=0.5
) # Add 0.5 to align with bars
x_labels.add(label)
y_label = Text(self.y_label, font_size=24).next_to(axes.y_axis, LEFT, buff=0.5)
x_axis_label = Text(self.x_label, font_size=24).next_to(
axes.x_axis, DOWN, buff=1.5
)
title = Text(self.title, font_size=36).to_edge(UP, buff=0.5)
# Animations
self.play(Create(axes))
self.play(Write(title))
self.play(Write(VGroup(y_label, x_axis_label)))
self.play(Write(x_labels))
# Animate each bar appearing
for bar, label in zip(bars, labels):
self.play(GrowFromEdge(bar, DOWN), Write(label), run_time=0.5)
# Highlight bars
for bar, label in zip(bars, labels):
self.play(
bar.animate.set_color(RED),
label.animate.scale(1.2),
rate_func=there_and_back,
run_time=0.3,
)
self.wait()
def render_bar_chart(
x_values, y_values, x_label, y_label, title, filename="bar_chart.mp4"
):
config.verbosity = "ERROR"
config.pixel_height = 720
config.pixel_width = 1280
config.frame_height = 8
config.frame_width = 14
config.output_file = filename # Optional: specify output filename
config.preview = True # Opens the video after rendering
config.quality = "medium_quality" # or "high_quality", "production_quality"
scene = BarChartAnimation(
x_values=x_values,
y_values=y_values,
x_label=x_label,
y_label=y_label,
title=title,
)
scene.render()
return
# Example usage
if __name__ == "__main__":
# Sample data
input_json_file = sys.argv[1]
chart_type = sys.argv[2]
with open(input_json_file, "r", encoding="utf-8") as f:
data = json.load(f)
if "x_values" not in data or "y_values" not in data:
raise ValueError("Invalid JSON data format")
x_values = data["x_values"]
y_values = data["y_values"]
x_label = data["x_label"] or "Categories"
y_label = data["y_label"] or "Values"
title = data["title"]
filename = data["filename"]
config.verbosity = "ERROR"
config.pixel_height = 720
config.pixel_width = 1280
config.frame_height = 8
config.frame_width = 14
config.output_file = filename
config.preview = True
config.quality = "medium_quality"
# Configure scene settings
if chart_type == "bar":
scene = BarChartAnimation(
x_values=x_values,
y_values=y_values,
x_label=x_label,
y_label=y_label,
title=title,
)
elif chart_type == "line":
scene = LineGraphAnimation(
x_values=x_values,
y_values=y_values,
x_label=x_label,
y_label=y_label,
title=title,
)
else:
raise ValueError("Invalid chart type. Use 'bar' or 'line'")
scene.render()