3b1b-manim/mobject/three_dimensions.py

151 lines
3.8 KiB
Python
Raw Normal View History

2018-08-09 17:56:05 -07:00
2018-04-01 10:51:54 -07:00
from constants import *
2018-04-01 10:51:54 -07:00
from mobject.types.vectorized_mobject import VMobject
from mobject.types.vectorized_mobject import VGroup
2018-04-01 10:51:54 -07:00
from mobject.geometry import Square
from utils.iterables import tuplify
from utils.space_ops import z_to_vector
##############
class ThreeDVMobject(VMobject):
2018-08-21 19:58:48 -07:00
CONFIG = {
"shade_in_3d": True,
}
class ParametricSurface(VGroup):
CONFIG = {
"u_min": 0,
"u_max": 1,
"v_min": 0,
"v_max": 1,
2018-08-15 17:30:24 -07:00
"resolution": 32,
"surface_piece_config": {},
"fill_color": BLUE_D,
"fill_opacity": 1.0,
2018-08-15 17:30:24 -07:00
"checkerboard_colors": [BLUE_D, BLUE_E],
"stroke_color": LIGHT_GREY,
"stroke_width": 0.5,
"should_make_jagged": False,
}
def __init__(self, func, **kwargs):
VGroup.__init__(self, **kwargs)
self.setup_in_uv_space()
self.apply_function(lambda p: func(p[0], p[1]))
if self.should_make_jagged:
self.make_jagged()
def setup_in_uv_space(self):
res = tuplify(self.resolution)
if len(res) == 1:
u_res = v_res = res
else:
u_res, v_res = res
u_min = self.u_min
u_max = self.u_max
v_min = self.v_min
v_max = self.v_max
u_values = np.linspace(u_min, u_max, u_res + 1)
v_values = np.linspace(v_min, v_max, v_res + 1)
faces = VGroup()
for i in range(u_res):
for j in range(v_res):
u1, u2 = u_values[i:i + 2]
v1, v2 = v_values[j:j + 2]
face = ThreeDVMobject()
face.set_points_as_corners([
[u1, v1, 0],
[u2, v1, 0],
[u2, v2, 0],
[u1, v2, 0],
[u1, v1, 0],
])
faces.add(face)
face.u_index = i
face.v_index = j
faces.set_fill(
color=self.fill_color,
opacity=self.fill_opacity
)
2018-08-15 17:30:24 -07:00
faces.set_stroke(
color=self.stroke_color,
width=self.stroke_width,
opacity=self.stroke_opacity,
)
2018-08-15 17:30:24 -07:00
self.add(*faces)
if self.checkerboard_colors:
self.set_fill_by_checkerboard(*self.checkerboard_colors)
def set_fill_by_checkerboard(self, *colors, opacity=None):
n_colors = len(colors)
for face in self:
c_index = (face.u_index + face.v_index) % n_colors
face.set_fill(colors[c_index], opacity=opacity)
# Specific shapes
class Sphere(ParametricSurface):
CONFIG = {
"resolution": (12, 24),
"radius": 3,
"u_min": 0.001,
"u_max": PI - 0.001,
"v_min": 0,
"v_max": TAU,
}
def __init__(self, **kwargs):
ParametricSurface.__init__(
self, self.func, **kwargs
)
self.scale(self.radius)
def func(self, u, v):
return np.array([
np.cos(v) * np.sin(u),
np.sin(v) * np.sin(u),
np.cos(u)
])
class Cube(VGroup):
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 = ThreeDVMobject(
Square(side_length=self.side_length)
)
face.make_jagged()
face.flip()
face.shift(self.side_length * OUT / 2.0)
face.apply_matrix(z_to_vector(vect))
self.add(face)
class Prism(Cube):
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)