Files
manim/hilbert.py
2015-11-25 15:28:07 -08:00

244 lines
7.0 KiB
Python

from mobject import Mobject, Mobject1D
from scene import Scene
from animation.transform import Transform
from animation.simple_animations import ShowCreation
from topics.geometry import Line, Point
from helpers import *
def flip(points, axis, angle = np.pi):
if axis is None:
return points
if isinstance(axis, tuple):
axes = axis
else:
axes = [axis]
for ax in axes:
matrix = rotation_matrix(angle, ax)
points = np.dot(points, np.transpose(matrix))
return points
class SpaceFillingCurve(Mobject1D):
DEFAULT_CONFIG = {
"radius" : 3,
"order" : 5,
"start_color" : RED,
"end_color" : GREEN,
}
def generate_points(self):
points = self.get_anchor_points()
for pair in zip(points, points[1:]):
self.add_line(*pair)
self.gradient_highlight(self.start_color, self.end_color)
def get_anchor_points(self):
raise Exception("Not implemented")
class SelfSimilarSpaceFillingCurve(SpaceFillingCurve):
DEFAULT_CONFIG = {
"axis_offset_pairs" : [],
"scale_factor" : 2,
"radius_scale_factor" : 0.5,
}
def refine_into_subparts(self, points):
transformed_copies = [
flip(points/self.scale_factor, axis) + \
offset*self.radius*self.radius_scale_factor
for axis, offset in self.axis_offset_pairs
]
return reduce(
lambda a, b : np.append(a, b, axis = 0),
transformed_copies
)
def get_anchor_points(self):
points = np.zeros((1, 3))
for count in range(self.order):
points = self.refine_into_subparts(
points
)
return points
class HilbertCurve(SelfSimilarSpaceFillingCurve):
DEFAULT_CONFIG = {
"axis_offset_pairs" : [
(RIGHT+UP, LEFT+DOWN ),
(None, LEFT+UP ),
(None, RIGHT+UP ),
(RIGHT+DOWN, RIGHT+DOWN),
],
}
class HilbertCurve3D(SelfSimilarSpaceFillingCurve):
DEFAULT_CONFIG = {
"axis_offset_pairs" : [ #TODO
(None, LEFT+DOWN+OUT),
(None, LEFT+UP+OUT),
(None, LEFT+UP+IN),
(None, LEFT+DOWN+IN),
(None, RIGHT+DOWN+IN),
(None, RIGHT+UP+IN),
(None, RIGHT+UP+OUT),
(None, RIGHT+DOWN+OUT),
],
}
class PeanoCurve(SelfSimilarSpaceFillingCurve):
DEFAULT_CONFIG = {
"start_color" : PURPLE,
"end_color" : TEAL,
"axis_offset_pairs" : [
(None, LEFT+DOWN ),
(UP, LEFT ),
(None, LEFT+UP ),
(RIGHT, UP ),
(LEFT+UP, ORIGIN ),
(RIGHT, DOWN ),
(None, RIGHT+DOWN),
(UP, RIGHT ),
(None, RIGHT+UP ),
],
"scale_factor" : 3,
"radius_scale_factor" : 2.0/3,
}
class TriangleFillingCurve(SelfSimilarSpaceFillingCurve):
DEFAULT_CONFIG = {
"start_color" : MAROON,
"end_color" : YELLOW,
"axis_offset_pairs" : [
(None, LEFT/4.+DOWN/6.),
(RIGHT, ORIGIN),
(None, RIGHT/4.+DOWN/6.),
(UP, UP/3.),
],
"scale_factor" : 2,
"radius_scale_factor" : 1.5,
}
class HexagonFillingCurve(SelfSimilarSpaceFillingCurve):
DEFAULT_CONFIG = {
"start_color" : WHITE,
"end_color" : BLUE_D,
"axis_offset_pairs" : [
(None, 1.5*DOWN + 0.5*np.sqrt(3)*LEFT),
(UP+np.sqrt(3)*RIGHT, 1.5*DOWN + 0.5*np.sqrt(3)*RIGHT),
(np.sqrt(3)*UP+RIGHT, ORIGIN),
((UP, RIGHT), np.sqrt(3)*LEFT),
(None, 1.5*UP + 0.5*np.sqrt(3)*LEFT),
(None, 1.5*UP + 0.5*np.sqrt(3)*RIGHT),
(RIGHT, np.sqrt(3)*RIGHT),
],
"scale_factor" : 3,
"radius_scale_factor" : 2/(3*np.sqrt(3)),
}
def refine_into_subparts(self, points):
return SelfSimilarSpaceFillingCurve.refine_into_subparts(
self,
flip(points, IN, np.pi/6)
)
class UtahFillingCurve(SelfSimilarSpaceFillingCurve):
DEFAULT_CONFIG = {
"start_color" : WHITE,
"end_color" : BLUE_D,
"axis_offset_pairs" : [
(None, 1.5*DOWN + 0.5*np.sqrt(3)*LEFT),
(UP+np.sqrt(3)*RIGHT, 1.5*DOWN + 0.5*np.sqrt(3)*RIGHT),
(np.sqrt(3)*UP+RIGHT, ORIGIN),
((UP, RIGHT), np.sqrt(3)*LEFT),
(None, 1.5*UP + 0.5*np.sqrt(3)*LEFT),
(None, 1.5*UP + 0.5*np.sqrt(3)*RIGHT),
(RIGHT, np.sqrt(3)*RIGHT),
],
"scale_factor" : 3,
"radius_scale_factor" : 2/(3*np.sqrt(3)),
}
class SnakeCurve(SpaceFillingCurve):
DEFAULT_CONFIG = {
"start_color" : BLUE,
"end_color" : YELLOW,
}
def get_anchor_points(self, order):
result = []
lower_left = ORIGIN + \
LEFT*self.radius + \
DOWN*self.radius
step = 2.0*self.radius / (order)
for y in range(order+1):
x_range = range(order+1)
if y%2 == 0:
x_range.reverse()
for x in x_range:
result.append(
lower_left + x*step*RIGHT + y*step*UP
)
return result
class SpaceFillingCurveScene(Scene):
@staticmethod
def args_to_string(CurveClass, order):
return CurveClass.__name__ + "Order" + str(order)
@staticmethod
def string_to_args(arg_str):
curve_class_name, order_str = arg_str.split()
space_filling_curves = dict([
(Class.__name__, Class)
for Class in get_all_descendent_classes(SpaceFillingCurve)
])
if curve_class_name not in space_filling_curves:
raise Exception(
"%s is not a space filling curve"%curve_class_name
)
CurveClass = space_filling_curves[curve_class_name]
return CurveClass, int(order_str)
class TransformOverIncreasingOrders(SpaceFillingCurveScene):
def construct(self, CurveClass, max_order):
sample = CurveClass(order = 1)
curve = Line(sample.radius*LEFT, sample.radius*RIGHT)
curve.gradient_highlight(
sample.start_color,
sample.end_color
)
for order in range(1, max_order):
new_curve = CurveClass(order = order)
self.play(
Transform(curve, new_curve),
run_time = 3/np.sqrt(order),
)
self.dither()
class DrawSpaceFillingCurve(SpaceFillingCurveScene):
def construct(self, CurveClass, order):
curve = CurveClass(order = order)
self.play(ShowCreation(curve), run_time = 10)
self.dither()