2022-02-15 14:37:15 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2021-01-13 09:20:11 -10:00
|
|
|
import itertools as it
|
2022-02-15 14:37:15 +08:00
|
|
|
|
|
|
|
import numpy as np
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2022-04-12 19:19:59 +08:00
|
|
|
from manimlib.constants import DEFAULT_MOBJECT_TO_MOBJECT_BUFFER
|
|
|
|
from manimlib.constants import DOWN, LEFT, RIGHT, UP
|
|
|
|
from manimlib.constants import WHITE
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.mobject.numbers import DecimalNumber
|
|
|
|
from manimlib.mobject.numbers import Integer
|
|
|
|
from manimlib.mobject.shape_matchers import BackgroundRectangle
|
2021-01-18 08:20:14 -10:00
|
|
|
from manimlib.mobject.svg.tex_mobject import Tex
|
|
|
|
from manimlib.mobject.svg.tex_mobject import TexText
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
|
|
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
from typing import TYPE_CHECKING
|
2022-02-16 21:08:25 +08:00
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
if TYPE_CHECKING:
|
2022-12-15 20:09:52 -08:00
|
|
|
from typing import Sequence
|
2022-04-12 19:19:59 +08:00
|
|
|
import numpy.typing as npt
|
2022-02-15 14:37:15 +08:00
|
|
|
from manimlib.mobject.mobject import Mobject
|
2022-12-16 20:19:18 -08:00
|
|
|
from manimlib.typing import ManimColor, np_vector
|
2022-02-15 14:37:15 +08:00
|
|
|
|
|
|
|
|
2018-04-01 10:21:42 -07:00
|
|
|
VECTOR_LABEL_SCALE_FACTOR = 0.8
|
|
|
|
|
2018-04-06 13:08:57 -07:00
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def matrix_to_tex_string(matrix: npt.ArrayLike) -> str:
|
2018-11-21 08:44:48 -05:00
|
|
|
matrix = np.array(matrix).astype("str")
|
2018-04-01 10:21:42 -07:00
|
|
|
if matrix.ndim == 1:
|
|
|
|
matrix = matrix.reshape((matrix.size, 1))
|
|
|
|
n_rows, n_cols = matrix.shape
|
2022-12-15 20:09:52 -08:00
|
|
|
prefix = R"\left[ \begin{array}{%s}" % ("c" * n_cols)
|
|
|
|
suffix = R"\end{array} \right]"
|
2018-04-01 10:21:42 -07:00
|
|
|
rows = [
|
|
|
|
" & ".join(row)
|
|
|
|
for row in matrix
|
|
|
|
]
|
2022-12-15 20:09:52 -08:00
|
|
|
return prefix + R" \\ ".join(rows) + suffix
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2018-04-06 13:08:57 -07:00
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def matrix_to_mobject(matrix: npt.ArrayLike) -> Tex:
|
2021-01-18 08:20:14 -10:00
|
|
|
return Tex(matrix_to_tex_string(matrix))
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2018-04-06 13:08:57 -07:00
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def vector_coordinate_label(
|
|
|
|
vector_mob: VMobject,
|
|
|
|
integer_labels: bool = True,
|
|
|
|
n_dim: int = 2,
|
|
|
|
color: ManimColor = WHITE
|
|
|
|
) -> Matrix:
|
2018-04-01 10:21:42 -07:00
|
|
|
vect = np.array(vector_mob.get_end())
|
|
|
|
if integer_labels:
|
|
|
|
vect = np.round(vect).astype(int)
|
|
|
|
vect = vect[:n_dim]
|
|
|
|
vect = vect.reshape((n_dim, 1))
|
2018-04-26 17:11:00 -07:00
|
|
|
label = Matrix(vect, add_background_rectangles_to_entries=True)
|
2018-04-01 10:21:42 -07:00
|
|
|
label.scale(VECTOR_LABEL_SCALE_FACTOR)
|
|
|
|
|
|
|
|
shift_dir = np.array(vector_mob.get_end())
|
2018-04-06 13:08:57 -07:00
|
|
|
if shift_dir[0] >= 0: # Pointing right
|
|
|
|
shift_dir -= label.get_left() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER * LEFT
|
|
|
|
else: # Pointing left
|
|
|
|
shift_dir -= label.get_right() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER * RIGHT
|
2018-04-01 10:21:42 -07:00
|
|
|
label.shift(shift_dir)
|
|
|
|
label.set_color(color)
|
|
|
|
label.rect = BackgroundRectangle(label)
|
|
|
|
label.add_to_back(label.rect)
|
|
|
|
return label
|
|
|
|
|
2018-04-06 13:08:57 -07:00
|
|
|
|
2018-04-01 10:21:42 -07:00
|
|
|
class Matrix(VMobject):
|
2022-12-15 20:09:52 -08:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
matrix: Sequence[Sequence[str | float | VMobject]],
|
|
|
|
v_buff: float = 0.8,
|
|
|
|
h_buff: float = 1.3,
|
|
|
|
bracket_h_buff: float = 0.2,
|
|
|
|
bracket_v_buff: float = 0.25,
|
|
|
|
add_background_rectangles_to_entries: bool = False,
|
|
|
|
include_background_rectangle: bool = False,
|
|
|
|
element_config: dict = dict(),
|
|
|
|
element_alignment_corner: np_vector = DOWN,
|
|
|
|
**kwargs
|
|
|
|
):
|
2018-04-01 10:21:42 -07:00
|
|
|
"""
|
2021-10-12 09:04:30 +08:00
|
|
|
Matrix can either include numbers, tex_strings,
|
2018-04-01 10:21:42 -07:00
|
|
|
or mobjects
|
|
|
|
"""
|
2022-12-15 20:09:52 -08:00
|
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
|
|
mob_matrix = self.matrix_to_mob_matrix(matrix, **element_config)
|
|
|
|
self.mob_matrix = mob_matrix
|
|
|
|
|
|
|
|
self.organize_mob_matrix(mob_matrix, v_buff, h_buff, element_alignment_corner)
|
2021-01-13 09:20:11 -10:00
|
|
|
self.elements = VGroup(*it.chain(*mob_matrix))
|
2018-04-24 16:06:14 -07:00
|
|
|
self.add(self.elements)
|
2022-12-15 20:09:52 -08:00
|
|
|
self.add_brackets(bracket_v_buff, bracket_h_buff)
|
2018-04-01 10:21:42 -07:00
|
|
|
self.center()
|
2022-12-15 20:09:52 -08:00
|
|
|
if add_background_rectangles_to_entries:
|
2018-04-24 16:06:14 -07:00
|
|
|
for mob in self.elements:
|
2018-04-01 10:21:42 -07:00
|
|
|
mob.add_background_rectangle()
|
2022-12-15 20:09:52 -08:00
|
|
|
if include_background_rectangle:
|
2018-04-27 13:50:08 -07:00
|
|
|
self.add_background_rectangle()
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2022-12-15 20:09:52 -08:00
|
|
|
|
|
|
|
def element_to_mobject(self, element: str | float, **config) -> Tex:
|
|
|
|
return Tex(str(element), **config)
|
|
|
|
|
|
|
|
def matrix_to_mob_matrix(self, matrix: npt.ArrayLike, **config) -> list[list[VMobject]]:
|
2021-01-13 09:20:11 -10:00
|
|
|
return [
|
|
|
|
[
|
2022-12-15 20:09:52 -08:00
|
|
|
self.element_to_mobject(item, **config)
|
2021-01-13 09:20:11 -10:00
|
|
|
for item in row
|
|
|
|
]
|
|
|
|
for row in matrix
|
|
|
|
]
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2022-12-15 20:09:52 -08:00
|
|
|
def organize_mob_matrix(
|
|
|
|
self,
|
|
|
|
matrix: list[list[Mobject]],
|
|
|
|
v_buff: float,
|
|
|
|
h_buff: float,
|
|
|
|
aligned_corner: np_vector,
|
|
|
|
):
|
2018-04-01 10:21:42 -07:00
|
|
|
for i, row in enumerate(matrix):
|
|
|
|
for j, elem in enumerate(row):
|
|
|
|
mob = matrix[i][j]
|
2018-04-24 16:06:14 -07:00
|
|
|
mob.move_to(
|
2022-12-15 20:09:52 -08:00
|
|
|
i * v_buff * DOWN + j * h_buff * RIGHT,
|
|
|
|
aligned_corner
|
2018-04-24 16:06:14 -07:00
|
|
|
)
|
2018-04-01 10:21:42 -07:00
|
|
|
return self
|
|
|
|
|
2022-12-15 20:09:52 -08:00
|
|
|
def add_brackets(self, v_buff: float, h_buff: float):
|
2022-11-03 16:31:19 -07:00
|
|
|
height = len(self.mob_matrix)
|
2022-12-15 20:09:52 -08:00
|
|
|
brackets = Tex("".join((
|
|
|
|
R"\left[\begin{array}{c}",
|
|
|
|
*height * [R"\quad \\"],
|
|
|
|
R"\end{array}\right]",
|
|
|
|
)))[0]
|
|
|
|
brackets.set_height(self.get_height() + v_buff)
|
|
|
|
l_bracket = brackets[:len(brackets) // 2]
|
|
|
|
r_bracket = brackets[len(brackets) // 2:]
|
|
|
|
l_bracket.next_to(self, LEFT, h_buff)
|
|
|
|
r_bracket.next_to(self, RIGHT, h_buff)
|
|
|
|
brackets.set_submobjects([l_bracket, r_bracket])
|
|
|
|
self.brackets = brackets
|
|
|
|
self.add(*brackets)
|
2018-04-01 10:21:42 -07:00
|
|
|
return self
|
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def get_columns(self) -> VGroup:
|
2018-09-27 17:37:25 -07:00
|
|
|
return VGroup(*[
|
2021-01-13 09:20:11 -10:00
|
|
|
VGroup(*[row[i] for row in self.mob_matrix])
|
|
|
|
for i in range(len(self.mob_matrix[0]))
|
2018-09-27 17:37:25 -07:00
|
|
|
])
|
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def get_rows(self) -> VGroup:
|
2021-03-27 11:56:58 -07:00
|
|
|
return VGroup(*[
|
|
|
|
VGroup(*row)
|
|
|
|
for row in self.mob_matrix
|
|
|
|
])
|
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def set_column_colors(self, *colors: ManimColor):
|
2018-09-27 17:37:25 -07:00
|
|
|
columns = self.get_columns()
|
|
|
|
for color, column in zip(colors, columns):
|
|
|
|
column.set_color(color)
|
2018-04-01 10:21:42 -07:00
|
|
|
return self
|
|
|
|
|
|
|
|
def add_background_to_entries(self):
|
|
|
|
for mob in self.get_entries():
|
|
|
|
mob.add_background_rectangle()
|
|
|
|
return self
|
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def get_mob_matrix(self) -> list[list[Mobject]]:
|
2018-04-01 10:21:42 -07:00
|
|
|
return self.mob_matrix
|
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def get_entries(self) -> VGroup:
|
2021-01-13 09:20:11 -10:00
|
|
|
return self.elements
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def get_brackets(self) -> VGroup:
|
2018-04-01 10:21:42 -07:00
|
|
|
return self.brackets
|
2018-04-24 16:06:14 -07:00
|
|
|
|
|
|
|
|
|
|
|
class DecimalMatrix(Matrix):
|
2022-12-15 20:09:52 -08:00
|
|
|
def element_to_mobject(self, element: float, num_decimal_places: int = 1, **config) -> DecimalNumber:
|
|
|
|
return DecimalNumber(element, num_decimal_places=num_decimal_places, **config)
|
2018-04-24 16:06:14 -07:00
|
|
|
|
|
|
|
|
|
|
|
class IntegerMatrix(Matrix):
|
2022-12-15 20:09:52 -08:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
matrix: npt.ArrayLike,
|
|
|
|
element_alignment_corner: np_vector = UP,
|
|
|
|
**kwargs
|
|
|
|
):
|
|
|
|
super().__init__(matrix, element_alignment_corner=element_alignment_corner, **kwargs)
|
|
|
|
|
|
|
|
def element_to_mobject(self, element: int, **config) -> Integer:
|
|
|
|
return Integer(element, **config)
|
2018-04-24 16:06:14 -07:00
|
|
|
|
|
|
|
|
|
|
|
class MobjectMatrix(Matrix):
|
2022-12-15 20:09:52 -08:00
|
|
|
def element_to_mobject(self, element: VMobject, **config) -> VMobject:
|
|
|
|
return element
|
2018-04-24 16:06:14 -07:00
|
|
|
|
|
|
|
|
2022-02-15 14:37:15 +08:00
|
|
|
def get_det_text(
|
|
|
|
matrix: Matrix,
|
|
|
|
determinant: int | str | None = None,
|
|
|
|
background_rect: bool = False,
|
|
|
|
initial_scale_factor: int = 2
|
|
|
|
) -> VGroup:
|
2021-01-18 08:20:14 -10:00
|
|
|
parens = Tex("(", ")")
|
2018-04-25 17:27:43 -07:00
|
|
|
parens.scale(initial_scale_factor)
|
2018-04-24 16:06:14 -07:00
|
|
|
parens.stretch_to_fit_height(matrix.get_height())
|
|
|
|
l_paren, r_paren = parens.split()
|
|
|
|
l_paren.next_to(matrix, LEFT, buff=0.1)
|
|
|
|
r_paren.next_to(matrix, RIGHT, buff=0.1)
|
2021-01-18 08:20:14 -10:00
|
|
|
det = TexText("det")
|
2018-04-29 18:03:09 -07:00
|
|
|
det.scale(initial_scale_factor)
|
|
|
|
det.next_to(l_paren, LEFT, buff=0.1)
|
2018-04-24 16:06:14 -07:00
|
|
|
if background_rect:
|
|
|
|
det.add_background_rectangle()
|
2019-03-16 22:12:31 -07:00
|
|
|
det_text = VGroup(det, l_paren, r_paren)
|
2018-04-24 16:06:14 -07:00
|
|
|
if determinant is not None:
|
2021-01-18 08:20:14 -10:00
|
|
|
eq = Tex("=")
|
2018-04-24 16:06:14 -07:00
|
|
|
eq.next_to(r_paren, RIGHT, buff=0.1)
|
2021-01-18 08:20:14 -10:00
|
|
|
result = Tex(str(determinant))
|
2018-04-24 16:06:14 -07:00
|
|
|
result.next_to(eq, RIGHT, buff=0.2)
|
|
|
|
det_text.add(eq, result)
|
|
|
|
return det_text
|