Animation engine for explanatory math videos with programmatic mathematical visualization capabilities
—
ManimGL provides precise geometric boolean operations for 2D vectorized objects through integration with the Skia-Path Ops library. These operations enable complex shape construction, geometric analysis, and sophisticated mathematical visualizations by combining, subtracting, and analyzing the relationships between shapes.
Combine multiple shapes into a single unified shape containing all areas from the input objects.
class Union(VMobject):
def __init__(self, *vmobjects: VMobject, **kwargs):
"""
Create the union (combined area) of two or more VMobjects.
Parameters:
- vmobjects: Variable number of VMobject instances (minimum 2 required)
- kwargs: Additional keyword arguments passed to VMobject constructor
Raises:
- ValueError: If fewer than 2 mobjects are provided
"""Subtract one shape from another, creating the remaining area after removal.
class Difference(VMobject):
def __init__(self, subject: VMobject, clip: VMobject, **kwargs):
"""
Create the difference between two VMobjects (subject minus clip).
Parameters:
- subject: The VMobject to subtract from
- clip: The VMobject to subtract
- kwargs: Additional keyword arguments passed to VMobject constructor
"""Find the overlapping area where two or more shapes intersect.
class Intersection(VMobject):
def __init__(self, *vmobjects: VMobject, **kwargs):
"""
Create the intersection (overlapping area) of two or more VMobjects.
Parameters:
- vmobjects: Variable number of VMobject instances (minimum 2 required)
- kwargs: Additional keyword arguments passed to VMobject constructor
Raises:
- ValueError: If fewer than 2 mobjects are provided
"""Create the exclusive or (XOR) of shapes, showing areas that belong to exactly one of the input objects.
class Exclusion(VMobject):
def __init__(self, *vmobjects: VMobject, **kwargs):
"""
Create the exclusive or (XOR) of two or more VMobjects.
Parameters:
- vmobjects: Variable number of VMobject instances (minimum 2 required)
- kwargs: Additional keyword arguments passed to VMobject constructor
Raises:
- ValueError: If fewer than 2 mobjects are provided
"""Low-level functions for converting between ManimGL objects and Skia paths for boolean operations.
def _convert_vmobject_to_skia_path(vmobject: VMobject) -> pathops.Path:
"""
Convert a ManimGL VMobject to a Skia path for boolean operations.
Parameters:
- vmobject: VMobject to convert
Returns:
pathops.Path object for use in boolean operations
"""
def _convert_skia_path_to_vmobject(path: pathops.Path, vmobject: VMobject) -> VMobject:
"""
Convert a Skia path back to a ManimGL VMobject.
Parameters:
- path: pathops.Path object from boolean operation
- vmobject: Template VMobject for style and properties
Returns:
VMobject with boolean operation result
"""from manimlib import *
class BooleanBasics(Scene):
def construct(self):
# Create base shapes
circle = Circle(radius=1.5, color=BLUE, fill_opacity=0.5)
square = Square(side_length=2.5, color=RED, fill_opacity=0.5)
# Position shapes for demonstration
circle.shift(LEFT * 0.5)
square.shift(RIGHT * 0.5)
# Show original shapes
self.play(ShowCreation(circle), ShowCreation(square))
self.wait()
# Union - combine both shapes
union_result = Union(circle, square)
union_result.set_fill(GREEN, opacity=0.7)
self.play(
Transform(VGroup(circle, square), union_result),
run_time=2
)
self.wait()
# Reset for next operation
circle = Circle(radius=1.5, color=BLUE, fill_opacity=0.5).shift(LEFT * 0.5)
square = Square(side_length=2.5, color=RED, fill_opacity=0.5).shift(RIGHT * 0.5)
# Intersection - only overlapping area
intersection_result = Intersection(circle, square)
intersection_result.set_fill(PURPLE, opacity=0.8)
self.play(Transform(union_result, VGroup(circle, square)))
self.wait()
self.play(Transform(VGroup(circle, square), intersection_result))
self.wait()class ComplexShapes(Scene):
def construct(self):
# Create gear shape using boolean operations
base_circle = Circle(radius=2, fill_opacity=1, color=GREY)
inner_circle = Circle(radius=0.8, fill_opacity=1, color=BLACK)
# Create gear teeth
num_teeth = 12
teeth = VGroup()
for i in range(num_teeth):
angle = i * TAU / num_teeth
tooth = Rectangle(
width=0.4, height=0.6,
fill_opacity=1, color=GREY
)
tooth.move_to(2.3 * np.array([np.cos(angle), np.sin(angle), 0]))
tooth.rotate(angle)
teeth.add(tooth)
# Combine all teeth with base circle
gear_outline = Union(base_circle, *teeth)
# Subtract inner circle to create hole
gear = Difference(gear_outline, inner_circle)
gear.set_fill(YELLOW, opacity=0.8)
gear.set_stroke(YELLOW_D, width=2)
self.play(ShowCreation(gear), run_time=3)
# Add rotation animation
self.play(Rotate(gear, TAU, run_time=4, rate_func=linear))class SetOperations(Scene):
def construct(self):
# Create Venn diagram
set_a = Circle(radius=1.2, color=BLUE, fill_opacity=0.3)
set_b = Circle(radius=1.2, color=RED, fill_opacity=0.3)
set_a.shift(LEFT * 0.8)
set_b.shift(RIGHT * 0.8)
# Labels
label_a = Text("A", font_size=48).move_to(set_a.get_center() + LEFT * 0.5)
label_b = Text("B", font_size=48).move_to(set_b.get_center() + RIGHT * 0.5)
# Show base sets
self.add(set_a, set_b, label_a, label_b)
self.wait()
# Demonstrate each operation
operations = [
("A ∪ B (Union)", Union(set_a, set_b), YELLOW),
("A ∩ B (Intersection)", Intersection(set_a, set_b), GREEN),
("A - B (Difference)", Difference(set_a, set_b), ORANGE),
("A ⊕ B (XOR)", Exclusion(set_a, set_b), PURPLE)
]
for title, operation, color in operations:
result = operation.copy()
result.set_fill(color, opacity=0.8)
result.set_stroke(color, width=3)
title_text = Text(title, font_size=36).to_edge(UP)
self.play(
Write(title_text),
ShowCreation(result),
run_time=2
)
self.wait(2)
self.play(
FadeOut(title_text),
FadeOut(result)
)class ShapeAnalysis(Scene):
def construct(self):
# Create overlapping shapes for analysis
triangle = RegularPolygon(3, radius=1.5, color=BLUE, fill_opacity=0.4)
hexagon = RegularPolygon(6, radius=1.8, color=RED, fill_opacity=0.4)
circle = Circle(radius=1.2, color=GREEN, fill_opacity=0.4)
triangle.shift(UP * 0.5)
hexagon.shift(DOWN * 0.3 + LEFT * 0.4)
circle.shift(DOWN * 0.3 + RIGHT * 0.4)
shapes = VGroup(triangle, hexagon, circle)
self.play(ShowCreation(shapes), run_time=2)
self.wait()
# Show different combinations
combinations = [
# All three shapes union
("All Combined", Union(triangle, hexagon, circle), YELLOW),
# Pairwise intersections
("Triangle ∩ Hexagon", Intersection(triangle, hexagon), ORANGE),
("Hexagon ∩ Circle", Intersection(hexagon, circle), PURPLE),
("Triangle ∩ Circle", Intersection(triangle, circle), PINK),
# Triple intersection
("All Intersect", Intersection(triangle, hexagon, circle), WHITE)
]
for title, result, color in combinations:
analysis = result.copy()
analysis.set_fill(color, opacity=0.9)
analysis.set_stroke(color, width=4)
title_text = Text(title, font_size=32).to_edge(UP)
self.play(Write(title_text))
self.play(
shapes.animate.set_fill_opacity(0.1),
ShowCreation(analysis),
run_time=2
)
self.wait(2)
self.play(
FadeOut(title_text),
FadeOut(analysis),
shapes.animate.set_fill_opacity(0.4)
)
self.wait()class DynamicBoolean(Scene):
def construct(self):
# Create movable shapes
circle = Circle(radius=1, color=BLUE, fill_opacity=0.6)
square = Square(side_length=1.8, color=RED, fill_opacity=0.6)
circle.to_edge(LEFT)
square.to_edge(RIGHT)
# Create result placeholder
result_union = Union(circle, square)
result_union.set_fill(YELLOW, opacity=0.8)
result_union.move_to(ORIGIN)
self.add(circle, square, result_union)
# Animate shapes moving and show dynamic boolean result
def update_union(mob):
new_union = Union(circle, square)
new_union.set_fill(YELLOW, opacity=0.8)
new_union.move_to(ORIGIN)
mob.become(new_union)
result_union.add_updater(update_union)
# Move shapes to show dynamic updating
self.play(
circle.animate.shift(RIGHT * 2),
square.animate.shift(LEFT * 2),
run_time=4
)
self.play(
circle.animate.shift(RIGHT * 1),
square.animate.shift(LEFT * 1),
run_time=3
)
result_union.clear_updaters()
self.wait()Boolean operations work by converting ManimGL VMobjects to Skia paths, performing the geometric operations, and converting back:
All boolean operation results are fully-featured VMobjects supporting:
Boolean operations are computationally intensive for complex shapes. For real-time or highly animated scenes, consider:
The boolean operations module provides powerful tools for geometric construction and mathematical visualization, enabling sophisticated shape analysis and complex geometric demonstrations in ManimGL.
Install with Tessl CLI
npx tessl i tessl/pypi-manimgldocs