2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.constants import *
|
|
|
|
from manimlib.mobject.geometry import Square
|
|
|
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
|
|
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
2020-02-05 14:47:14 -08:00
|
|
|
from manimlib.utils.iterables import listify
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.utils.space_ops import z_to_vector
|
2018-04-01 10:45:41 -07:00
|
|
|
|
|
|
|
##############
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2020-02-07 09:32:38 -08:00
|
|
|
# TODO, replace these with a special 3d type, not VMobject
|
|
|
|
|
|
|
|
|
2018-08-15 16:23:29 -07:00
|
|
|
class ThreeDVMobject(VMobject):
|
2018-08-21 19:58:48 -07:00
|
|
|
CONFIG = {
|
|
|
|
"shade_in_3d": True,
|
|
|
|
}
|
2018-08-15 16:23:29 -07:00
|
|
|
|
|
|
|
|
|
|
|
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,
|
2018-08-15 16:23:29 -07:00
|
|
|
"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],
|
2018-08-15 16:23:29 -07:00
|
|
|
"stroke_color": LIGHT_GREY,
|
|
|
|
"stroke_width": 0.5,
|
|
|
|
"should_make_jagged": False,
|
2018-08-28 09:44:32 -07:00
|
|
|
"pre_function_handle_to_anchor_scale_factor": 0.00001,
|
2018-08-15 16:23:29 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-08-15 16:23:29 -07:00
|
|
|
def __init__(self, func, **kwargs):
|
|
|
|
VGroup.__init__(self, **kwargs)
|
2019-06-20 13:26:22 -07:00
|
|
|
self.func = func
|
2018-08-15 16:23:29 -07:00
|
|
|
self.setup_in_uv_space()
|
|
|
|
self.apply_function(lambda p: func(p[0], p[1]))
|
|
|
|
if self.should_make_jagged:
|
|
|
|
self.make_jagged()
|
|
|
|
|
2018-11-27 13:39:37 -08:00
|
|
|
def get_u_values_and_v_values(self):
|
2020-02-05 14:47:14 -08:00
|
|
|
res = listify(self.resolution)
|
2018-08-16 22:25:51 -07:00
|
|
|
if len(res) == 1:
|
2018-08-27 16:33:06 -07:00
|
|
|
u_res = v_res = res[0]
|
2018-08-16 22:25:51 -07:00
|
|
|
else:
|
|
|
|
u_res, v_res = res
|
2018-08-15 16:23:29 -07:00
|
|
|
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)
|
2018-11-27 13:39:37 -08:00
|
|
|
|
|
|
|
return u_values, v_values
|
|
|
|
|
|
|
|
def setup_in_uv_space(self):
|
|
|
|
u_values, v_values = self.get_u_values_and_v_values()
|
2018-08-15 16:23:29 -07:00
|
|
|
faces = VGroup()
|
2018-11-27 13:39:37 -08:00
|
|
|
for i in range(len(u_values) - 1):
|
|
|
|
for j in range(len(v_values) - 1):
|
2018-08-16 22:25:51 -07:00
|
|
|
u1, u2 = u_values[i:i + 2]
|
|
|
|
v1, v2 = v_values[j:j + 2]
|
|
|
|
face = ThreeDVMobject()
|
|
|
|
face.set_points_as_corners([
|
2018-08-15 16:23:29 -07:00
|
|
|
[u1, v1, 0],
|
|
|
|
[u2, v1, 0],
|
|
|
|
[u2, v2, 0],
|
|
|
|
[u1, v2, 0],
|
|
|
|
[u1, v1, 0],
|
|
|
|
])
|
2018-08-16 22:25:51 -07:00
|
|
|
faces.add(face)
|
|
|
|
face.u_index = i
|
|
|
|
face.v_index = j
|
2018-08-28 09:44:32 -07:00
|
|
|
face.u1 = u1
|
|
|
|
face.u2 = u2
|
|
|
|
face.v1 = v1
|
|
|
|
face.v2 = v2
|
2018-08-15 16:23:29 -07:00
|
|
|
faces.set_fill(
|
|
|
|
color=self.fill_color,
|
|
|
|
opacity=self.fill_opacity
|
|
|
|
)
|
2018-08-15 17:30:24 -07:00
|
|
|
faces.set_stroke(
|
2018-08-15 16:23:29 -07:00
|
|
|
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)
|
2018-08-15 16:23:29 -07:00
|
|
|
|
2018-08-16 22:25:51 -07:00
|
|
|
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)
|
2018-08-15 16:23:29 -07:00
|
|
|
|
|
|
|
|
|
|
|
# Specific shapes
|
|
|
|
|
|
|
|
|
|
|
|
class Sphere(ParametricSurface):
|
|
|
|
CONFIG = {
|
2018-08-16 22:25:51 -07:00
|
|
|
"resolution": (12, 24),
|
2018-08-22 21:23:24 -07:00
|
|
|
"radius": 1,
|
2018-08-15 16:23:29 -07:00
|
|
|
"u_min": 0.001,
|
2018-08-16 22:25:51 -07:00
|
|
|
"u_max": PI - 0.001,
|
|
|
|
"v_min": 0,
|
|
|
|
"v_max": TAU,
|
2018-08-15 16:23:29 -07:00
|
|
|
}
|
2018-04-01 10:45:41 -07:00
|
|
|
|
2018-08-15 16:23:29 -07:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
ParametricSurface.__init__(
|
|
|
|
self, self.func, **kwargs
|
|
|
|
)
|
|
|
|
self.scale(self.radius)
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-08-15 16:23:29 -07:00
|
|
|
def func(self, u, v):
|
|
|
|
return np.array([
|
2018-08-16 22:25:51 -07:00
|
|
|
np.cos(v) * np.sin(u),
|
|
|
|
np.sin(v) * np.sin(u),
|
|
|
|
np.cos(u)
|
2018-08-15 16:23:29 -07:00
|
|
|
])
|
2018-04-01 10:45:41 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-08-15 16:23:29 -07:00
|
|
|
class Cube(VGroup):
|
2018-04-01 10:45:41 -07:00
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"fill_opacity": 0.75,
|
|
|
|
"fill_color": BLUE,
|
|
|
|
"stroke_width": 0,
|
|
|
|
"side_length": 2,
|
2018-04-01 10:45:41 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2020-02-11 19:55:00 -08:00
|
|
|
def init_points(self):
|
2018-04-01 10:45:41 -07:00
|
|
|
for vect in IN, OUT, LEFT, RIGHT, UP, DOWN:
|
2018-08-22 21:23:24 -07:00
|
|
|
face = Square(
|
|
|
|
side_length=self.side_length,
|
|
|
|
shade_in_3d=True,
|
2018-08-15 16:23:29 -07:00
|
|
|
)
|
|
|
|
face.flip()
|
2018-04-06 13:58:59 -07:00
|
|
|
face.shift(self.side_length * OUT / 2.0)
|
2018-08-15 16:23:29 -07:00
|
|
|
face.apply_matrix(z_to_vector(vect))
|
2018-04-01 10:45:41 -07:00
|
|
|
|
|
|
|
self.add(face)
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-04-01 10:45:41 -07:00
|
|
|
class Prism(Cube):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"dimensions": [3, 2, 1]
|
2018-04-01 10:45:41 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2020-02-11 19:55:00 -08:00
|
|
|
def init_points(self):
|
|
|
|
Cube.init_points(self)
|
2018-04-01 10:45:41 -07:00
|
|
|
for dim, value in enumerate(self.dimensions):
|
2018-04-06 13:58:59 -07:00
|
|
|
self.rescale_to_fit(value, dim, stretch=True)
|