2020-06-09 21:24:42 -07:00
|
|
|
import math
|
|
|
|
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.constants import *
|
2021-02-04 12:36:35 -08:00
|
|
|
from manimlib.mobject.types.surface import Surface
|
2020-06-05 13:37:06 -07:00
|
|
|
from manimlib.mobject.types.surface import SGroup
|
2020-06-06 13:23:30 -07:00
|
|
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
|
|
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
2021-08-19 08:37:01 -07:00
|
|
|
from manimlib.mobject.geometry import Square
|
2021-11-08 21:47:26 -08:00
|
|
|
from manimlib.utils.bezier import interpolate
|
2020-06-05 13:24:26 -07:00
|
|
|
from manimlib.utils.config_ops import digest_config
|
|
|
|
from manimlib.utils.space_ops import get_norm
|
|
|
|
from manimlib.utils.space_ops import z_to_vector
|
2021-08-19 08:37:01 -07:00
|
|
|
from manimlib.utils.space_ops import compass_directions
|
2018-04-01 10:45:41 -07:00
|
|
|
|
|
|
|
|
2020-06-06 13:23:30 -07:00
|
|
|
class SurfaceMesh(VGroup):
|
|
|
|
CONFIG = {
|
2021-11-08 21:47:26 -08:00
|
|
|
"resolution": (21, 11),
|
2020-06-06 13:23:30 -07:00
|
|
|
"stroke_width": 1,
|
2021-11-08 21:47:26 -08:00
|
|
|
"normal_nudge": 1e-2,
|
2020-06-15 12:00:37 -07:00
|
|
|
"depth_test": True,
|
2021-01-08 22:26:47 -08:00
|
|
|
"flat_stroke": False,
|
2020-06-06 13:23:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, uv_surface, **kwargs):
|
2021-02-04 12:36:35 -08:00
|
|
|
if not isinstance(uv_surface, Surface):
|
|
|
|
raise Exception("uv_surface must be of type Surface")
|
2020-06-06 13:23:30 -07:00
|
|
|
self.uv_surface = uv_surface
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
|
|
def init_points(self):
|
|
|
|
uv_surface = self.uv_surface
|
|
|
|
|
|
|
|
full_nu, full_nv = uv_surface.resolution
|
|
|
|
part_nu, part_nv = self.resolution
|
2021-11-08 21:47:26 -08:00
|
|
|
# 'indices' are treated as floats. Later, there will be
|
|
|
|
# an interpolation between the floor and ceiling of these
|
|
|
|
# indices
|
|
|
|
u_indices = np.linspace(0, full_nu, part_nu)
|
|
|
|
v_indices = np.linspace(0, full_nv, part_nv)
|
2020-06-06 13:23:30 -07:00
|
|
|
|
|
|
|
points, du_points, dv_points = uv_surface.get_surface_points_and_nudged_points()
|
|
|
|
normals = uv_surface.get_unit_normals()
|
2021-07-28 07:32:16 -07:00
|
|
|
nudge = self.normal_nudge
|
2020-06-06 13:23:30 -07:00
|
|
|
nudged_points = points + nudge * normals
|
|
|
|
|
|
|
|
for ui in u_indices:
|
|
|
|
path = VMobject()
|
2021-11-08 21:47:26 -08:00
|
|
|
# full_ui = full_nv * ui
|
|
|
|
# path.set_points_smoothly(
|
|
|
|
# nudged_points[full_ui:full_ui + full_nv]
|
|
|
|
# )
|
|
|
|
low_ui = full_nv * int(math.floor(ui))
|
|
|
|
high_ui = full_nv * int(math.ceil(ui))
|
|
|
|
path.set_points_smoothly(interpolate(
|
|
|
|
nudged_points[low_ui:low_ui + full_nv],
|
|
|
|
nudged_points[high_ui:high_ui + full_nv],
|
|
|
|
ui % 1
|
|
|
|
))
|
2020-06-06 13:23:30 -07:00
|
|
|
self.add(path)
|
|
|
|
for vi in v_indices:
|
|
|
|
path = VMobject()
|
2021-11-08 21:47:26 -08:00
|
|
|
# path.set_points_smoothly(
|
|
|
|
# nudged_points[vi::full_nv]
|
|
|
|
# )
|
|
|
|
path.set_points_smoothly(interpolate(
|
|
|
|
nudged_points[int(math.floor(vi))::full_nv],
|
|
|
|
nudged_points[int(math.ceil(vi))::full_nv],
|
|
|
|
vi % 1
|
|
|
|
))
|
2020-06-06 13:23:30 -07:00
|
|
|
self.add(path)
|
|
|
|
|
|
|
|
|
2021-02-04 12:36:35 -08:00
|
|
|
# 3D shapes
|
2018-08-15 16:23:29 -07:00
|
|
|
|
2021-02-04 12:36:35 -08:00
|
|
|
class Sphere(Surface):
|
2018-08-15 16:23:29 -07:00
|
|
|
CONFIG = {
|
2020-06-06 13:23:30 -07:00
|
|
|
"resolution": (101, 51),
|
2018-08-22 21:23:24 -07:00
|
|
|
"radius": 1,
|
2020-06-04 17:17:38 -07:00
|
|
|
"u_range": (0, TAU),
|
|
|
|
"v_range": (0, PI),
|
2018-08-15 16:23:29 -07:00
|
|
|
}
|
2018-04-01 10:45:41 -07:00
|
|
|
|
2020-06-05 13:24:26 -07:00
|
|
|
def uv_func(self, u, v):
|
2020-06-04 15:41:20 -07:00
|
|
|
return self.radius * np.array([
|
2020-06-04 17:17:38 -07:00
|
|
|
np.cos(u) * np.sin(v),
|
|
|
|
np.sin(u) * np.sin(v),
|
2020-06-05 11:12:52 -07:00
|
|
|
-np.cos(v)
|
2018-08-15 16:23:29 -07:00
|
|
|
])
|
2018-04-01 10:45:41 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2021-02-04 12:36:35 -08:00
|
|
|
class Torus(Surface):
|
2020-06-17 17:12:23 -07:00
|
|
|
CONFIG = {
|
|
|
|
"u_range": (0, TAU),
|
|
|
|
"v_range": (0, TAU),
|
|
|
|
"r1": 3,
|
|
|
|
"r2": 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
def uv_func(self, u, v):
|
|
|
|
P = np.array([math.cos(u), math.sin(u), 0])
|
|
|
|
return (self.r1 - self.r2 * math.cos(v)) * P - math.sin(v) * OUT
|
|
|
|
|
|
|
|
|
2021-02-04 12:36:35 -08:00
|
|
|
class Cylinder(Surface):
|
2020-06-05 11:12:52 -07:00
|
|
|
CONFIG = {
|
2020-06-05 13:24:26 -07:00
|
|
|
"height": 2,
|
|
|
|
"radius": 1,
|
|
|
|
"axis": OUT,
|
|
|
|
"u_range": (0, TAU),
|
|
|
|
"v_range": (-1, 1),
|
2020-06-06 13:23:30 -07:00
|
|
|
"resolution": (101, 11),
|
2020-06-05 13:24:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def init_points(self):
|
|
|
|
super().init_points()
|
|
|
|
self.scale(self.radius)
|
|
|
|
self.set_depth(self.height, stretch=True)
|
|
|
|
self.apply_matrix(z_to_vector(self.axis))
|
|
|
|
return self
|
2020-06-05 11:12:52 -07:00
|
|
|
|
2020-06-05 13:24:26 -07:00
|
|
|
def uv_func(self, u, v):
|
|
|
|
return [np.cos(u), np.sin(u), v]
|
|
|
|
|
|
|
|
|
|
|
|
class Line3D(Cylinder):
|
|
|
|
CONFIG = {
|
|
|
|
"width": 0.05,
|
2020-06-18 16:25:48 -07:00
|
|
|
"resolution": (21, 25)
|
2020-06-05 11:12:52 -07:00
|
|
|
}
|
|
|
|
|
2020-06-05 13:24:26 -07:00
|
|
|
def __init__(self, start, end, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
|
|
|
axis = end - start
|
|
|
|
super().__init__(
|
|
|
|
height=get_norm(axis),
|
|
|
|
radius=self.width / 2,
|
|
|
|
axis=axis
|
|
|
|
)
|
2020-06-17 17:12:23 -07:00
|
|
|
self.shift((start + end) / 2)
|
2020-06-05 11:12:52 -07:00
|
|
|
|
|
|
|
|
2021-02-04 12:36:35 -08:00
|
|
|
class Disk3D(Surface):
|
2018-04-01 10:45:41 -07:00
|
|
|
CONFIG = {
|
2020-06-05 13:24:26 -07:00
|
|
|
"radius": 1,
|
|
|
|
"u_range": (0, 1),
|
|
|
|
"v_range": (0, TAU),
|
2020-06-06 13:23:30 -07:00
|
|
|
"resolution": (2, 25),
|
2020-06-05 13:24:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def init_points(self):
|
|
|
|
super().init_points()
|
|
|
|
self.scale(self.radius)
|
|
|
|
|
|
|
|
def uv_func(self, u, v):
|
|
|
|
return [
|
|
|
|
u * np.cos(v),
|
|
|
|
u * np.sin(v),
|
|
|
|
0
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2021-02-04 12:36:35 -08:00
|
|
|
class Square3D(Surface):
|
2020-06-05 13:37:06 -07:00
|
|
|
CONFIG = {
|
|
|
|
"side_length": 2,
|
|
|
|
"u_range": (-1, 1),
|
|
|
|
"v_range": (-1, 1),
|
2020-06-06 13:23:30 -07:00
|
|
|
"resolution": (2, 2),
|
2020-06-05 13:37:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def init_points(self):
|
|
|
|
super().init_points()
|
|
|
|
self.scale(self.side_length / 2)
|
|
|
|
|
|
|
|
def uv_func(self, u, v):
|
|
|
|
return [u, v, 0]
|
|
|
|
|
|
|
|
|
|
|
|
class Cube(SGroup):
|
2020-06-05 13:24:26 -07:00
|
|
|
CONFIG = {
|
|
|
|
"color": BLUE,
|
|
|
|
"opacity": 1,
|
|
|
|
"gloss": 0.5,
|
2020-06-06 13:23:30 -07:00
|
|
|
"square_resolution": (2, 2),
|
2018-04-06 13:58:59 -07:00
|
|
|
"side_length": 2,
|
2021-08-19 08:37:01 -07:00
|
|
|
"square_class": Square3D,
|
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):
|
2021-08-19 08:37:01 -07:00
|
|
|
face = Square3D(
|
|
|
|
resolution=self.square_resolution,
|
|
|
|
side_length=self.side_length,
|
|
|
|
)
|
|
|
|
self.add(*self.square_to_cube_faces(face))
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def square_to_cube_faces(square):
|
|
|
|
radius = square.get_height() / 2
|
|
|
|
square.move_to(radius * OUT)
|
|
|
|
result = [square]
|
|
|
|
result.extend([
|
|
|
|
square.copy().rotate(PI / 2, axis=vect, about_point=ORIGIN)
|
|
|
|
for vect in compass_directions(4)
|
|
|
|
])
|
|
|
|
result.append(square.copy().rotate(PI, RIGHT, about_point=ORIGIN))
|
|
|
|
return result
|
|
|
|
|
|
|
|
def _get_face(self):
|
|
|
|
return Square3D(resolution=self.square_resolution)
|
|
|
|
|
|
|
|
|
|
|
|
class VCube(VGroup):
|
|
|
|
CONFIG = {
|
|
|
|
"fill_color": BLUE_D,
|
|
|
|
"fill_opacity": 1,
|
|
|
|
"stroke_width": 0,
|
|
|
|
"gloss": 0.5,
|
|
|
|
"shadow": 0.5,
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, side_length=2, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
face = Square(side_length=side_length)
|
|
|
|
face.get_triangulation()
|
|
|
|
self.add(*Cube.square_to_cube_faces(face))
|
|
|
|
self.init_colors()
|
|
|
|
self.apply_depth_test()
|
|
|
|
self.refresh_unit_normal()
|
2018-04-01 10:45:41 -07:00
|
|
|
|
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)
|