2016-04-09 20:03:57 -07:00
|
|
|
from .mobject import Mobject
|
|
|
|
from helpers import *
|
|
|
|
|
2016-04-14 19:30:47 -07:00
|
|
|
class PMobject(Mobject):
|
2016-04-17 19:29:27 -07:00
|
|
|
def init_points(self):
|
2017-09-19 13:12:45 -07:00
|
|
|
self.rgbas = np.zeros((0, 4))
|
2016-04-17 19:29:27 -07:00
|
|
|
self.points = np.zeros((0, 3))
|
2016-04-09 20:03:57 -07:00
|
|
|
return self
|
|
|
|
|
|
|
|
def get_array_attrs(self):
|
2017-09-19 13:12:45 -07:00
|
|
|
return Mobject.get_array_attrs(self) + ["rgbas"]
|
2016-04-09 20:03:57 -07:00
|
|
|
|
2017-09-19 13:12:45 -07:00
|
|
|
def add_points(self, points, rgbas = None, color = None, alpha = 1):
|
2016-04-09 20:03:57 -07:00
|
|
|
"""
|
2017-09-19 13:12:45 -07:00
|
|
|
points must be a Nx3 numpy array, as must rgbas if it is not None
|
2016-04-09 20:03:57 -07:00
|
|
|
"""
|
|
|
|
if not isinstance(points, np.ndarray):
|
|
|
|
points = np.array(points)
|
2017-09-19 13:12:45 -07:00
|
|
|
num_new_points = len(points)
|
2016-04-09 20:03:57 -07:00
|
|
|
self.points = np.append(self.points, points, axis = 0)
|
2017-09-19 13:12:45 -07:00
|
|
|
if rgbas is None:
|
2016-04-09 20:03:57 -07:00
|
|
|
color = Color(color) if color else self.color
|
2017-09-19 13:12:45 -07:00
|
|
|
rgbas = np.repeat(
|
|
|
|
[color_to_rgba(color, alpha)],
|
|
|
|
num_new_points,
|
|
|
|
axis = 0
|
|
|
|
)
|
|
|
|
elif len(rgbas) != len(points):
|
|
|
|
raise Exception("points and rgbas must have same shape")
|
|
|
|
self.rgbas = np.append(self.rgbas, rgbas, axis = 0)
|
2016-04-09 20:03:57 -07:00
|
|
|
return self
|
|
|
|
|
2017-10-26 21:30:59 -07:00
|
|
|
def highlight(self, color = YELLOW_C, family = True):
|
2017-09-19 13:12:45 -07:00
|
|
|
rgba = color_to_rgba(color)
|
2016-07-22 11:22:31 -07:00
|
|
|
mobs = self.family_members_with_points() if family else [self]
|
|
|
|
for mob in mobs:
|
2017-10-26 21:30:59 -07:00
|
|
|
mob.rgbas[:,:] = rgba
|
2016-04-09 20:03:57 -07:00
|
|
|
return self
|
|
|
|
|
2018-01-17 12:17:58 -08:00
|
|
|
# def gradient_highlight(self, start_color, end_color):
|
|
|
|
def gradient_highlight(self, *colors):
|
|
|
|
self.rgbas = np.array(map(
|
|
|
|
color_to_rgba,
|
|
|
|
color_gradient(colors, len(self.points))
|
|
|
|
))
|
|
|
|
return self
|
|
|
|
|
2017-09-19 13:12:45 -07:00
|
|
|
start_rgba, end_rgba = map(color_to_rgba, [start_color, end_color])
|
2016-07-18 14:03:25 -07:00
|
|
|
for mob in self.family_members_with_points():
|
2016-04-09 20:03:57 -07:00
|
|
|
num_points = mob.get_num_points()
|
2017-09-19 13:12:45 -07:00
|
|
|
mob.rgbas = np.array([
|
|
|
|
interpolate(start_rgba, end_rgba, alpha)
|
2016-04-09 20:03:57 -07:00
|
|
|
for alpha in np.arange(num_points)/float(num_points)
|
|
|
|
])
|
|
|
|
return self
|
|
|
|
|
|
|
|
def match_colors(self, mobject):
|
|
|
|
Mobject.align_data(self, mobject)
|
2017-09-19 13:12:45 -07:00
|
|
|
self.rgbas = np.array(mobject.rgbas)
|
2016-04-09 20:03:57 -07:00
|
|
|
return self
|
|
|
|
|
|
|
|
def filter_out(self, condition):
|
2016-07-18 14:03:25 -07:00
|
|
|
for mob in self.family_members_with_points():
|
2016-04-09 20:03:57 -07:00
|
|
|
to_eliminate = ~np.apply_along_axis(condition, 1, mob.points)
|
|
|
|
mob.points = mob.points[to_eliminate]
|
2017-09-19 13:12:45 -07:00
|
|
|
mob.rgbas = mob.rgbas[to_eliminate]
|
2016-04-09 20:03:57 -07:00
|
|
|
return self
|
|
|
|
|
|
|
|
def thin_out(self, factor = 5):
|
|
|
|
"""
|
|
|
|
Removes all but every nth point for n = factor
|
|
|
|
"""
|
2016-07-18 14:03:25 -07:00
|
|
|
for mob in self.family_members_with_points():
|
2016-04-09 20:03:57 -07:00
|
|
|
num_points = self.get_num_points()
|
|
|
|
mob.apply_over_attr_arrays(
|
|
|
|
lambda arr : arr[
|
|
|
|
np.arange(0, num_points, factor)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def sort_points(self, function = lambda p : p[0]):
|
|
|
|
"""
|
|
|
|
function is any map from R^3 to R
|
|
|
|
"""
|
2016-07-18 14:03:25 -07:00
|
|
|
for mob in self.family_members_with_points():
|
2016-04-09 20:03:57 -07:00
|
|
|
indices = np.argsort(
|
|
|
|
np.apply_along_axis(function, 1, mob.points)
|
|
|
|
)
|
|
|
|
mob.apply_over_attr_arrays(lambda arr : arr[indices])
|
|
|
|
return self
|
|
|
|
|
|
|
|
def fade_to(self, color, alpha):
|
2017-09-19 13:12:45 -07:00
|
|
|
self.rgbas = interpolate(self.rgbas, color_to_rgba(color), alpha)
|
2016-04-17 00:31:38 -07:00
|
|
|
for mob in self.submobjects:
|
2016-04-09 20:03:57 -07:00
|
|
|
mob.fade_to(color, alpha)
|
|
|
|
return self
|
|
|
|
|
2017-09-19 13:12:45 -07:00
|
|
|
def get_all_rgbas(self):
|
|
|
|
return self.get_merged_array("rgbas")
|
2016-04-09 20:03:57 -07:00
|
|
|
|
2016-04-17 00:31:38 -07:00
|
|
|
def ingest_submobjects(self):
|
2016-04-09 20:03:57 -07:00
|
|
|
attrs = self.get_array_attrs()
|
|
|
|
arrays = map(self.get_merged_array, attrs)
|
|
|
|
for attr, array in zip(attrs, arrays):
|
|
|
|
setattr(self, attr, array)
|
2016-04-17 00:31:38 -07:00
|
|
|
self.submobjects = []
|
2016-04-09 20:03:57 -07:00
|
|
|
return self
|
|
|
|
|
|
|
|
def get_color(self):
|
2017-09-19 13:12:45 -07:00
|
|
|
return rgba_to_color(self.rgbas[0, :])
|
2016-04-09 20:03:57 -07:00
|
|
|
|
|
|
|
def point_from_proportion(self, alpha):
|
|
|
|
index = alpha*(self.get_num_points()-1)
|
|
|
|
return self.points[index]
|
|
|
|
|
2016-04-10 12:34:28 -07:00
|
|
|
# Alignment
|
|
|
|
def align_points_with_larger(self, larger_mobject):
|
2016-04-14 19:30:47 -07:00
|
|
|
assert(isinstance(larger_mobject, PMobject))
|
2016-04-10 12:34:28 -07:00
|
|
|
self.apply_over_attr_arrays(
|
2017-05-31 16:07:37 -07:00
|
|
|
lambda a : stretch_array_to_length(
|
2016-04-10 12:34:28 -07:00
|
|
|
a, larger_mobject.get_num_points()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2016-04-14 19:30:47 -07:00
|
|
|
def get_point_mobject(self, center = None):
|
2016-04-13 20:30:26 -07:00
|
|
|
if center is None:
|
|
|
|
center = self.get_center()
|
|
|
|
return Point(center)
|
2016-04-10 12:34:28 -07:00
|
|
|
|
|
|
|
def interpolate_color(self, mobject1, mobject2, alpha):
|
2017-09-19 13:12:45 -07:00
|
|
|
self.rgbas = interpolate(
|
|
|
|
mobject1.rgbas, mobject2.rgbas, alpha
|
2016-04-10 12:34:28 -07:00
|
|
|
)
|
|
|
|
|
2016-07-19 11:08:31 -07:00
|
|
|
def pointwise_become_partial(self, mobject, a, b):
|
2016-04-11 21:18:52 -07:00
|
|
|
lower_index, upper_index = [
|
|
|
|
int(x * mobject.get_num_points())
|
|
|
|
for x in a, b
|
|
|
|
]
|
|
|
|
for attr in self.get_array_attrs():
|
|
|
|
full_array = getattr(mobject, attr)
|
|
|
|
partial_array = full_array[lower_index:upper_index]
|
|
|
|
setattr(self, attr, partial_array)
|
|
|
|
|
2016-04-09 20:03:57 -07:00
|
|
|
|
|
|
|
#TODO, Make the two implementations bellow non-redundant
|
2016-04-14 19:30:47 -07:00
|
|
|
class Mobject1D(PMobject):
|
2016-04-09 20:03:57 -07:00
|
|
|
CONFIG = {
|
|
|
|
"density" : DEFAULT_POINT_DENSITY_1D,
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
|
|
|
self.epsilon = 1.0 / self.density
|
|
|
|
Mobject.__init__(self, **kwargs)
|
|
|
|
|
|
|
|
def add_line(self, start, end, color = None):
|
|
|
|
start, end = map(np.array, [start, end])
|
|
|
|
length = np.linalg.norm(end - start)
|
|
|
|
if length == 0:
|
|
|
|
points = [start]
|
|
|
|
else:
|
|
|
|
epsilon = self.epsilon/length
|
|
|
|
points = [
|
|
|
|
interpolate(start, end, t)
|
|
|
|
for t in np.arange(0, 1, epsilon)
|
|
|
|
]
|
|
|
|
self.add_points(points, color = color)
|
|
|
|
|
2016-04-14 19:30:47 -07:00
|
|
|
class Mobject2D(PMobject):
|
2016-04-09 20:03:57 -07:00
|
|
|
CONFIG = {
|
|
|
|
"density" : DEFAULT_POINT_DENSITY_2D,
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
|
|
|
self.epsilon = 1.0 / self.density
|
|
|
|
Mobject.__init__(self, **kwargs)
|
|
|
|
|
|
|
|
|
2018-01-17 13:41:40 -08:00
|
|
|
class PointCloudDot(Mobject1D):
|
|
|
|
CONFIG = {
|
|
|
|
"radius" : 0.075,
|
|
|
|
"stroke_width" : 2,
|
|
|
|
"density" : DEFAULT_POINT_DENSITY_1D,
|
|
|
|
"color" : YELLOW,
|
|
|
|
}
|
|
|
|
def __init__(self, center, **kwargs):
|
|
|
|
Mobject1D.__init__(self, **kwargs)
|
|
|
|
self.shift(center)
|
|
|
|
|
|
|
|
def generate_points(self):
|
|
|
|
self.add_points([
|
|
|
|
r*(np.cos(theta)*RIGHT + np.sin(theta)*UP)
|
|
|
|
for r in np.arange(0, self.radius, self.epsilon)
|
|
|
|
for theta in np.arange(0, 2*np.pi, self.epsilon/r)
|
|
|
|
])
|
2016-04-09 20:03:57 -07:00
|
|
|
|
2016-04-14 19:30:47 -07:00
|
|
|
class Point(PMobject):
|
2016-04-09 20:03:57 -07:00
|
|
|
CONFIG = {
|
|
|
|
"color" : BLACK,
|
|
|
|
}
|
|
|
|
def __init__(self, location = ORIGIN, **kwargs):
|
2016-04-14 19:30:47 -07:00
|
|
|
PMobject.__init__(self, **kwargs)
|
2016-04-10 12:34:28 -07:00
|
|
|
self.add_points([location])
|
2016-04-09 20:03:57 -07:00
|
|
|
|