mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 14:03:59 +08:00
Prepping for hilbert curve project
This commit is contained in:
@ -7,8 +7,7 @@ import warnings
|
|||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
from animation import Animation
|
from animation import Animation
|
||||||
from mobject import Mobject
|
from mobject import Mobject, Point
|
||||||
from topics.geometry import Point
|
|
||||||
|
|
||||||
class Transform(Animation):
|
class Transform(Animation):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
|
@ -45,7 +45,7 @@ def get_configuration(sys_argv):
|
|||||||
print str(err)
|
print str(err)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
config = {
|
config = {
|
||||||
"module" : None,
|
"file" : None,
|
||||||
"scene_name" : "",
|
"scene_name" : "",
|
||||||
"args_extension" : "",
|
"args_extension" : "",
|
||||||
"display_config" : PRODUCTION_QUALITY_DISPLAY_CONFIG,
|
"display_config" : PRODUCTION_QUALITY_DISPLAY_CONFIG,
|
||||||
@ -83,7 +83,7 @@ def get_configuration(sys_argv):
|
|||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
print HELP_MESSAGE
|
print HELP_MESSAGE
|
||||||
sys.exit()
|
sys.exit()
|
||||||
config["module"] = args[0].replace(".py", "")
|
config["file"] = args[0]
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
config["scene_name"] = args[1]
|
config["scene_name"] = args[1]
|
||||||
if len(args) > 2:
|
if len(args) > 2:
|
||||||
@ -180,17 +180,22 @@ def get_scene_classes(scene_names_to_classes, config):
|
|||||||
return scene_names_to_classes.values()
|
return scene_names_to_classes.values()
|
||||||
return prompt_user_for_choice(scene_names_to_classes)
|
return prompt_user_for_choice(scene_names_to_classes)
|
||||||
|
|
||||||
|
def get_module(file_name):
|
||||||
|
module_name = file_name.replace(".py", "")
|
||||||
|
last_module = imp.load_module(".", *imp.find_module("."))
|
||||||
|
for part in module_name.split(os.sep):
|
||||||
|
load_args = imp.find_module(part, last_module.__path__)
|
||||||
|
last_module = imp.load_module(part, *load_args)
|
||||||
|
return last_module
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
config = get_configuration(sys.argv)
|
config = get_configuration(sys.argv)
|
||||||
module = imp.load_module(
|
module = get_module(config["file"])
|
||||||
config["module"],
|
|
||||||
*imp.find_module(config["module"])
|
|
||||||
)
|
|
||||||
scene_names_to_classes = dict(
|
scene_names_to_classes = dict(
|
||||||
inspect.getmembers(module, is_scene)
|
inspect.getmembers(module, is_scene)
|
||||||
)
|
)
|
||||||
config["movie_prefix"] = config["module"]
|
config["movie_prefix"] = config["file"].replace(".py", "")
|
||||||
scene_kwargs = config["display_config"]
|
scene_kwargs = config["display_config"]
|
||||||
scene_kwargs["announce_construction"] = True
|
scene_kwargs["announce_construction"] = True
|
||||||
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
||||||
|
@ -214,7 +214,8 @@ def streth_array_to_length(nparray, length):
|
|||||||
curr_len = len(nparray)
|
curr_len = len(nparray)
|
||||||
if curr_len > length:
|
if curr_len > length:
|
||||||
raise Warning("Trying to stretch array to a length shorter than its own")
|
raise Warning("Trying to stretch array to a length shorter than its own")
|
||||||
indices = np.arange(length)/ (float(length)/curr_len)
|
indices = np.arange(length)/ float(length)
|
||||||
|
indices *= curr_len
|
||||||
return nparray[indices.astype('int')]
|
return nparray[indices.astype('int')]
|
||||||
|
|
||||||
def make_even(iterable_1, iterable_2):
|
def make_even(iterable_1, iterable_2):
|
||||||
|
338
hilbert.py
338
hilbert.py
@ -1,338 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
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 rotate(points, angle = np.pi, axis = OUT):
|
|
||||||
if axis is None:
|
|
||||||
return points
|
|
||||||
matrix = rotation_matrix(angle, axis)
|
|
||||||
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 LindenmayerCurve(SpaceFillingCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"axiom" : "A",
|
|
||||||
"rule" : {},
|
|
||||||
"scale_factor" : 2,
|
|
||||||
"radius" : 3,
|
|
||||||
"start_step" : RIGHT,
|
|
||||||
"angle" : np.pi/2,
|
|
||||||
}
|
|
||||||
|
|
||||||
def expand_command_string(self, command):
|
|
||||||
result = ""
|
|
||||||
for letter in command:
|
|
||||||
if letter in self.rule:
|
|
||||||
result += self.rule[letter]
|
|
||||||
else:
|
|
||||||
result += letter
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_command_string(self):
|
|
||||||
result = self.axiom
|
|
||||||
for x in range(self.order):
|
|
||||||
result = self.expand_command_string(result)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_anchor_points(self):
|
|
||||||
step = float(self.radius) * self.start_step
|
|
||||||
step /= (self.scale_factor**self.order)
|
|
||||||
curr = np.zeros(3)
|
|
||||||
result = [curr]
|
|
||||||
for letter in self.get_command_string():
|
|
||||||
if letter is "+":
|
|
||||||
step = rotate(step, self.angle)
|
|
||||||
elif letter is "-":
|
|
||||||
step = rotate(step, -self.angle)
|
|
||||||
else:
|
|
||||||
curr = curr + step
|
|
||||||
result.append(curr)
|
|
||||||
return np.array(result) - center_of_mass(result)
|
|
||||||
|
|
||||||
|
|
||||||
class SelfSimilarSpaceFillingCurve(SpaceFillingCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"offsets" : [],
|
|
||||||
"offset_index_to_rotation_axis" : {},
|
|
||||||
"scale_factor" : 2,
|
|
||||||
"radius_scale_factor" : 0.5,
|
|
||||||
}
|
|
||||||
def transform(self, points, offset):
|
|
||||||
"""
|
|
||||||
How to transform the copy of points shifted by
|
|
||||||
offset. Generally meant to be extended in subclasses
|
|
||||||
"""
|
|
||||||
if offset in self.offset_index_to_rotation_axis:
|
|
||||||
return rotate(
|
|
||||||
points,
|
|
||||||
axis = self.offset_index_to_rotation_axis[offset]
|
|
||||||
)
|
|
||||||
points /= self.scale_factor,
|
|
||||||
points += offset*self.radius*self.radius_scale_factor
|
|
||||||
return points
|
|
||||||
|
|
||||||
def refine_into_subparts(self, points):
|
|
||||||
transformed_copies = [
|
|
||||||
self.transform(points, offset)
|
|
||||||
for offset in self.offsets
|
|
||||||
]
|
|
||||||
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 = {
|
|
||||||
"offsets" : [
|
|
||||||
LEFT+DOWN,
|
|
||||||
LEFT+UP,
|
|
||||||
RIGHT+UP,
|
|
||||||
RIGHT+DOWN,
|
|
||||||
],
|
|
||||||
"offset_index_to_rotation_axis" : {
|
|
||||||
0 : RIGHT+UP,
|
|
||||||
3 : RIGHT+DOWN,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class HilbertCurve3D(SelfSimilarSpaceFillingCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"offsets" : [
|
|
||||||
LEFT+DOWN+OUT,
|
|
||||||
LEFT+UP+OUT,
|
|
||||||
LEFT+UP+IN,
|
|
||||||
LEFT+DOWN+IN,
|
|
||||||
RIGHT+DOWN+IN,
|
|
||||||
RIGHT+UP+IN,
|
|
||||||
RIGHT+UP+OUT,
|
|
||||||
RIGHT+DOWN+OUT,
|
|
||||||
],
|
|
||||||
"offset_index_to_rotation_axis" : {}#TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
class PeanoCurve(SelfSimilarSpaceFillingCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"start_color" : PURPLE,
|
|
||||||
"end_color" : TEAL,
|
|
||||||
"offsets" : [
|
|
||||||
LEFT+DOWN,
|
|
||||||
LEFT,
|
|
||||||
LEFT+UP,
|
|
||||||
UP,
|
|
||||||
ORIGIN,
|
|
||||||
DOWN,
|
|
||||||
RIGHT+DOWN,
|
|
||||||
RIGHT,
|
|
||||||
RIGHT+UP,
|
|
||||||
],
|
|
||||||
"offset_index_to_rotation_axis" : {
|
|
||||||
1 : UP,
|
|
||||||
3 : RIGHT,
|
|
||||||
4 : LEFT+UP,
|
|
||||||
5 : RIGHT,
|
|
||||||
6 : UP,
|
|
||||||
},
|
|
||||||
"scale_factor" : 3,
|
|
||||||
"radius_scale_factor" : 2.0/3,
|
|
||||||
}
|
|
||||||
|
|
||||||
class TriangleFillingCurve(SelfSimilarSpaceFillingCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"start_color" : MAROON,
|
|
||||||
"end_color" : YELLOW,
|
|
||||||
"offsets" : [
|
|
||||||
LEFT/4.+DOWN/6.,
|
|
||||||
ORIGIN,
|
|
||||||
RIGHT/4.+DOWN/6.,
|
|
||||||
UP/3.,
|
|
||||||
],
|
|
||||||
"offset_index_to_rotation_axis" : {
|
|
||||||
1 : RIGHT,
|
|
||||||
3 : UP,
|
|
||||||
},
|
|
||||||
"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,
|
|
||||||
# rotate(points, np.pi/6, IN)
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
class UtahFillingCurve(SelfSimilarSpaceFillingCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"start_color" : WHITE,
|
|
||||||
"end_color" : BLUE_D,
|
|
||||||
"axis_offset_pairs" : [
|
|
||||||
|
|
||||||
],
|
|
||||||
"scale_factor" : 3,
|
|
||||||
"radius_scale_factor" : 2/(3*np.sqrt(3)),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class FlowSnake(LindenmayerCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"start_color" : YELLOW,
|
|
||||||
"end_color" : GREEN,
|
|
||||||
"axiom" : "A",
|
|
||||||
"rule" : {
|
|
||||||
"A" : "A-B--B+A++AA+B-",
|
|
||||||
"B" : "+A-BB--B-A++A+B",
|
|
||||||
},
|
|
||||||
"radius" : 6, #TODO, this is innaccurate
|
|
||||||
"scale_factor" : np.sqrt(7),
|
|
||||||
"start_step" : RIGHT,
|
|
||||||
"angle" : -np.pi/3,
|
|
||||||
}
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
LindenmayerCurve.__init__(self, **kwargs)
|
|
||||||
self.rotate(-self.order*np.pi/9)
|
|
||||||
|
|
||||||
class Sierpinski(LindenmayerCurve):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"start_color" : RED,
|
|
||||||
"end_color" : WHITE,
|
|
||||||
"axiom" : "A",
|
|
||||||
"rule" : {
|
|
||||||
"A" : "+B-A-B+",
|
|
||||||
"B" : "-A+B+A-",
|
|
||||||
},
|
|
||||||
"radius" : 6, #TODO, this is innaccurate
|
|
||||||
"scale_factor" : 2,
|
|
||||||
"start_step" : RIGHT,
|
|
||||||
"angle" : -np.pi/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()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4,4 +4,4 @@ __all__ = [
|
|||||||
"tex_mobject",
|
"tex_mobject",
|
||||||
]
|
]
|
||||||
|
|
||||||
from mobject import Mobject, Mobject1D, Mobject2D
|
from mobject import Mobject, Point, Mobject1D, Mobject2D
|
@ -450,16 +450,15 @@ class Mobject(object):
|
|||||||
smaller.apply_over_attr_arrays(
|
smaller.apply_over_attr_arrays(
|
||||||
lambda a : streth_array_to_length(a, target_size)
|
lambda a : streth_array_to_length(a, target_size)
|
||||||
)
|
)
|
||||||
|
#Recurse
|
||||||
num_sub_mobjects1 = len(mobject1.sub_mobjects)
|
diff = len(mobject1.sub_mobjects) - len(mobject2.sub_mobjects)
|
||||||
num_sub_mobjects2 = len(mobject2.sub_mobjects)
|
|
||||||
if num_sub_mobjects1 != num_sub_mobjects2:
|
if diff < 0:
|
||||||
diff = abs(num_sub_mobjects1 - num_sub_mobjects2)
|
larger, smaller = mobject2, mobject1
|
||||||
if num_sub_mobjects1 < num_sub_mobjects2:
|
elif diff > 0:
|
||||||
larger, smaller = mobject2, mobject1
|
larger, smaller = mobject1, mobject2
|
||||||
else:
|
if diff != 0:
|
||||||
larger, smaller = mobject1, mobject2
|
for sub_mob in larger.sub_mobjects[-abs(diff):]:
|
||||||
for sub_mob in larger.sub_mobjects[-diff:]:
|
|
||||||
smaller.add(Point(sub_mob.get_center()))
|
smaller.add(Point(sub_mob.get_center()))
|
||||||
for m1, m2 in zip(mobject1.sub_mobjects, mobject2.sub_mobjects):
|
for m1, m2 in zip(mobject1.sub_mobjects, mobject2.sub_mobjects):
|
||||||
Mobject.align_data(m1, m2)
|
Mobject.align_data(m1, m2)
|
||||||
@ -477,6 +476,18 @@ class Mobject(object):
|
|||||||
getattr(mobject2, attr),
|
getattr(mobject2, attr),
|
||||||
alpha))
|
alpha))
|
||||||
|
|
||||||
|
|
||||||
|
class Point(Mobject):
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
"color" : BLACK,
|
||||||
|
}
|
||||||
|
def __init__(self, location = ORIGIN, **kwargs):
|
||||||
|
digest_locals(self)
|
||||||
|
Mobject.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
def generate_points(self):
|
||||||
|
self.add_points([self.location])
|
||||||
|
|
||||||
#TODO, Make the two implementations bellow non-redundant
|
#TODO, Make the two implementations bellow non-redundant
|
||||||
class Mobject1D(Mobject):
|
class Mobject1D(Mobject):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
|
@ -9,7 +9,7 @@ class TexMobject(Mobject):
|
|||||||
"template_tex_file" : TEMPLATE_TEX_FILE,
|
"template_tex_file" : TEMPLATE_TEX_FILE,
|
||||||
"color" : WHITE,
|
"color" : WHITE,
|
||||||
"point_thickness" : 1,
|
"point_thickness" : 1,
|
||||||
"should_center" : False,
|
"should_center" : True,
|
||||||
}
|
}
|
||||||
def __init__(self, expression, **kwargs):
|
def __init__(self, expression, **kwargs):
|
||||||
if "size" not in kwargs:
|
if "size" not in kwargs:
|
||||||
|
@ -38,8 +38,9 @@ class Scene(object):
|
|||||||
(self.height, self.width, 3),
|
(self.height, self.width, 3),
|
||||||
dtype = 'uint8'
|
dtype = 'uint8'
|
||||||
)
|
)
|
||||||
self.background = self.original_background
|
self.background = self.original_background
|
||||||
self.frames = [self.background]
|
self.curr_frame = self.background
|
||||||
|
self.frames = [self.curr_frame]
|
||||||
self.mobjects = []
|
self.mobjects = []
|
||||||
|
|
||||||
self.construct(*self.construct_args)
|
self.construct(*self.construct_args)
|
||||||
@ -58,10 +59,10 @@ class Scene(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
return self.frames[-1]
|
return self.curr_frame
|
||||||
|
|
||||||
def set_frame(self, frame):
|
def set_frame(self, frame):
|
||||||
self.frames[-1] = frame
|
self.curr_frame = frame
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add(self, *mobjects):
|
def add(self, *mobjects):
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
from mobject import Mobject, Mobject, ImageMobject, TexMobject
|
from mobject import Mobject
|
||||||
|
from mobject.image_mobject import ImageMobject
|
||||||
|
from mobject.tex_mobject import TexMobject
|
||||||
|
from topics.geometry import Circle
|
||||||
|
|
||||||
|
|
||||||
PI_CREATURE_DIR = os.path.join(IMAGE_DIR, "PiCreature")
|
PI_CREATURE_DIR = os.path.join(IMAGE_DIR, "PiCreature")
|
||||||
@ -163,7 +166,6 @@ class Mortimer(PiCreature):
|
|||||||
class Bubble(Mobject):
|
class Bubble(Mobject):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"direction" : LEFT,
|
"direction" : LEFT,
|
||||||
"index_of_tip" : -1,
|
|
||||||
"center_point" : ORIGIN,
|
"center_point" : ORIGIN,
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@ -174,7 +176,7 @@ class Bubble(Mobject):
|
|||||||
self.content = Mobject()
|
self.content = Mobject()
|
||||||
|
|
||||||
def get_tip(self):
|
def get_tip(self):
|
||||||
return self.points[self.index_of_tip]
|
raise Exception("Not implemented")
|
||||||
|
|
||||||
def get_bubble_center(self):
|
def get_bubble_center(self):
|
||||||
return self.get_center()+self.center_offset
|
return self.get_center()+self.center_offset
|
||||||
@ -245,6 +247,9 @@ class SpeechBubble(Bubble):
|
|||||||
self.rotate(np.pi/2)
|
self.rotate(np.pi/2)
|
||||||
self.points[:,1] *= float(self.initial_height)/self.initial_width
|
self.points[:,1] *= float(self.initial_height)/self.initial_width
|
||||||
|
|
||||||
|
def get_tip(self):
|
||||||
|
pass
|
||||||
|
|
||||||
class ThoughtBubble(Bubble):
|
class ThoughtBubble(Bubble):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"num_bulges" : 7,
|
"num_bulges" : 7,
|
||||||
@ -253,10 +258,14 @@ class ThoughtBubble(Bubble):
|
|||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
Bubble.__init__(self, **kwargs)
|
Bubble.__init__(self, **kwargs)
|
||||||
self.index_of_tip = np.argmin(self.points[:,1])
|
|
||||||
|
def get_tip(self):
|
||||||
|
return self.small_circle.get_bottom()
|
||||||
|
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
self.add(Circle().scale(0.15).shift(2.5*DOWN+2*LEFT))
|
self.small_circle = Circle().scale(0.15)
|
||||||
|
self.small_circle.shift(2.5*DOWN+2*LEFT)
|
||||||
|
self.add(self.small_circle)
|
||||||
self.add(Circle().scale(0.3).shift(2*DOWN+1.5*LEFT))
|
self.add(Circle().scale(0.3).shift(2*DOWN+1.5*LEFT))
|
||||||
for n in range(self.num_bulges):
|
for n in range(self.num_bulges):
|
||||||
theta = 2*np.pi*n/self.num_bulges
|
theta = 2*np.pi*n/self.num_bulges
|
||||||
|
@ -2,18 +2,6 @@ from helpers import *
|
|||||||
|
|
||||||
from mobject import Mobject, Mobject1D
|
from mobject import Mobject, Mobject1D
|
||||||
|
|
||||||
|
|
||||||
class Point(Mobject):
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"color" : BLACK,
|
|
||||||
}
|
|
||||||
def __init__(self, location = ORIGIN, **kwargs):
|
|
||||||
digest_locals(self)
|
|
||||||
Mobject.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
def generate_points(self):
|
|
||||||
self.add_points([self.location])
|
|
||||||
|
|
||||||
class Dot(Mobject1D): #Use 1D density, even though 2D
|
class Dot(Mobject1D): #Use 1D density, even though 2D
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"radius" : 0.05
|
"radius" : 0.05
|
||||||
|
Reference in New Issue
Block a user