Merge pull request #375 from 3b1b/shadows

Shadows
This commit is contained in:
Grant Sanderson 2018-12-26 11:44:46 -08:00 committed by GitHub
commit 1728faf81e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 4237 additions and 119 deletions

View file

@ -57,6 +57,10 @@ class ShowShadows(ThreeDScene):
"camera_config": {
"light_source_start_point": 10 * OUT,
"frame_center": [0, 0, 0.5],
},
"initial_orientation_config": {
"phi": 60 * DEGREES,
"theta": -120 * DEGREES,
}
}
@ -144,8 +148,7 @@ class ShowShadows(ThreeDScene):
temp_shadow = updating_mobject_from_func(lambda: get_shadow(obj3d))
self.add(temp_shadow)
self.move_camera(
phi=60 * DEGREES,
theta=-120 * DEGREES,
**self.initial_orientation_config,
added_anims=[
LaggedStart(DrawBorderThenFill, obj3d)
],

View file

@ -300,7 +300,7 @@ class Camera(object):
ctx.set_matrix(cairo.Matrix(
fdiv(pw, fw), 0,
0, -fdiv(ph, fh),
(pw / 2) + fc[0] * fdiv(pw, fw),
(pw / 2) - fc[0] * fdiv(pw, fw),
(ph / 2) + fc[1] * fdiv(ph, fh),
))
self.cache_cairo_context(pixel_array, ctx)

View file

@ -11,6 +11,7 @@ from animation.creation import DrawBorderThenFill
from animation.creation import Write
from animation.creation import FadeIn
from animation.creation import FadeOut
from continual_animation.continual_animation import ContinualMovement
from mobject.svg.tex_mobject import TextMobject
from mobject.types.vectorized_mobject import VGroup
from scene.scene import Scene
@ -18,12 +19,15 @@ from scene.moving_camera_scene import MovingCameraScene
from for_3b1b_videos.pi_creature_animations import Blink
from for_3b1b_videos.pi_creature import Mortimer
from for_3b1b_videos.pi_creature import Randolph
from for_3b1b_videos.pi_creature_scene import PiCreatureScene
from mobject.geometry import Line
from mobject.geometry import DashedLine
from mobject.geometry import Rectangle
from mobject.geometry import Square
from mobject.svg.drawings import PatreonLogo
from mobject.svg.drawings import Logo
from utils.space_ops import get_norm
from utils.space_ops import normalize
class OpeningQuote(Scene):
@ -147,7 +151,7 @@ class PatreonThanks(Scene):
last_group = group
class PatreonEndScreen(PatreonThanks):
class PatreonEndScreen(PatreonThanks, PiCreatureScene):
CONFIG = {
"n_patron_columns": 3,
"max_patron_width": 3.5,
@ -155,6 +159,7 @@ class PatreonEndScreen(PatreonThanks):
"randomize_order": True,
"capitalize": True,
"name_y_spacing": 0.7,
"thanks_words": "Funded by the community, with special thanks to:",
}
def construct(self):
@ -169,10 +174,10 @@ class PatreonEndScreen(PatreonThanks):
for patron in self.specific_patrons
]
self.add_title()
# self.add_title()
self.scroll_through_patrons()
def add_title(self):
def create_pi_creatures(self):
title = self.title = TextMobject("Clicky Stuffs")
title.scale(1.5)
title.to_edge(UP, buff=MED_SMALL_BUFF)
@ -184,6 +189,7 @@ class PatreonEndScreen(PatreonThanks):
pi.look(DOWN)
pi.next_to(title, vect, buff=MED_LARGE_BUFF)
self.add_foreground_mobjects(title, randy, morty)
return self.pi_creatures
def scroll_through_patrons(self):
logo_box = Square(side_length=2.5)
@ -202,12 +208,13 @@ class PatreonEndScreen(PatreonThanks):
line = DashedLine(FRAME_X_RADIUS * LEFT, FRAME_X_RADIUS * RIGHT)
line.move_to(ORIGIN)
thanks = TextMobject("Funded by the community, with special thanks to:")
thanks = TextMobject(self.thanks_words)
thanks.scale(0.9)
thanks.next_to(black_rect.get_bottom(), UP, SMALL_BUFF)
thanks.set_color(YELLOW)
underline = Line(LEFT, RIGHT)
underline.set_width(thanks.get_width() + MED_SMALL_BUFF)
underline.match_width(thanks)
underline.scale(1.1)
underline.next_to(thanks, DOWN, SMALL_BUFF)
thanks.add(underline)
@ -233,13 +240,22 @@ class PatreonEndScreen(PatreonThanks):
thanks.to_edge(RIGHT)
columns.next_to(thanks, DOWN, 3 * LARGE_BUFF)
self.add(columns, black_rect, line, thanks)
self.play(
columns.move_to, 2 * DOWN, DOWN,
columns.align_to, thanks, {"alignment_vect": RIGHT},
rate_func=None,
run_time=self.run_time,
columns.generate_target()
columns.target.move_to(2 * DOWN, DOWN)
columns.target.align_to(
thanks, alignment_vect=RIGHT
)
vect = columns.target.get_center() - columns.get_center()
distance = get_norm(vect)
wait_time = 20
columns_shift = ContinualMovement(
columns,
direction=normalize(vect),
rate=(distance / wait_time)
)
self.add(columns_shift, black_rect, line, thanks)
self.wait(wait_time)
class LogoGenerationTemplate(MovingCameraScene):

View file

@ -481,6 +481,19 @@ class DashedLine(Line):
return self.end
class Elbow(VMobject):
CONFIG = {
"width": 0.2,
"angle": 0,
}
def __init__(self, **kwargs):
VMobject.__init__(self, **kwargs)
self.set_points_as_corners([UP, UP + RIGHT, RIGHT])
self.set_width(self.width, about_point=ORIGIN)
self.rotate(self.angle, about_point=ORIGIN)
class Arrow(Line):
CONFIG = {
"tip_length": 0.25,

View file

@ -18,7 +18,6 @@ from utils.iterables import list_update
from utils.iterables import remove_list_redundancies
from utils.paths import straight_path
from utils.space_ops import angle_of_vector
from utils.space_ops import complex_to_R3
from utils.space_ops import rotation_matrix
from utils.simple_functions import get_num_args
from utils.space_ops import get_norm
@ -270,10 +269,15 @@ class Mobject(Container):
return self
def apply_complex_function(self, function, **kwargs):
return self.apply_function(
lambda x_y_z: complex_to_R3(function(complex(x_y_z[0], x_y_z[1]))),
**kwargs
)
def R3_func(point):
x, y, z = point
xy_complex = function(complex(x, y))
return [
xy_complex.real,
xy_complex.imag,
z
]
return self.apply_function(R3_func)
def wag(self, direction=RIGHT, axis=DOWN, wag_factor=1.0):
for mob in self.family_members_with_points():

View file

@ -42,7 +42,7 @@ class ParametricSurface(VGroup):
if self.should_make_jagged:
self.make_jagged()
def setup_in_uv_space(self):
def get_u_values_and_v_values(self):
res = tuplify(self.resolution)
if len(res) == 1:
u_res = v_res = res[0]
@ -55,9 +55,14 @@ class ParametricSurface(VGroup):
u_values = np.linspace(u_min, u_max, u_res + 1)
v_values = np.linspace(v_min, v_max, v_res + 1)
return u_values, v_values
def setup_in_uv_space(self):
u_values, v_values = self.get_u_values_and_v_values()
faces = VGroup()
for i in range(u_res):
for j in range(v_res):
for i in range(len(u_values) - 1):
for j in range(len(v_values) - 1):
u1, u2 = u_values[i:i + 2]
v1, v2 = v_values[j:j + 2]
face = ThreeDVMobject()

View file

@ -41,3 +41,13 @@ class ExponentialValueTracker(ValueTracker):
def set_value(self, value):
return ValueTracker.set_value(self, np.log(value))
class ComplexValueTracker(ValueTracker):
def get_value(self):
return complex(*self.points[0, :2])
def set_value(self, z):
z = complex(z)
self.points[0, :2] = (z.real, z.imag)
return self

339
old_projects/for_flammy.py Normal file
View file

@ -0,0 +1,339 @@
from big_ol_pile_of_manim_imports import *
from old_projects.sphere_area import *
class MadAtMathologer(PiCreatureScene):
def create_pi_creature(self):
return Mortimer().to_corner(DR)
def construct(self):
morty = self.pi_creature
self.play(morty.change, "angry")
self.wait(3)
self.play(morty.change, "heistant")
self.wait(2)
self.play(morty.change, "shruggie")
self.wait(3)
class JustTheIntegral(Scene):
def construct(self):
tex = TexMobject("\\int_0^{\\pi / 2} \\cos(\\theta)d\\theta")
tex.scale(2)
self.add(tex)
class SphereVideoWrapper(Scene):
def construct(self):
title = TextMobject("Surface area of a sphere")
title.scale(1.5)
title.to_edge(UP)
rect = ScreenRectangle(height=6)
rect.next_to(title, DOWN)
self.add(title)
self.play(ShowCreation(rect))
self.wait()
class SphereRings(SecondProof):
CONFIG = {
"sphere_config": {
"resolution": (60, 60),
},
}
def construct(self):
self.setup_shapes()
self.grow_rings()
self.show_one_ring()
self.show_radial_line()
self.show_thickness()
self.flash_through_rings()
def grow_rings(self):
sphere = self.sphere
rings = self.rings
north_rings = rings[:len(rings) // 2]
sphere.set_fill(opacity=0)
sphere.set_stroke(WHITE, 0.5, opacity=0.5)
southern_mesh = VGroup(*[
face.copy() for face in sphere
if face.get_center()[2] < 0
])
southern_mesh.set_stroke(WHITE, 0.1, 0.5)
self.play(Write(sphere))
self.wait()
self.play(
FadeOut(sphere),
FadeIn(southern_mesh),
FadeIn(north_rings),
)
self.wait(4)
self.north_rings = north_rings
self.southern_mesh = southern_mesh
def show_one_ring(self):
north_rings = self.north_rings
index = len(north_rings) // 2
ring = north_rings[index]
to_fade = VGroup(*[
nr for nr in north_rings
if nr is not ring
])
north_rings.save_state()
circle = Circle()
circle.set_stroke(PINK, 5)
circle.set_width(ring.get_width())
circle.move_to(ring, IN)
thickness = ring.get_depth() * np.sqrt(2)
brace = Brace(Line(ORIGIN, 0.2 * RIGHT), UP)
brace.set_width(thickness)
brace.rotate(90 * DEGREES, RIGHT)
brace.rotate(45 * DEGREES, UP)
brace.move_to(1.5 * (RIGHT + OUT))
brace.set_stroke(WHITE, 1)
word = TextMobject("Thickness")
word.rotate(90 * DEGREES, RIGHT)
word.next_to(brace, RIGHT + OUT, buff=0)
self.play(
to_fade.set_fill, {"opacity": 0.2},
to_fade.set_stroke, {"opacity": 0.0},
)
self.move_camera(
phi=0, theta=-90 * DEGREES,
run_time=2,
)
self.stop_ambient_camera_rotation()
self.play(ShowCreation(circle))
self.play(FadeOut(circle))
self.move_camera(
phi=70 * DEGREES,
theta=-100 * DEGREES,
run_time=2,
)
self.begin_ambient_camera_rotation(0.02)
self.play(
GrowFromCenter(brace),
Write(word),
)
self.wait(2)
self.play(FadeOut(VGroup(brace, word)))
self.circum_circle = circle
self.thickness_label = VGroup(brace, word)
self.ring = ring
def show_radial_line(self):
ring = self.ring
point = ring.get_corner(RIGHT + IN)
R_line = Line(ORIGIN, point)
xy_line = Line(ORIGIN, self.sphere.get_right())
theta = np.arccos(np.dot(
normalize(R_line.get_vector()),
normalize(xy_line.get_vector())
))
arc = Arc(angle=theta, radius=0.5)
arc.rotate(90 * DEGREES, RIGHT, about_point=ORIGIN)
theta = TexMobject("\\theta")
theta.rotate(90 * DEGREES, RIGHT)
theta.next_to(arc, RIGHT)
theta.shift(SMALL_BUFF * (LEFT + OUT))
R_label = TexMobject("R")
R_label.rotate(90 * DEGREES, RIGHT)
R_label.next_to(
R_line.get_center(), OUT + LEFT,
buff=SMALL_BUFF
)
VGroup(R_label, R_line).set_color(YELLOW)
z_axis_point = np.array(point)
z_axis_point[:2] = 0
r_line = DashedLine(z_axis_point, point)
r_line.set_color(RED)
r_label = TexMobject("R\\cos(\\theta)")
r_label.rotate(90 * DEGREES, RIGHT)
r_label.scale(0.7)
r_label.match_color(r_line)
r_label.set_stroke(width=0, background=True)
r_label.next_to(r_line, OUT, 0.5 * SMALL_BUFF)
VGroup(
R_label, xy_line, arc, R_label,
r_line, r_label,
).set_shade_in_3d(True)
# self.stop_ambient_camera_rotation()
self.move_camera(
phi=85 * DEGREES,
theta=-100 * DEGREES,
added_anims=[
ring.set_fill, {"opacity": 0.5},
ring.set_stroke, {"opacity": 0.1},
ShowCreation(R_line),
FadeInFrom(R_label, IN),
]
)
self.wait()
self.play(
FadeIn(xy_line),
ShowCreation(arc),
Write(theta),
)
self.wait()
self.play(
ShowCreation(r_line),
FadeInFrom(r_label, IN),
)
self.wait()
self.move_camera(
phi=70 * DEGREES,
theta=-110 * DEGREES,
run_time=3
)
self.wait(2)
def show_thickness(self):
brace, word = self.thickness_label
R_dtheta = TexMobject("R \\, d\\theta")
R_dtheta.rotate(90 * DEGREES, RIGHT)
R_dtheta.move_to(word, LEFT)
self.play(
GrowFromCenter(brace),
Write(R_dtheta)
)
self.wait(3)
def flash_through_rings(self):
rings = self.north_rings.copy()
rings.fade(1)
rings.sort_submobjects(lambda p: p[2])
for x in range(8):
self.play(LaggedStart(
ApplyMethod, rings,
lambda m: (m.set_fill, PINK, 0.5),
rate_func=there_and_back,
lag_ratio=0.1,
run_time=2,
))
class IntegralSymbols(Scene):
def construct(self):
int_sign = TexMobject("\\displaystyle \\int")
int_sign.set_height(1.5)
int_sign.move_to(5 * LEFT)
circumference, times, thickness = ctt = TextMobject(
"circumference", "$\\times$", "thickness"
)
circumference.set_color(MAROON_B)
ctt.next_to(int_sign, RIGHT, SMALL_BUFF)
area_brace = Brace(ctt, DOWN)
area_text = area_brace.get_text("Area of a ring")
all_rings = TextMobject("All rings")
all_rings.scale(0.5)
all_rings.next_to(int_sign, DOWN, SMALL_BUFF)
all_rings.shift(SMALL_BUFF * LEFT)
circum_formula = TexMobject(
"2\\pi", "R\\cos(\\theta)",
)
circum_formula[1].set_color(RED)
circum_formula.move_to(circumference)
circum_brace = Brace(circum_formula, UP)
R_dtheta = TexMobject("R \\, d\\theta")
R_dtheta.move_to(thickness, LEFT)
R_dtheta_brace = Brace(R_dtheta, UP)
zero, pi_halves = bounds = TexMobject("0", "\\pi / 2")
bounds.scale(0.5)
zero.move_to(all_rings)
pi_halves.next_to(int_sign, UP, SMALL_BUFF)
pi_halves.shift(SMALL_BUFF * RIGHT)
self.add(int_sign)
self.play(
GrowFromCenter(area_brace),
FadeInFrom(area_text, UP),
)
self.wait()
self.play(FadeInFromDown(circumference))
self.play(
FadeInFromDown(thickness),
Write(times)
)
self.play(Write(all_rings))
self.wait()
self.play(
circumference.next_to, circum_brace, UP, MED_SMALL_BUFF,
circumference.shift, SMALL_BUFF * UR,
GrowFromCenter(circum_brace),
)
self.play(FadeInFrom(circum_formula, UP))
self.wait()
self.play(
thickness.next_to, circumference, RIGHT, MED_SMALL_BUFF,
GrowFromCenter(R_dtheta_brace),
area_brace.stretch, 0.84, 0, {"about_edge": LEFT},
MaintainPositionRelativeTo(area_text, area_brace),
)
self.play(FadeInFrom(R_dtheta, UP))
self.wait()
self.play(ReplacementTransform(all_rings, bounds))
self.wait()
# RHS
rhs = TexMobject(
"\\displaystyle =", "2\\pi R^2", "\\int_0^{\\pi / 2}",
"\\cos(\\theta)", "d\\theta",
)
rhs.set_color_by_tex("cos", RED)
rhs.next_to(R_dtheta, RIGHT)
int_brace = Brace(rhs[2:], DOWN)
q_marks = int_brace.get_text("???")
one = TexMobject("1")
one.move_to(q_marks)
self.play(FadeInFrom(rhs, 4 * LEFT))
self.wait()
self.play(CircleThenFadeAround(rhs[1]))
self.wait()
self.play(CircleThenFadeAround(rhs[2:]))
self.wait()
self.play(
GrowFromCenter(int_brace),
LaggedStart(
FadeInFrom, q_marks,
lambda m: (m, UP),
)
)
self.wait()
self.play(ReplacementTransform(q_marks, one))
self.wait()
class ShamelessPlug(TeacherStudentsScene):
def construct(self):
self.student_says(
"But why $4\\pi R^2$?",
target_mode="maybe"
)
self.change_student_modes(
"erm", "maybe", "happy",
added_anims=[self.teacher.change, "happy"]
)
self.wait(3)

View file

@ -1,47 +1,4 @@
from big_ol_pile_of_manim_imports import *
# from pprint import pprint
# Helpers
def get_three_d_scene_config(high_quality=True):
hq_config = {
"camera_config": {
"should_apply_shading": True,
"exponential_projection": True,
},
"three_d_axes_config": {
"num_axis_pieces": 1,
"number_line_config": {
"unit_size": 2,
# "tick_frequency": 0.5,
"tick_frequency": 1,
"numbers_with_elongated_ticks": [0, 1, 2],
"stroke_width": 2,
}
},
"sphere_config": {
"radius": 2,
"resolution": (24, 48),
}
}
lq_added_config = {
"camera_config": {
"should_apply_shading": False,
},
"three_d_axes_config": {
"num_axis_pieces": 1,
},
"sphere_config": {
"resolution": (12, 24),
}
}
if high_quality:
return hq_config
else:
return merge_config([
lq_added_config,
hq_config
])
def q_mult(q1, q2):
@ -459,53 +416,6 @@ class RubiksCube(VGroup):
# Abstract scenes
class SpecialThreeDScene(ThreeDScene):
CONFIG = {
"cut_axes_at_radius": True,
}
def __init__(self, **kwargs):
digest_config(self, kwargs)
if self.frame_duration == PRODUCTION_QUALITY_FRAME_DURATION:
high_quality = True
else:
high_quality = False
default_config = get_three_d_scene_config(high_quality)
config = merge_config([self.CONFIG, kwargs, default_config])
ThreeDScene.__init__(self, **config)
def get_axes(self):
axes = ThreeDAxes(**self.three_d_axes_config)
for axis in axes:
if self.cut_axes_at_radius:
p0 = axis.main_line.get_start()
p1 = axis.number_to_point(-1)
p2 = axis.number_to_point(1)
p3 = axis.main_line.get_end()
new_pieces = VGroup(
Line(p0, p1), Line(p1, p2), Line(p2, p3),
)
for piece in new_pieces:
piece.shade_in_3d = True
new_pieces.match_style(axis.pieces)
axis.pieces.submobjects = new_pieces.submobjects
for tick in axis.tick_marks:
tick.add(VectorizedPoint(
1.5 * tick.get_center(),
))
return axes
def get_sphere(self):
return Sphere(**self.sphere_config)
def get_default_camera_position(self):
return {
"phi": 70 * DEGREES,
"theta": -110 * DEGREES,
}
# Animated scenes
class ManyNumberSystems(Scene):

3708
old_projects/sphere_area.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -3265,18 +3265,23 @@ class Thumbnail(ZetaTransformationScene):
"anchor_density" : 35
}
def construct(self):
self.y_min = -4
self.y_max = 4
self.x_min = 1
self.x_max = int(FRAME_X_RADIUS+2)
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.add_reflected_plane()
self.apply_zeta_function()
# self.apply_zeta_function()
self.plane.set_stroke(width = 4)
div_sum = TexMobject("-\\frac{1}{12} = ", "1+2+3+4+\\cdots")
div_sum.set_width(FRAME_WIDTH-1)
div_sum.to_edge(DOWN)
div_sum.set_color(YELLOW)
for mob in div_sum.submobjects:
mob.add_to_back(BackgroundRectangle(mob))
div_sum.set_background_stroke(width=8)
# for mob in div_sum.submobjects:
# mob.add_to_back(BackgroundRectangle(mob))
zeta = TexMobject("\\zeta(s)")
zeta.set_height(FRAME_Y_RADIUS-1)
@ -3286,7 +3291,7 @@ class Thumbnail(ZetaTransformationScene):
million.set_width(FRAME_X_RADIUS+1)
million.to_edge(UP+RIGHT)
million.set_color(GREEN_B)
million.add_background_rectangle()
million.set_background_stroke(width=8)
self.add(div_sum, million, zeta)

View file

@ -1,15 +1,26 @@
from constants import *
from constants import DEGREES
from constants import PRODUCTION_QUALITY_FRAME_DURATION
from continual_animation.update import ContinualGrowValue
from animation.transform import ApplyMethod
from camera.three_d_camera import ThreeDCamera
from mobject.coordinate_systems import ThreeDAxes
from mobject.geometry import Line
from mobject.three_dimensions import Sphere
from mobject.types.vectorized_mobject import VGroup
from mobject.types.vectorized_mobject import VectorizedPoint
from scene.scene import Scene
from utils.config_ops import digest_config
from utils.config_ops import merge_config
class ThreeDScene(Scene):
CONFIG = {
"camera_class": ThreeDCamera,
"ambient_camera_rotation": None,
"default_angled_camera_orientation_kwargs": {
"phi": 70 * DEGREES,
"theta": -135 * DEGREES,
}
}
def set_camera_orientation(self, phi=None, theta=None, distance=None, gamma=None):
@ -22,7 +33,7 @@ class ThreeDScene(Scene):
if gamma is not None:
self.camera.set_gamma(gamma)
def begin_ambient_camera_rotation(self, rate=0.05):
def begin_ambient_camera_rotation(self, rate=0.02):
self.ambient_camera_rotation = ContinualGrowValue(
self.camera.theta_tracker,
rate=rate
@ -86,3 +97,90 @@ class ThreeDScene(Scene):
def remove_fixed_in_frame_mobjects(self, *mobjects):
self.camera.remove_fixed_in_frame_mobjects(*mobjects)
##
def set_to_default_angled_camera_orientation(self, **kwargs):
config = dict(self.default_camera_orientation_kwargs)
config.update(kwargs)
self.set_camera_orientation(**config)
class SpecialThreeDScene(ThreeDScene):
CONFIG = {
"cut_axes_at_radius": True,
"camera_config": {
"should_apply_shading": True,
"exponential_projection": True,
},
"three_d_axes_config": {
"num_axis_pieces": 1,
"number_line_config": {
"unit_size": 2,
"tick_frequency": 1,
"numbers_with_elongated_ticks": [0, 1, 2],
"stroke_width": 2,
}
},
"sphere_config": {
"radius": 2,
"resolution": (24, 48),
},
"default_angled_camera_position": {
"phi": 70 * DEGREES,
"theta": -110 * DEGREES,
},
# When scene is extracted with -l flag, this
# configuration will override the above configuration.
"low_quality_config": {
"camera_config": {
"should_apply_shading": False,
},
"three_d_axes_config": {
"num_axis_pieces": 1,
},
"sphere_config": {
"resolution": (12, 24),
}
}
}
def __init__(self, **kwargs):
digest_config(self, kwargs)
if self.frame_duration == PRODUCTION_QUALITY_FRAME_DURATION:
config = {}
else:
config = self.low_quality_config
ThreeDScene.__init__(self, **merge_config([kwargs, config]))
def get_axes(self):
axes = ThreeDAxes(**self.three_d_axes_config)
for axis in axes:
if self.cut_axes_at_radius:
p0 = axis.main_line.get_start()
p1 = axis.number_to_point(-1)
p2 = axis.number_to_point(1)
p3 = axis.main_line.get_end()
new_pieces = VGroup(
Line(p0, p1), Line(p1, p2), Line(p2, p3),
)
for piece in new_pieces:
piece.shade_in_3d = True
new_pieces.match_style(axis.pieces)
axis.pieces.submobjects = new_pieces.submobjects
for tick in axis.tick_marks:
tick.add(VectorizedPoint(
1.5 * tick.get_center(),
))
return axes
def get_sphere(self, **kwargs):
config = merge_config([kwargs, self.sphere_config])
return Sphere(**config)
def get_default_camera_position(self):
return self.default_angled_camera_position
def set_camera_to_default_position(self):
self.set_camera_orientation(
**self.default_angled_camera_position
)

View file

@ -57,6 +57,13 @@ def digest_config(obj, kwargs, caller_locals={}):
def merge_config(all_dicts):
"""
Creates a dict whose keyset is the union of all the
input dictionaries. The value for each key is based
on the first dict in the list with that key.
When values are dictionaries, it is applied recursively
"""
all_config = reduce(op.add, [list(d.items()) for d in all_dicts])
config = dict()
for c in all_config: