From 7912f6f2d39788838c3729b98330412a395af18e Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 22 Aug 2018 14:48:42 -0700 Subject: [PATCH] Implmented ThreeDScene.add_fixed_orientation_mobjects and ThreeDScene.add_fixed_in_frame_mobjects --- camera/camera.py | 27 +++++++++++++++------- camera/three_d_camera.py | 48 +++++++++++++++++++++++++++++++++++----- mobject/mobject.py | 2 ++ mobject/number_line.py | 16 ++++++++------ scene/three_d_scene.py | 12 ++++++++++ utils/space_ops.py | 1 + 6 files changed, 86 insertions(+), 20 deletions(-) diff --git a/camera/camera.py b/camera/camera.py index 98d4ad97..344ab98c 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -341,7 +341,9 @@ class Camera(object): def set_cairo_context_path(self, ctx, vmobject): ctx.new_path() for vmob in it.chain([vmobject], vmobject.get_subpath_mobjects()): - points = self.transform_points_pre_display(vmob.points) + points = self.transform_points_pre_display( + vmob, vmob.points + ) ctx.new_sub_path() ctx.move_to(*points[0][:2]) for triplet in zip(points[1::3], points[2::3], points[3::3]): @@ -361,7 +363,9 @@ class Camera(object): ) else: points = vmobject.get_gradient_start_and_end_points() - points = self.transform_points_pre_display(points) + points = self.transform_points_pre_display( + vmobject, points + ) pat = cairo.LinearGradient(*it.chain(*[ point[:2] for point in points ])) @@ -420,16 +424,19 @@ class Camera(object): def display_multiple_point_cloud_mobjects(self, pmobjects, pixel_array): for pmobject in pmobjects: self.display_point_cloud( + pmobject, pmobject.points, pmobject.rgbas, self.adjusted_thickness(pmobject.stroke_width), pixel_array, ) - def display_point_cloud(self, points, rgbas, thickness, pixel_array): + def display_point_cloud(self, pmobject, points, rgbas, thickness, pixel_array): if len(points) == 0: return - pixel_coords = self.points_to_pixel_coords(points) + pixel_coords = self.points_to_pixel_coords( + pmobject, points + ) pixel_coords = self.thickened_coordinates( pixel_coords, thickness ) @@ -461,7 +468,9 @@ class Camera(object): self.display_image_mobject(image_mobject, pixel_array) def display_image_mobject(self, image_mobject, pixel_array): - corner_coords = self.points_to_pixel_coords(image_mobject.points) + corner_coords = self.points_to_pixel_coords( + image_mobject, image_mobject.points + ) ul_coords, ur_coords, dl_coords = corner_coords right_vect = ur_coords - ul_coords down_vect = dl_coords - ul_coords @@ -538,13 +547,15 @@ class Camera(object): points[violator_indices] = rescaled return points - def transform_points_pre_display(self, points): + def transform_points_pre_display(self, points, mobject): # Subclasses (like ThreeDCamera) may want to # adjust points before they're shown return points - def points_to_pixel_coords(self, points): - points = self.transform_points_pre_display(points) + def points_to_pixel_coords(self, mobject, points): + points = self.transform_points_pre_display( + mobject, points + ) shifted_points = points - self.get_frame_center() result = np.zeros((len(points), 2)) diff --git a/camera/three_d_camera.py b/camera/three_d_camera.py index a2d31b26..bad356e7 100644 --- a/camera/three_d_camera.py +++ b/camera/three_d_camera.py @@ -6,7 +6,6 @@ from constants import * from camera.camera import Camera from mobject.types.point_cloud_mobject import Point -from mobject.three_dimensions import ThreeDVMobject from mobject.three_d_utils import get_3d_vmob_start_corner from mobject.three_d_utils import get_3d_vmob_start_corner_unit_normal from mobject.three_d_utils import get_3d_vmob_end_corner @@ -14,8 +13,10 @@ from mobject.three_d_utils import get_3d_vmob_end_corner_unit_normal from mobject.value_tracker import ValueTracker from utils.color import get_shaded_rgb +# from utils.config_ops import digest_config from utils.space_ops import rotation_about_z from utils.space_ops import rotation_matrix +from utils.space_ops import center_of_mass class ThreeDCamera(Camera): @@ -40,6 +41,8 @@ class ThreeDCamera(Camera): self.gamma_tracker = ValueTracker(self.gamma) self.light_source = Point(self.light_source_start_point) self.frame_center = Point(self.frame_center) + self.fixed_orientation_mobjects = set() + self.fixed_in_frame_mobjects = set() self.reset_rotation_matrix() def capture_mobjects(self, mobjects, **kwargs): @@ -155,15 +158,50 @@ class ThreeDCamera(Camera): result = np.dot(matrix, result) return result - def transform_points_pre_display(self, points): - fc = self.get_frame_center() + def project_points(self, points): + frame_center = self.get_frame_center() distance = self.get_distance() - points -= fc rot_matrix = self.get_rotation_matrix() + + points -= frame_center points = np.dot(points, rot_matrix.T) zs = points[:, 2] zs[zs >= distance] = distance - 0.001 for i in 0, 1: points[:, i] *= distance / (distance - zs) - points += fc + points += frame_center return points + + def project_point(self, point): + return self.project_points(point.reshape((1, 3)))[0, :] + + def transform_points_pre_display(self, mobject, points): + fixed_orientation = mobject in self.fixed_orientation_mobjects + fixed_in_frame = mobject in self.fixed_in_frame_mobjects + + if fixed_in_frame: + return points + if fixed_orientation: + center = center_of_mass(points) + new_center = self.project_point(center) + return points + (new_center - center) + else: + return self.project_points(points) + + def add_fixed_orientation_mobjects(self, *mobjects): + for mobject in self.extract_mobject_family_members(mobjects): + self.fixed_orientation_mobjects.add(mobject) + + def add_fixed_in_frame_mobjects(self, *mobjects): + for mobject in self.extract_mobject_family_members(mobjects): + self.fixed_in_frame_mobjects.add(mobject) + + def remove_fixed_orientation_mobjects(self, *mobjects): + for mobject in self.extract_mobject_family_members(mobjects): + if mobject in self.fixed_orientation_mobjects: + self.fixed_orientation_mobjects.remove(mobject) + + def remove_fixed_in_frame_mobjects(self, *mobjects): + for mobject in self.extract_mobject_family_members(mobjects): + if mobject in self.fixed_in_frame_mobjects: + self.fixed_in_frame_mobjects.remove(mobject) diff --git a/mobject/mobject.py b/mobject/mobject.py index 2081da3a..4fe733ee 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -705,6 +705,8 @@ class Mobject(Container): def get_critical_point(self, direction): result = np.zeros(self.dim) all_points = self.get_all_points() + if len(all_points) == 0: + return result for dim in range(self.dim): if direction[dim] <= 0: min_val = min(all_points[:, dim]) diff --git a/mobject/number_line.py b/mobject/number_line.py index b7d8d336..819d93b1 100644 --- a/mobject/number_line.py +++ b/mobject/number_line.py @@ -96,16 +96,18 @@ class NumberLine(VMobject): ) def point_to_number(self, point): - left_point, right_point = self.main_line.get_start_and_end() - full_vect = right_point - left_point + start_point, end_point = self.main_line.get_start_and_end() + full_vect = end_point - start_point + unit_vect = normalize(full_vect) - def distance_from_left(p): - return np.dot(p - left_point, full_vect) / get_norm(full_vect) + def distance_from_start(p): + return np.dot(p - start_point, unit_vect) - return interpolate( - self.x_min, self.x_max, - distance_from_left(point) / distance_from_left(right_point) + proportion = fdiv( + distance_from_start(point), + distance_from_start(end_point) ) + return interpolate(self.x_min, self.x_max, proportion) def default_numbers_to_display(self): if self.numbers_to_show is not None: diff --git a/scene/three_d_scene.py b/scene/three_d_scene.py index 4c4323e7..c4c00595 100644 --- a/scene/three_d_scene.py +++ b/scene/three_d_scene.py @@ -72,3 +72,15 @@ class ThreeDScene(Scene): if any([cm in moving_mobjects for cm in camera_mobjects]): return self.mobjects return moving_mobjects + + def add_fixed_orientation_mobjects(self, *mobjects): + self.camera.add_fixed_orientation_mobjects(*mobjects) + + def add_fixed_in_frame_mobjects(self, *mobjects): + self.camera.add_fixed_in_frame_mobjects(*mobjects) + + def remove_fixed_orientation_mobjects(self, *mobjects): + self.camera.remove_fixed_orientation_mobjects(*mobjects) + + def remove_fixed_in_frame_mobjects(self, *mobjects): + self.camera.remove_fixed_in_frame_mobjects(*mobjects) diff --git a/utils/space_ops.py b/utils/space_ops.py index cc8f5039..40026a1a 100644 --- a/utils/space_ops.py +++ b/utils/space_ops.py @@ -6,6 +6,7 @@ from functools import reduce # Matrix operations + def get_norm(vect): return sum([x**2 for x in vect])**0.5