From e003bf5c4706b2a9af970e9a052aed46755148bf Mon Sep 17 00:00:00 2001 From: "Garrett Credi [ddxtanx]" Date: Mon, 5 Mar 2018 18:53:13 -0600 Subject: [PATCH 1/2] Add 3D graphing via square approximation. --- topics/three_dimensions.py | 256 +++++++++++++++++-------------------- 1 file changed, 120 insertions(+), 136 deletions(-) diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index 752e4818..b3967ae4 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -1,45 +1,37 @@ - from helpers import * - from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint from topics.geometry import Square, Line from scene import Scene from camera import Camera from animation.continual_animation import AmbientMovement from animation.transform import ApplyMethod +import numpy as np class CameraWithPerspective(Camera): - CONFIG = { - "camera_distance" : 20, - } + CONFIG = {'camera_distance': 20} + def points_to_pixel_coords(self, points): - distance_ratios = np.divide( - self.camera_distance, - self.camera_distance - points[:,2] - ) + distance_ratios = np.divide(self.camera_distance, self.camera_distance - points[:, 2]) scale_factors = interpolate(0, 1, distance_ratios) adjusted_points = np.array(points) - for i in 0, 1: - adjusted_points[:,i] *= scale_factors + for i in (0, 1): + adjusted_points[(:, i)] *= scale_factors return Camera.points_to_pixel_coords(self, adjusted_points) + class ThreeDCamera(CameraWithPerspective): - CONFIG = { - "sun_vect" : 5*UP+LEFT, - "shading_factor" : 0.2, - "distance" : 5., - "default_distance" : 5., - "phi" : 0, #Angle off z axis - "theta" : -TAU/4, #Rotation about z axis - } + CONFIG = {'sun_vect': 5 * UP + LEFT, + 'shading_factor': 0.2, + 'distance': 5.0, + 'default_distance': 5.0, + 'phi': 0, + 'theta': -TAU / 4} + def __init__(self, *args, **kwargs): Camera.__init__(self, *args, **kwargs) - self.unit_sun_vect = self.sun_vect/np.linalg.norm(self.sun_vect) - ## rotation_mobject lives in the phi-theta-distance space + self.unit_sun_vect = self.sun_vect / np.linalg.norm(self.sun_vect) self.rotation_mobject = VectorizedPoint() - ## moving_center lives in the x-y-z space - ## It representes the center of rotation self.moving_center = VectorizedPoint(self.space_center) self.set_position(self.phi, self.theta, self.distance) @@ -56,64 +48,56 @@ class ThreeDCamera(CameraWithPerspective): return self.modified_rgb(vmobject, vmobject.get_fill_rgb()) def get_shaded_rgb(self, rgb, normal_vect): - brightness = np.dot(normal_vect, self.unit_sun_vect)**2 + brightness = np.dot(normal_vect, self.unit_sun_vect) ** 2 if brightness > 0: - alpha = self.shading_factor*brightness + alpha = self.shading_factor * brightness return interpolate(rgb, np.ones(3), alpha) else: - alpha = -self.shading_factor*brightness + alpha = -self.shading_factor * brightness return interpolate(rgb, np.zeros(3), alpha) def get_unit_normal_vect(self, vmobject): anchors = vmobject.get_anchors() if len(anchors) < 3: return OUT - normal = np.cross(anchors[1]-anchors[0], anchors[2]-anchors[1]) + normal = np.cross(anchors[1] - anchors[0], anchors[2] - anchors[1]) if normal[2] < 0: normal = -normal length = np.linalg.norm(normal) if length == 0: return OUT - return normal/length + return normal / length def display_multiple_vectorized_mobjects(self, vmobjects): - camera_point = self.spherical_coords_to_point( - *self.get_spherical_coords() - ) + camera_point = self.spherical_coords_to_point(*self.get_spherical_coords()) + def z_cmp(*vmobs): - # Compare to three dimensional mobjects based on - # how close they are to the camera - # return cmp(*[ - # -np.linalg.norm(vm.get_center()-camera_point) - # for vm in vmobs - # ]) three_d_status = map(should_shade_in_3d, vmobs) - has_points = [vm.get_num_points() > 0 for vm in vmobs] + has_points = [ vm.get_num_points() > 0 for vm in vmobs ] if all(three_d_status) and all(has_points): cmp_vect = self.get_unit_normal_vect(vmobs[1]) - return cmp(*[ - np.dot(vm.get_center(), cmp_vect) - for vm in vmobs - ]) + return cmp(*[ np.dot(vm.get_center(), cmp_vect) for vm in vmobs ]) else: return 0 - Camera.display_multiple_vectorized_mobjects( - self, sorted(vmobjects, cmp = z_cmp) - ) + + Camera.display_multiple_vectorized_mobjects(self, sorted(vmobjects, cmp=z_cmp)) def get_spherical_coords(self, phi = None, theta = None, distance = None): curr_phi, curr_theta, curr_d = self.rotation_mobject.points[0] - if phi is None: phi = curr_phi - if theta is None: theta = curr_theta - if distance is None: distance = curr_d + if phi is None: + phi = curr_phi + if theta is None: + theta = curr_theta + if distance is None: + distance = curr_d return np.array([phi, theta, distance]) def get_cartesian_coords(self, phi = None, theta = None, distance = None): - spherical_coords_array = self.get_spherical_coords(phi,theta,distance) + spherical_coords_array = self.get_spherical_coords(phi, theta, distance) phi2 = spherical_coords_array[0] theta2 = spherical_coords_array[1] d2 = spherical_coords_array[2] - return self.spherical_coords_to_point(phi2,theta2,d2) + return self.spherical_coords_to_point(phi2, theta2, d2) def get_phi(self): return self.get_spherical_coords()[0] @@ -125,11 +109,7 @@ class ThreeDCamera(CameraWithPerspective): return self.get_spherical_coords()[2] def spherical_coords_to_point(self, phi, theta, distance): - return distance*np.array([ - np.sin(phi)*np.cos(theta), - np.sin(phi)*np.sin(theta), - np.cos(phi) - ]) + return distance * np.array([np.sin(phi) * np.cos(theta), np.sin(phi) * np.sin(theta), np.cos(phi)]) def get_center_of_rotation(self, x = None, y = None, z = None): curr_x, curr_y, curr_z = self.moving_center.points[0] @@ -141,8 +121,7 @@ class ThreeDCamera(CameraWithPerspective): z = curr_z return np.array([x, y, z]) - def set_position(self, phi = None, theta = None, distance = None, - center_x = None, center_y = None, center_z = None): + def set_position(self, phi = None, theta = None, distance = None, center_x = None, center_y = None, center_z = None): point = self.get_spherical_coords(phi, theta, distance) self.rotation_mobject.move_to(point) self.phi, self.theta, self.distance = point @@ -151,34 +130,24 @@ class ThreeDCamera(CameraWithPerspective): self.space_center = self.moving_center.points[0] def get_view_transformation_matrix(self): - return (self.default_distance / self.get_distance()) * np.dot( - rotation_matrix(self.get_phi(), LEFT), - rotation_about_z(-self.get_theta() - np.pi/2), - ) + return self.default_distance / self.get_distance() * np.dot(rotation_matrix(self.get_phi(), LEFT), rotation_about_z(-self.get_theta() - np.pi / 2)) def points_to_pixel_coords(self, points): matrix = self.get_view_transformation_matrix() new_points = np.dot(points, matrix.T) self.space_center = self.moving_center.points[0] - return Camera.points_to_pixel_coords(self, new_points) -class ThreeDScene(Scene): - CONFIG = { - "camera_class" : ThreeDCamera, - "ambient_camera_rotation" : None, - } - def set_camera_position(self, phi = None, theta = None, distance = None, - center_x = None, center_y = None, center_z = None): +class ThreeDScene(Scene): + CONFIG = {'camera_class': ThreeDCamera, + 'ambient_camera_rotation': None} + + def set_camera_position(self, phi = None, theta = None, distance = None, center_x = None, center_y = None, center_z = None): self.camera.set_position(phi, theta, distance, center_x, center_y, center_z) def begin_ambient_camera_rotation(self, rate = 0.01): - self.ambient_camera_rotation = AmbientMovement( - self.camera.rotation_mobject, - direction = UP, - rate = rate - ) + self.ambient_camera_rotation = AmbientMovement(self.camera.rotation_mobject, direction=UP, rate=rate) self.add(self.ambient_camera_rotation) def stop_ambient_camera_rotation(self): @@ -186,25 +155,11 @@ class ThreeDScene(Scene): self.remove(self.ambient_camera_rotation) self.ambient_camera_rotation = None - def move_camera( - self, - phi = None, theta = None, distance = None, - center_x = None, center_y = None, center_z = None, - added_anims = [], - **kwargs - ): + def move_camera(self, phi = None, theta = None, distance = None, center_x = None, center_y = None, center_z = None, added_anims = [], **kwargs): target_point = self.camera.get_spherical_coords(phi, theta, distance) - movement = ApplyMethod( - self.camera.rotation_mobject.move_to, - target_point, - **kwargs - ) + movement = ApplyMethod(self.camera.rotation_mobject.move_to, target_point, **kwargs) target_center = self.camera.get_center_of_rotation(center_x, center_y, center_z) - movement_center = ApplyMethod( - self.camera.moving_center.move_to, - target_center, - **kwargs - ) + movement_center = ApplyMethod(self.camera.moving_center.move_to, target_center, **kwargs) is_camera_rotating = self.ambient_camera_rotation in self.continual_animations if is_camera_rotating: self.remove(self.ambient_camera_rotation) @@ -219,71 +174,100 @@ class ThreeDScene(Scene): return list_update(self.mobjects, moving_mobjects) return moving_mobjects -############## def should_shade_in_3d(mobject): - return hasattr(mobject, "shade_in_3d") and mobject.shade_in_3d + return hasattr(mobject, 'shade_in_3d') and mobject.shade_in_3d + def shade_in_3d(mobject): for submob in mobject.submobject_family(): submob.shade_in_3d = True + def turn_off_3d_shading(mobject): for submob in mobject.submobject_family(): submob.shade_in_3d = False + class ThreeDMobject(VMobject): + def __init__(self, *args, **kwargs): VMobject.__init__(self, *args, **kwargs) shade_in_3d(self) -class Cube(ThreeDMobject): - CONFIG = { - "fill_opacity" : 0.75, - "fill_color" : BLUE, - "stroke_width" : 0, - "propagate_style_to_family" : True, - "side_length" : 2, - } - def generate_points(self): - for vect in IN, OUT, LEFT, RIGHT, UP, DOWN: - face = Square(side_length = self.side_length) - face.shift(self.side_length*OUT/2.0) - face.apply_function(lambda p : np.dot(p, z_to_vector(vect).T)) +class Cube(ThreeDMobject): + CONFIG = {'fill_opacity': 0.75, + 'fill_color': BLUE, + 'stroke_width': 0, + 'propagate_style_to_family': True, + 'side_length': 2} + + def generate_points(self): + for vect in (IN, + OUT, + LEFT, + RIGHT, + UP, + DOWN): + face = Square(side_length=self.side_length) + face.shift(self.side_length * OUT / 2.0) + face.apply_function(lambda p: np.dot(p, z_to_vector(vect).T)) self.add(face) + +class SphereThreeD(ThreeDMobject): + + def __init__(self, r, eps): + self.r = r + self.eps = eps + ThreeDMobject.__init__(self) + + CONFIG = {'fill_opacity': 0.75, + 'fill_color': BLUE, + 'stroke_width': 0, + 'propagate_style_to_family': True, + 'side_length': 2} + + def generate_points(self): + points = [ (self.r * (np.sin(phi) * np.cos(theta)), self.r * (np.sin(phi) * np.sin(theta)), self.r * np.cos(phi)) for phi in np.arange(0, 2 * np.pi, self.eps) for theta in np.arange(0, 2 * np.pi, self.eps) ] + for vect in points: + face = Square(side_length=self.eps) + face.apply_function(lambda p: np.dot(p, z_to_vector(vect).T)) + self.add(face) + + +class Parametric3D(ThreeDMobject): + CONFIG = {'fill_opacity': 0.75, + 'fill_color': BLUE, + 'stroke_width': 0, + 'propagate_style_to_family': True} + + def __init__(self, f, g, h, phi_min, phi_max, theta_min, theta_max, eps): + self.f = f + self.g = g + self.h = h + self.phi_min = phi_min + self.phi_max = phi_max + self.theta_min = theta_min + self.theta_max = theta_max + self.eps = eps + ThreeDMobject.__init__(self) + + def generate_points(self): + points = [ (self.f(phi, theta), self.g(phi, theta), self.h(phi, theta)) for phi in np.arange(self.phi_min, self.phi_max, self.eps) for theta in np.arange(self.theta_min, self.theta_max, self.eps) ] + for vect in points: + face = Square(side_length=self.eps) + scalefactor = np.linalg.norm(vect) + face.shift(scalefactor * OUT / 2.0) + face.apply_function(lambda p: np.dot(p, z_to_vector(vect).T)) + self.add(face) + + class Prism(Cube): - CONFIG = { - "dimensions" : [3, 2, 1] - } + CONFIG = {'dimensions': [3, 2, 1]} + def generate_points(self): Cube.generate_points(self) for dim, value in enumerate(self.dimensions): - self.rescale_to_fit(value, dim, stretch = True) - - - - - - - - - - - - - - - - - - - - - - - - - - + self.rescale_to_fit(value, dim, stretch=True) \ No newline at end of file From cc63caeec0e9bf93cb1e5cc68e4df0829d332b04 Mon Sep 17 00:00:00 2001 From: "Garrett Credi [ddxtanx]" Date: Mon, 5 Mar 2018 20:51:55 -0600 Subject: [PATCH 2/2] Improve readability of three_dimensions --- topics/three_dimensions.py | 244 ++++++++++++++++++++++++++----------- 1 file changed, 173 insertions(+), 71 deletions(-) diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index b3967ae4..5fc3fe68 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -1,4 +1,6 @@ + from helpers import * + from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint from topics.geometry import Square, Line from scene import Scene @@ -6,32 +8,38 @@ from camera import Camera from animation.continual_animation import AmbientMovement from animation.transform import ApplyMethod import numpy as np - class CameraWithPerspective(Camera): - CONFIG = {'camera_distance': 20} - + CONFIG = { + "camera_distance" : 20, + } def points_to_pixel_coords(self, points): - distance_ratios = np.divide(self.camera_distance, self.camera_distance - points[:, 2]) + distance_ratios = np.divide( + self.camera_distance, + self.camera_distance - points[:,2] + ) scale_factors = interpolate(0, 1, distance_ratios) adjusted_points = np.array(points) - for i in (0, 1): - adjusted_points[(:, i)] *= scale_factors + for i in 0, 1: + adjusted_points[:,i] *= scale_factors return Camera.points_to_pixel_coords(self, adjusted_points) - class ThreeDCamera(CameraWithPerspective): - CONFIG = {'sun_vect': 5 * UP + LEFT, - 'shading_factor': 0.2, - 'distance': 5.0, - 'default_distance': 5.0, - 'phi': 0, - 'theta': -TAU / 4} - + CONFIG = { + "sun_vect" : 5*UP+LEFT, + "shading_factor" : 0.2, + "distance" : 5., + "default_distance" : 5., + "phi" : 0, #Angle off z axis + "theta" : -TAU/4, #Rotation about z axis + } def __init__(self, *args, **kwargs): Camera.__init__(self, *args, **kwargs) - self.unit_sun_vect = self.sun_vect / np.linalg.norm(self.sun_vect) + self.unit_sun_vect = self.sun_vect/np.linalg.norm(self.sun_vect) + ## rotation_mobject lives in the phi-theta-distance space self.rotation_mobject = VectorizedPoint() + ## moving_center lives in the x-y-z space + ## It representes the center of rotation self.moving_center = VectorizedPoint(self.space_center) self.set_position(self.phi, self.theta, self.distance) @@ -48,56 +56,64 @@ class ThreeDCamera(CameraWithPerspective): return self.modified_rgb(vmobject, vmobject.get_fill_rgb()) def get_shaded_rgb(self, rgb, normal_vect): - brightness = np.dot(normal_vect, self.unit_sun_vect) ** 2 + brightness = np.dot(normal_vect, self.unit_sun_vect)**2 if brightness > 0: - alpha = self.shading_factor * brightness + alpha = self.shading_factor*brightness return interpolate(rgb, np.ones(3), alpha) else: - alpha = -self.shading_factor * brightness + alpha = -self.shading_factor*brightness return interpolate(rgb, np.zeros(3), alpha) def get_unit_normal_vect(self, vmobject): anchors = vmobject.get_anchors() if len(anchors) < 3: return OUT - normal = np.cross(anchors[1] - anchors[0], anchors[2] - anchors[1]) + normal = np.cross(anchors[1]-anchors[0], anchors[2]-anchors[1]) if normal[2] < 0: normal = -normal length = np.linalg.norm(normal) if length == 0: return OUT - return normal / length + return normal/length def display_multiple_vectorized_mobjects(self, vmobjects): - camera_point = self.spherical_coords_to_point(*self.get_spherical_coords()) - + camera_point = self.spherical_coords_to_point( + *self.get_spherical_coords() + ) def z_cmp(*vmobs): + # Compare to three dimensional mobjects based on + # how close they are to the camera + # return cmp(*[ + # -np.linalg.norm(vm.get_center()-camera_point) + # for vm in vmobs + # ]) three_d_status = map(should_shade_in_3d, vmobs) - has_points = [ vm.get_num_points() > 0 for vm in vmobs ] + has_points = [vm.get_num_points() > 0 for vm in vmobs] if all(three_d_status) and all(has_points): cmp_vect = self.get_unit_normal_vect(vmobs[1]) - return cmp(*[ np.dot(vm.get_center(), cmp_vect) for vm in vmobs ]) + return cmp(*[ + np.dot(vm.get_center(), cmp_vect) + for vm in vmobs + ]) else: return 0 - - Camera.display_multiple_vectorized_mobjects(self, sorted(vmobjects, cmp=z_cmp)) + Camera.display_multiple_vectorized_mobjects( + self, sorted(vmobjects, cmp = z_cmp) + ) def get_spherical_coords(self, phi = None, theta = None, distance = None): curr_phi, curr_theta, curr_d = self.rotation_mobject.points[0] - if phi is None: - phi = curr_phi - if theta is None: - theta = curr_theta - if distance is None: - distance = curr_d + if phi is None: phi = curr_phi + if theta is None: theta = curr_theta + if distance is None: distance = curr_d return np.array([phi, theta, distance]) def get_cartesian_coords(self, phi = None, theta = None, distance = None): - spherical_coords_array = self.get_spherical_coords(phi, theta, distance) + spherical_coords_array = self.get_spherical_coords(phi,theta,distance) phi2 = spherical_coords_array[0] theta2 = spherical_coords_array[1] d2 = spherical_coords_array[2] - return self.spherical_coords_to_point(phi2, theta2, d2) + return self.spherical_coords_to_point(phi2,theta2,d2) def get_phi(self): return self.get_spherical_coords()[0] @@ -109,7 +125,11 @@ class ThreeDCamera(CameraWithPerspective): return self.get_spherical_coords()[2] def spherical_coords_to_point(self, phi, theta, distance): - return distance * np.array([np.sin(phi) * np.cos(theta), np.sin(phi) * np.sin(theta), np.cos(phi)]) + return distance*np.array([ + np.sin(phi)*np.cos(theta), + np.sin(phi)*np.sin(theta), + np.cos(phi) + ]) def get_center_of_rotation(self, x = None, y = None, z = None): curr_x, curr_y, curr_z = self.moving_center.points[0] @@ -121,7 +141,8 @@ class ThreeDCamera(CameraWithPerspective): z = curr_z return np.array([x, y, z]) - def set_position(self, phi = None, theta = None, distance = None, center_x = None, center_y = None, center_z = None): + def set_position(self, phi = None, theta = None, distance = None, + center_x = None, center_y = None, center_z = None): point = self.get_spherical_coords(phi, theta, distance) self.rotation_mobject.move_to(point) self.phi, self.theta, self.distance = point @@ -130,24 +151,34 @@ class ThreeDCamera(CameraWithPerspective): self.space_center = self.moving_center.points[0] def get_view_transformation_matrix(self): - return self.default_distance / self.get_distance() * np.dot(rotation_matrix(self.get_phi(), LEFT), rotation_about_z(-self.get_theta() - np.pi / 2)) + return (self.default_distance / self.get_distance()) * np.dot( + rotation_matrix(self.get_phi(), LEFT), + rotation_about_z(-self.get_theta() - np.pi/2), + ) def points_to_pixel_coords(self, points): matrix = self.get_view_transformation_matrix() new_points = np.dot(points, matrix.T) self.space_center = self.moving_center.points[0] + return Camera.points_to_pixel_coords(self, new_points) - class ThreeDScene(Scene): - CONFIG = {'camera_class': ThreeDCamera, - 'ambient_camera_rotation': None} + CONFIG = { + "camera_class" : ThreeDCamera, + "ambient_camera_rotation" : None, + } - def set_camera_position(self, phi = None, theta = None, distance = None, center_x = None, center_y = None, center_z = None): + def set_camera_position(self, phi = None, theta = None, distance = None, + center_x = None, center_y = None, center_z = None): self.camera.set_position(phi, theta, distance, center_x, center_y, center_z) def begin_ambient_camera_rotation(self, rate = 0.01): - self.ambient_camera_rotation = AmbientMovement(self.camera.rotation_mobject, direction=UP, rate=rate) + self.ambient_camera_rotation = AmbientMovement( + self.camera.rotation_mobject, + direction = UP, + rate = rate + ) self.add(self.ambient_camera_rotation) def stop_ambient_camera_rotation(self): @@ -155,11 +186,25 @@ class ThreeDScene(Scene): self.remove(self.ambient_camera_rotation) self.ambient_camera_rotation = None - def move_camera(self, phi = None, theta = None, distance = None, center_x = None, center_y = None, center_z = None, added_anims = [], **kwargs): + def move_camera( + self, + phi = None, theta = None, distance = None, + center_x = None, center_y = None, center_z = None, + added_anims = [], + **kwargs + ): target_point = self.camera.get_spherical_coords(phi, theta, distance) - movement = ApplyMethod(self.camera.rotation_mobject.move_to, target_point, **kwargs) + movement = ApplyMethod( + self.camera.rotation_mobject.move_to, + target_point, + **kwargs + ) target_center = self.camera.get_center_of_rotation(center_x, center_y, center_z) - movement_center = ApplyMethod(self.camera.moving_center.move_to, target_center, **kwargs) + movement_center = ApplyMethod( + self.camera.moving_center.move_to, + target_center, + **kwargs + ) is_camera_rotating = self.ambient_camera_rotation in self.continual_animations if is_camera_rotating: self.remove(self.ambient_camera_rotation) @@ -174,34 +219,33 @@ class ThreeDScene(Scene): return list_update(self.mobjects, moving_mobjects) return moving_mobjects +############## def should_shade_in_3d(mobject): - return hasattr(mobject, 'shade_in_3d') and mobject.shade_in_3d - + return hasattr(mobject, "shade_in_3d") and mobject.shade_in_3d def shade_in_3d(mobject): for submob in mobject.submobject_family(): submob.shade_in_3d = True - def turn_off_3d_shading(mobject): for submob in mobject.submobject_family(): submob.shade_in_3d = False - class ThreeDMobject(VMobject): - def __init__(self, *args, **kwargs): VMobject.__init__(self, *args, **kwargs) shade_in_3d(self) class Cube(ThreeDMobject): - CONFIG = {'fill_opacity': 0.75, - 'fill_color': BLUE, - 'stroke_width': 0, - 'propagate_style_to_family': True, - 'side_length': 2} + CONFIG = { + 'fill_opacity': 0.75, + 'fill_color': BLUE, + 'stroke_width': 0, + 'propagate_style_to_family': True, + 'side_length': 2 + } def generate_points(self): for vect in (IN, @@ -216,34 +260,82 @@ class Cube(ThreeDMobject): self.add(face) -class SphereThreeD(ThreeDMobject): - - def __init__(self, r, eps): +class Sphere(ThreeDMobject): + CONFIG = { + 'fill_opacity': .75, + 'fill_color': BLUE, + 'stroke_width': 0, + 'propagate_style_to_family': True, + 'side_length': 2 + } + def __init__(self, r, eps, opacity = .75): self.r = r self.eps = eps + self.CONFIG['fill_opacity'] = opacity ThreeDMobject.__init__(self) - CONFIG = {'fill_opacity': 0.75, - 'fill_color': BLUE, - 'stroke_width': 0, - 'propagate_style_to_family': True, - 'side_length': 2} - def generate_points(self): - points = [ (self.r * (np.sin(phi) * np.cos(theta)), self.r * (np.sin(phi) * np.sin(theta)), self.r * np.cos(phi)) for phi in np.arange(0, 2 * np.pi, self.eps) for theta in np.arange(0, 2 * np.pi, self.eps) ] + points = [ + ( + self.r * (np.sin(phi) * np.cos(theta)), + self.r * (np.sin(phi) * np.sin(theta)), + self.r * np.cos(phi) + ) + for phi in np.arange(0, 2 * np.pi, self.eps) + for theta in np.arange(0, 2 * np.pi, self.eps) + ] for vect in points: face = Square(side_length=self.eps) + scalefactor = np.linalg.norm(vect) + face.shift(scalefactor * OUT / 2.0) face.apply_function(lambda p: np.dot(p, z_to_vector(vect).T)) self.add(face) + shade_in_3d(self) + +class Torus(ThreeDMobject): + CONFIG = { + 'fill_opacity': .75, + 'fill_color': BLUE, + 'stroke_width': 0, + 'propagate_style_to_family': True, + 'side_length': 2 + } + def __init__(self, r1, r2, eps, opacity=.75): + self.r1 = r1 + self.r2 = r2 + self.eps = eps + self.CONFIG['fill_opacity'] = opacity + ThreeDMobject.__init__(self) + + + def generate_points(self): + points = [ + ( + (self.r1 + self.r2 * np.cos(theta)) * np.cos(phi), + (self.r1 + self.r2 * np.cos(theta)) * np.sin(phi), + self.r2 * np.sin(theta) + ) + for phi in np.arange(0, 2 * np.pi, self.eps) + for theta in np.arange(0, 2 * np.pi, self.eps) + ] + for vect in points: + face = Square(side_length=self.eps) + scalefactor = np.linalg.norm(vect) + face.shift(scalefactor * OUT / 2.0) + face.apply_function(lambda p: np.dot(p, z_to_vector(vect).T)) + self.add(face) + shade_in_3d(self) class Parametric3D(ThreeDMobject): - CONFIG = {'fill_opacity': 0.75, - 'fill_color': BLUE, - 'stroke_width': 0, - 'propagate_style_to_family': True} + CONFIG = { + 'fill_opacity': 0.75, + 'fill_color': BLUE, + 'stroke_width': 0, + 'propagate_style_to_family': True + } - def __init__(self, f, g, h, phi_min, phi_max, theta_min, theta_max, eps): + def __init__(self, f, g, h, phi_min, phi_max, theta_min, theta_max, eps, opacity = .75): self.f = f self.g = g self.h = h @@ -252,16 +344,26 @@ class Parametric3D(ThreeDMobject): self.theta_min = theta_min self.theta_max = theta_max self.eps = eps + self.CONFIG['fill_opacity'] = opacity ThreeDMobject.__init__(self) def generate_points(self): - points = [ (self.f(phi, theta), self.g(phi, theta), self.h(phi, theta)) for phi in np.arange(self.phi_min, self.phi_max, self.eps) for theta in np.arange(self.theta_min, self.theta_max, self.eps) ] + points = [ + ( + self.f(phi, theta), + self.g(phi, theta), + self.h(phi, theta) + ) + for phi in np.arange(self.phi_min, self.phi_max, self.eps) + for theta in np.arange(self.theta_min, self.theta_max, self.eps) + ] for vect in points: face = Square(side_length=self.eps) scalefactor = np.linalg.norm(vect) face.shift(scalefactor * OUT / 2.0) face.apply_function(lambda p: np.dot(p, z_to_vector(vect).T)) self.add(face) + shade_in_3d(self) class Prism(Cube):