3b1b-manim/topics/geometry.py
2016-04-19 00:20:19 -07:00

243 lines
6.6 KiB
Python

from helpers import *
from mobject import Mobject
from mobject.vectorized_mobject import VMobject
class Arc(VMobject):
CONFIG = {
"radius" : 1.0,
"start_angle" : 0,
"num_anchors" : 8,
"anchors_span_full_range" : True
}
def __init__(self, angle, **kwargs):
digest_locals(self)
VMobject.__init__(self, **kwargs)
def generate_points(self):
self.set_anchor_points(
self.get_unscaled_anchor_points(),
mode = "smooth"
)
self.scale(self.radius)
def get_unscaled_anchor_points(self):
step = self.angle/self.num_anchors
end_angle = self.start_angle + self.angle
if self.anchors_span_full_range:
end_angle += step
return [
np.cos(a)*RIGHT+np.sin(a)*UP
for a in np.arange(
self.start_angle, end_angle, step
)
]
class Circle(Arc):
CONFIG = {
"color" : RED,
"close_new_points" : True,
"anchors_span_full_range" : False
}
def __init__(self, **kwargs):
Arc.__init__(self, 2*np.pi, **kwargs)
class Dot(Circle): #Use 1D density, even though 2D
CONFIG = {
"radius" : 0.05,
"stroke_width" : 0,
"fill_color" : WHITE,
"fill_opacity" : 1.0
}
def __init__(self, point = ORIGIN, **kwargs):
Circle.__init__(self, **kwargs)
self.shift(point)
self.init_colors()
class Line(VMobject):
CONFIG = {
"buff" : 0,
}
def __init__(self, start, end, **kwargs):
digest_config(self, kwargs)
self.set_start_and_end(start, end)
VMobject.__init__(self, **kwargs)
def set_start_and_end(self, start, end):
start_to_end = self.pointify(end) - self.pointify(start)
vect = np.zeros(len(start_to_end))
longer_dim = np.argmax(map(abs, start_to_end))
vect[longer_dim] = start_to_end[longer_dim]
self.start, self.end = [
arg.get_edge_center(unit*vect)
if isinstance(arg, Mobject)
else np.array(arg)
for arg, unit in zip([start, end], [1, -1])
]
start_to_end = self.end - self.start
length = np.linalg.norm(start_to_end)
if length > 2*self.buff:
start_to_end /= np.linalg.norm(start_to_end)
self.start = self.start + self.buff*start_to_end
self.end = self.end - self.buff*start_to_end
def pointify(self, mob_or_point):
if isinstance(mob_or_point, Mobject):
return mob_or_point.get_center()
else:
return np.array(mob_or_point)
def generate_points(self):
self.set_points_as_corners([self.start, self.end])
def get_length(self):
return np.linalg.norm(self.start - self.end)
def get_start_and_end(self):
return self.points[0], self.points[-1]
def get_slope(self):
start, end = self.get_start_and_end()
rise, run = [
float(end[i] - start[i])
for i in [1, 0]
]
return np.inf if run == 0 else rise/run
def get_angle(self):
start, end = self.get_start_and_end()
return angle_of_vector(end-start)
class Arrow(Line):
CONFIG = {
"color" : YELLOW_C,
"tip_length" : 0.25,
"buff" : 0.3,
}
def __init__(self, *args, **kwargs):
if len(args) == 1:
point = self.pointify(args[0])
args = (point+UP+LEFT, target)
Line.__init__(self, *args, **kwargs)
self.add_tip()
def add_tip(self):
vect = self.start-self.end
length = np.linalg.norm(vect)
vect *= self.tip_length/length
tip_points = [
self.end+rotate_vector(vect, u*np.pi/5)
for u in 1, -1
]
self.tip = VMobject(close_new_points = False)
self.tip.set_anchor_points(
[tip_points[0], self.end, tip_points[1]],
mode = "corners"
)
self.add(self.tip)
self.init_colors()
class Vector(Arrow):
CONFIG = {
"color" : WHITE,
"buff" : 0,
}
def __init__(self, start, direction, **kwargs):
Arrow.__init__(self, start, end, **kwargs)
class Cross(VMobject):
CONFIG = {
"color" : YELLOW,
"radius" : 0.3
}
def generate_points(self):
p1, p2, p3, p4 = self.radius * np.array([
UP+LEFT,
DOWN+RIGHT,
UP+RIGHT,
DOWN+LEFT,
])
self.add(Line(p1, p2), Line(p3, p4))
self.init_colors()
class CubicBezier(VMobject):
def __init__(self, points, **kwargs):
VMobject.__init__(self, **kwargs)
self.set_points(points)
class Polygon(VMobject):
CONFIG = {
"color" : GREEN_D,
"mark_paths_closed" : True,
"close_new_points" : True,
}
def __init__(self, *vertices, **kwargs):
assert len(vertices) > 1
digest_locals(self)
VMobject.__init__(self, **kwargs)
def generate_points(self):
self.set_anchor_points(self.vertices, mode = "corners")
def get_vertices(self):
return self.get_anchors_and_handles()[0]
class Rectangle(VMobject):
CONFIG = {
"color" : YELLOW,
"height" : 2.0,
"width" : 4.0,
"mark_paths_closed" : True,
"close_new_points" : True,
}
def generate_points(self):
y, x = self.height/2, self.width/2
self.set_anchor_points([
x*LEFT+y*UP,
x*RIGHT+y*UP,
x*RIGHT+y*DOWN,
x*LEFT+y*DOWN
], mode = "corners")
class Square(Rectangle):
CONFIG = {
"side_length" : 2.0,
}
def __init__(self, **kwargs):
digest_config(self, kwargs)
Rectangle.__init__(
self,
height = self.side_length,
width = self.side_length,
**kwargs
)
class Grid(VMobject):
CONFIG = {
"height" : 6.0,
"width" : 6.0,
}
def __init__(self, rows, columns, **kwargs):
digest_config(self, kwargs, locals())
VMobject.__init__(self, **kwargs)
def generate_points(self):
x_step = self.width / self.columns
y_step = self.height / self.rows
for x in np.arange(0, self.width+x_step, x_step):
self.add(Line(
[x-self.width/2., -self.height/2., 0],
[x-self.width/2., self.height/2., 0],
))
for y in np.arange(0, self.height+y_step, y_step):
self.add(Line(
[-self.width/2., y-self.height/2., 0],
[self.width/2., y-self.height/2., 0]
))