title: “Visualizing Geometric Intersections with Manim” format: revealjs editor: visual —
Introduction
This animation demonstrates how a straight line can intersect with a circle, resulting in: - Two points of intersection (secant line): The line cuts through the circle. - No intersection: The line misses the circle entirely. - Single point of intersection (tangent line): The line just touches the circle.
Mathematically, these intersection points are found by solving the simultaneous equations of the circle and the line: - Equation of the circle: \((x - h)^2 + (y - k)^2 = r^2\), where \((h, k)\) is the center and \(r\) the radius. - Equation of the line: \(y = mx + c\) (or any linear form).
Applications:
This concept is used in collision detection, where the intersection determines if an object (circle) is hit by a projectile (line).
1. Imports and Setup
from manim import *
import numpy as np
Explanation: - manim
: This is the animation engine. It provides all the geometric objects (like Circle
, Line
, Dot
), scene control (Scene
, Create
, Write
), and math tools. - numpy
: Used for mathematical calculations—square roots, vector norms, and arithmetic. Essential for computing intersections.
2. circle_line_intersection()
Function
def circle_line_intersection(circle: Circle, line: Line):
This function computes the points where a line intersects a circle using analytical geometry.
= line.copy().shift(-circle.get_arc_center()) cline
- Moves the line so the circle center is at the origin.
- Why? The math for intersecting a circle centered at (0, 0) is simpler.
= circle.get_arc_center()[:2] x0, y0
- Gets the original center of the circle, to translate back later.
= cline.get_start()[:2]
x1, y1 = cline.get_end()[:2]
x2, y2 = circle.width / 2 r
- Extracts the endpoints of the shifted line.
- Radius is calculated from width (
width = 2 × radius
).
= x2 - x1
dx = y2 - y1
dy = np.sqrt(dx**2 + dy**2)
dr = x1 * y2 - x2 * y1
D = r**2 * dr**2 - D**2 Delta
D
is a determinant related to area.Delta
is the discriminant:- Δ < 0: No intersection
- Δ = 0: Tangent
- Δ > 0: Secant (two points)
if Delta < 0:
return []
- No real solution → the line misses the circle.
= np.sqrt(Delta)
sqrt_Delta = np.sign(dy) if dy != 0 else 1 # avoid zero division
sign_dy = (D * dy + sign_dy * dx * sqrt_Delta) / (dr**2) + x0
sx1 = (-D * dx + abs(dy) * sqrt_Delta) / (dr**2) + y0
sy1 = (D * dy - sign_dy * dx * sqrt_Delta) / (dr**2) + x0
sx2 = (-D * dx - abs(dy) * sqrt_Delta) / (dr**2) + y0 sy2
- Uses analytical formulas to compute intersection point(s)—shifted back using
x0
,y0
.
if Delta == 0:
return [np.array([sx1, sy1, 0])]
else:
return [np.array([sx1, sy1, 0]), np.array([sx2, sy2, 0])]
- Only one intersection (tangent) if
Delta == 0
, otherwise two points (secant).
3. circle_circle_intersection()
Function
def circle_circle_intersection(circle1: Circle, circle2: Circle):
This function finds the intersection points between two circles using a geometric method.
= circle1.get_center()
c0 = circle2.get_center()
c1 = circle1.width / 2
r0 = circle2.width / 2 r1
- Extracts centers and radii of both circles.
= c1 - c0
v = np.linalg.norm(v) d
v
: Vector between centers.d
: Distance between centers.
if d > r0 + r1 or d == 0 or d < abs(r0 - r1):
return []
- If circles are too far apart, exactly concentric, or one is inside the other with no intersection → no intersection.
= v / d u
u
is the unit direction vector from center 1 to center 2.
= c0 + (d**2 - r1**2 + r0**2) * u / (2*d) xvec
- Midpoint between the two intersection points (the base point).
= np.array([u[1], -u[0], 0]) uperp
uperp
: A vector perpendicular tou
(used to offset to the left/right).
= np.sqrt(
a -d + r1 - r0) *
(-d - r1 + r0) *
(-d + r1 + r0) *
(+ r1 + r0)
(d / (2 * d) )
- Computes the distance from base to each intersection point using a Heron-like formula.
return [xvec + a * uperp, xvec - a * uperp]
- Returns the two actual points of intersection.
4. line_line_intersection()
Function
def line_line_intersection(line1: Line, line2: Line):
return line_intersection([line1.get_start(), line1.get_end()],
[line2.get_start(), line2.get_end()])
- Uses
manim.utils.geometry.line_intersection()
internally. - Assumes 2D line segments, returns a point of intersection.
5. IntersectionTest
Scene
This is the core animation class that uses all the geometry logic.
Title and Grid
= Text("Circle-Line Intersections Animation", color=BLUE, font_size=24)
title self.play(Write(title))
= NumberPlane().add_coordinates().set_opacity(0.2)
number_plane self.add(number_plane)
- Displays an animated title.
- Adds a coordinate grid for context.
Drawing Objects
= Circle(radius=3, color=RED).shift(2 * LEFT)
circ1 = Circle(radius=2, color=YELLOW).shift(DOWN) circ2
- Two circles with different positions and sizes.
= Line([-5, -2, 0], [6, 1, 0], color=BLUE)
line1 = Line([-1, 4, 0], [5, -2, 0], color=MAROON) line2
- Two diagonal lines intersecting the circles.
Intersections
Circle-Circle:
= circle_circle_intersection(circ1, circ2) Cs
- Calls the custom function to get intersection points.
= VGroup(
Cdots *[
=ORANGE), MathTex(f"C_{{{i+1}}}").next_to(p, UR, buff=0.05))
VGroup(Dot(p, colorfor i, p in enumerate(Cs)
] )
- Places a dot and label (C₁, C₂) at each intersection.
Circle-Line:
= circle_line_intersection(circ1, line1)
Ds = VGroup(
Ddots *[
=GREEN), MathTex(f"D_{{{i+1}}}").next_to(p, DOWN, buff=0.1))
VGroup(Dot(p, colorfor i, p in enumerate(Ds)
] )
- Intersects the line with the first circle, labels them D₁, D₂.
= circle_line_intersection(circ2, line1) Es
- Same for the second circle → E₁, E₂.
Line-Line:
= line_line_intersection(line1, line2)
F = VGroup(Dot(point=F, color=WHITE), MathTex("F").next_to(F, UP, buff=0.1)) Fdot
- Intersects the two lines, shows point F.
Text Annotations
= Text("Circle-Circle Intersection: C1, C2", font_size=14).shift(UP * 3 + RIGHT * 5)
text1 = Text("Circle-Line Intersection: D1, D2, E1, E2", font_size=14).next_to(text1, DOWN, buff=0.1)
text2 = Text("Line-Line Intersection: F", font_size=14).next_to(text2, DOWN, buff=0.1) text3
- Adds a legend/explanation on the top right of the screen.
Final Transition
self.play(FadeOut(*self.mobjects), run_time=4)
- Smoothly fades everything out to conclude the animation.
Summary of Key Concepts
Concept | Why It Matters |
---|---|
Coordinate Translation | Simplifies math, lets you assume origin at (0,0) |
Determinants & Δ | Help decide how/if objects intersect |
Scene Construction | Allows animation sequencing in Manim |
Labeling & Coloring | Clarifies intersection points and relationships |