+
+.. manim:: MyScene
+ :hide_source:
+ :quality: low
+
+ from manim_data_structures import *
+
+ class MyScene(Scene):
+ def construct(self):
+ arr = MArray([1, 2, 3], label='Array', arr_label_pos=MArrayDirection.DOWN)
+ self.add(arr)
+ self.wait(1)
+ self.play(*arr.append_elem(4, append_anim=GrowFromCenter))
+ self.wait(1)
+
+.. currentmodule:: manim_data_structures.m_enum
+
+.. note::
+
+ You can also specify arguments to the passed animation via the ``append_anim_args`` parameter and also set the target of the animation using the ``append_anim_target`` parameter that takes in :py:class:`MArrayElementComp` enum.
+
+Remove Element
+^^^^^^^^^^^^^^
+
+.. currentmodule:: manim_data_structures.m_array
+
+To remove an element simply invoke the :py:class:`MArray.remove_elem` method with the index of element you wish to remove. The method returns two the removal animation and a function that udpates the indices of the remaining elements.
+
+.. code-block:: python
+ :linenos:
+
+ (remove_anim, update_indices) = arr.remove_elem(1)
+ self.play(remove_anim)
+ self.play(*update_indices())
+
+.. raw:: html
+
+
+
+.. manim:: MyScene
+ :hide_source:
+ :quality: low
+
+ from manim_data_structures import *
+
+ class MyScene(Scene):
+ def construct(self):
+ arr = MArray([1, 2, 3], label='Array', arr_label_pos=MArrayDirection.DOWN)
+ self.add(arr)
+ self.wait(1)
+ (remove_anim, update_indices) = arr.remove_elem(1)
+ self.play(remove_anim)
+ self.play(*update_indices())
+ self.wait(1)
+
+Similar to how you were able to pass the append animation to the :py:class:`MArray.append_elem` function, you can specify two animations for the :py:class:`MArray.remove_elem` method:
+1. Element removal animation via the ``removal_anim`` parameter.
+2. Indices update animation via the ``update_anim`` parameter.
+
+The code snippet below provides an example:
+
+.. code-block:: python
+ :linenos:
+
+ (remove_anim, update_indices) = arr.remove_elem(1, removal_anim=ShowPassingFlash , update_anim=Indicate)
+
+.. raw:: html
+
+
+
+.. manim:: MyScene
+ :hide_source:
+ :quality: low
+
+ from manim_data_structures import *
+
+ class MyScene(Scene):
+ def construct(self):
+ arr = MArray([1, 2, 3], label='Array', arr_label_pos=MArrayDirection.DOWN)
+ self.add(arr)
+ self.wait(1)
+ (remove_anim, update_indices) = arr.remove_elem(1, removal_anim=ShowPassingFlash , update_anim=Indicate)
+ self.play(remove_anim)
+ self.play(*update_indices())
+ self.wait(1)
+
+.. note::
+
+ You can also specify arguments to the passed animation via the ``*_anim_args`` parameter and also set the target of the animation using the ``*_anim_target`` parameter.
+
Update Element
^^^^^^^^^^^^^^
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 33ca3f4..9a27f02 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -26,6 +26,32 @@ To import the package in your script, add the following import statement:
from manim_data_structures import *
+Variables
+~~~~~~~~~
+
+.. manim:: VarScene
+ :save_last_frame:
+
+ from manim_data_structures import *
+
+ class VarScene(Scene):
+ def construct(self):
+ var = MVariable(10, 0, 'Var')
+ self.add(var)
+
+Arrays
+~~~~~~
+
+.. manim:: ArrayScene
+ :save_last_frame:
+
+ from manim_data_structures import *
+
+ class ArrayScene(Scene):
+ def construct(self):
+ arr = MArray([1, 2, 3], label='Arr')
+ self.add(arr)
+
Next Steps
----------
diff --git a/docs/source/reference/arrays.rst b/docs/source/reference/arrays.rst
index ef7cf36..8b054cf 100644
--- a/docs/source/reference/arrays.rst
+++ b/docs/source/reference/arrays.rst
@@ -6,5 +6,5 @@ Arrays
.. autosummary::
:toctree: generated
- ~array.MArrayElement
- ~array.MArray
+ ~m_array.MArrayElement
+ ~m_array.MArray
diff --git a/docs/source/reference/enums.rst b/docs/source/reference/enums.rst
index db73378..105e0f5 100644
--- a/docs/source/reference/enums.rst
+++ b/docs/source/reference/enums.rst
@@ -7,3 +7,4 @@ Enums
:toctree: generated
~m_enum.MArrayDirection
+ ~m_enum.MArrayElementComp
diff --git a/docs/source/reference/index.rst b/docs/source/reference/index.rst
index 5618f02..c093185 100644
--- a/docs/source/reference/index.rst
+++ b/docs/source/reference/index.rst
@@ -9,5 +9,6 @@ Module Index
.. toctree::
:maxdepth: 2
+ variables
arrays
enums
diff --git a/docs/source/reference/variables.rst b/docs/source/reference/variables.rst
new file mode 100644
index 0000000..af05f0b
--- /dev/null
+++ b/docs/source/reference/variables.rst
@@ -0,0 +1,9 @@
+Variables
+=========
+
+.. currentmodule:: manim_data_structures
+
+.. autosummary::
+ :toctree: generated
+
+ ~m_variable.MVariable
diff --git a/pyproject.toml b/pyproject.toml
index 9c29220..7cc1eb6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "manim-data-structures"
-version = "0.1.4"
+version = "0.1.5"
description = "A Manim implementation for data structures"
authors = ["Hammad Nasir "]
readme = "README.md"
diff --git a/src/manim_data_structures/__init__.py b/src/manim_data_structures/__init__.py
index bc8e73b..e8d6b71 100644
--- a/src/manim_data_structures/__init__.py
+++ b/src/manim_data_structures/__init__.py
@@ -1,6 +1,13 @@
-__version__ = "0.1.4"
+__version__ = "0.1.5"
-from .array import *
+from .m_array import *
from .m_enum import *
+from .m_variable import *
-__all__ = ["MArrayElement", "MArray", "MArrayDirection"]
+__all__ = [
+ "MArrayElement",
+ "MArray",
+ "MArrayDirection",
+ "MArrayElementComp",
+ "MVariable",
+]
diff --git a/src/manim_data_structures/array.py b/src/manim_data_structures/m_array.py
similarity index 52%
rename from src/manim_data_structures/array.py
rename to src/manim_data_structures/m_array.py
index 226dd60..ef555bd 100644
--- a/src/manim_data_structures/array.py
+++ b/src/manim_data_structures/m_array.py
@@ -3,7 +3,7 @@
import numpy as np
from manim import *
-from .m_enum import MArrayDirection
+from .m_enum import MArrayDirection, MArrayElementComp
class MArrayElement(VGroup):
@@ -21,6 +21,10 @@ class MArrayElement(VGroup):
Specifies the position of :attr:`__mob_index`
index_gap : :class:`float`, default: `0.25`
Specifies the distance between :attr:`__mob_square` and :attr:`__mob_index`
+ label_pos : :class:`np.ndarray`, default: `LEFT`
+ Specifies the position of :attr:`__mob_label`
+ label_gap : :class:`float`, default: `0.5`
+ Specifies the distance between :attr:`__mob_square` and :attr:`__mob_label`
next_to_mob : :class:`MArrayElement`, default: `None`
Specifies placement for :attr:`__mob_square`
next_to_dir : :class:`np.ndarray`, default: `RIGHT`
@@ -44,9 +48,19 @@ class MArrayElement(VGroup):
Specifies the position of :attr:`__mob_index`
__index_gap : :class:`float`
Specifies the distance between :attr:`__mob_square` and :attr:`__mob_index`
+ __label_pos : :class:`np.ndarray`, default: `LEFT`
+ Specifies the position of :attr:`__mob_label`
+ __label_gap : :class:`float`, default: `0.5`
+ Specifies the distance between :attr:`__mob_square` and :attr:`__mob_label`
"""
- def __init_props(self, index_pos: np.ndarray, index_gap: float) -> None:
+ def __init_props(
+ self,
+ index_pos: np.ndarray,
+ index_gap: float,
+ label_pos: np.ndarray,
+ label_gap: float,
+ ) -> None:
"""Initializes the attributes for the class.
Parameters
@@ -55,6 +69,10 @@ def __init_props(self, index_pos: np.ndarray, index_gap: float) -> None:
Specifies the position of :attr:`__mob_index`
index_gap : :class:`float`
Specifies the distance between :attr:`__mob_square` and :attr:`__mob_index`
+ label_pos : :class:`np.ndarray`
+ Specifies the position of :attr:`__mob_label`
+ label_gap : :class:`float`
+ Specifies the distance between :attr:`__mob_square` and :attr:`__mob_label`
"""
self.__mob_square_props = {
@@ -65,14 +83,18 @@ def __init_props(self, index_pos: np.ndarray, index_gap: float) -> None:
}
self.__mob_value_props = {"text": "", "color": WHITE, "weight": BOLD}
self.__mob_index_props = {"text": "", "color": BLUE_D, "font_size": 32}
+ self.__mob_label_props = {"text": "", "color": BLUE_A, "font_size": 38}
self.__index_pos = index_pos
self.__index_gap = index_gap
+ self.__label_pos = label_pos
+ self.__label_gap = label_gap
def __update_props(
self,
mob_square_args: dict = {},
mob_value_args: dict = {},
mob_index_args: dict = {},
+ mob_label_args: dict = {},
) -> None:
"""Updates the attributes of the class.
@@ -84,11 +106,14 @@ def __update_props(
Arguments for :class:`manim.Text` that represents the element value.
mob_index_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Text` that represents the element index.
+ mob_label_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element label.
"""
self.__mob_square_props.update(mob_square_args)
self.__mob_value_props.update(mob_value_args)
self.__mob_index_props.update(mob_index_args)
+ self.__mob_label_props.update(mob_label_args)
if type(self.__mob_value_props["text"]) != str:
self.__mob_value_props["text"] = str(self.__mob_value_props["text"])
@@ -96,11 +121,15 @@ def __update_props(
if type(self.__mob_index_props["text"]) != str:
self.__mob_index_props["text"] = str(self.__mob_index_props["text"])
+ if type(self.__mob_label_props["text"]) != str:
+ self.__mob_label_props["text"] = str(self.__mob_label_props["text"])
+
def __init_mobs(
self,
init_square: bool = False,
init_value: bool = False,
init_index: bool = False,
+ init_label: bool = False,
next_to_mob: "MArrayElement" = None,
next_to_dir: np.ndarray = RIGHT,
) -> None:
@@ -114,6 +143,8 @@ def __init_mobs(
Instantiates a :class:`manim.Text` and adds it to :attr:`__mob_value`.
init_index : :class:`bool`, default: `False`
Instantiates a :class:`manim.Text` and adds it to :attr:`__mob_index`.
+ init_label : :class:`bool`, default: `False`
+ Instantiates a :class:`manim.Text` and adds it to :attr:`__mob_label`.
next_to_mob : :class:`MArrayElement`, default: `None`
Specifies placement for :attr:`__mob_square`
next_to_dir : :class:`np.ndarray`, default: `RIGHT`
@@ -140,13 +171,23 @@ def __init_mobs(
)
self.add(self.__mob_index)
+ if init_label:
+ self.__mob_label = Text(**self.__mob_label_props)
+ self.__mob_label.next_to(
+ self.__mob_square, self.__label_pos, self.__label_gap
+ )
+ self.add(self.__mob_label)
+
def __init__(
self,
mob_square_args: dict = {},
mob_value_args: dict = {},
mob_index_args: dict = {},
+ mob_label_args: dict = {},
index_pos: np.ndarray = UP,
index_gap: float = 0.25,
+ label_pos: np.ndarray = LEFT,
+ label_gap: float = 0.5,
next_to_mob: "MArrayElement" = None,
next_to_dir: np.ndarray = RIGHT,
**kwargs
@@ -161,26 +202,34 @@ def __init__(
Arguments for :class:`manim.Text` that represents the element value.
mob_index_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Text` that represents the element index.
+ mob_label_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element label.
index_pos : :class:`np.ndarray`, default: `UP`
- Specifies the position of :attr:`__mob_index`
+ Specifies the position of :attr:`__mob_index`.
index_gap : :class:`float`, default: `0.25`
- Specifies the distance between :attr:`__mob_square` and :attr:`__mob_index`
+ Specifies the distance between :attr:`__mob_square` and :attr:`__mob_index`.
+ label_pos : :class:`np.ndarray`, default: `LEFT`
+ Specifies the position of :attr:`__mob_label`.
+ label_gap : :class:`float`, default: `0.5`
+ Specifies the distance between :attr:`__mob_square` and :attr:`__mob_label`
next_to_mob : :class:`MArrayElement`, default: `None`
- Specifies placement for :attr:`__mob_square`
+ Specifies placement for :attr:`__mob_square`.
next_to_dir : :class:`np.ndarray`, default: `RIGHT`
- Specifies direction of placement for :attr:`__mob_square`
+ Specifies direction of placement for :attr:`__mob_square`.
"""
super().__init__(**kwargs)
# Initialize props
- self.__init_props(index_pos, index_gap)
+ self.__init_props(index_pos, index_gap, label_pos, label_gap)
# Update props
- self.__update_props(mob_square_args, mob_value_args, mob_index_args)
+ self.__update_props(
+ mob_square_args, mob_value_args, mob_index_args, mob_label_args
+ )
# Initialize mobjects
- self.__init_mobs(True, True, True, next_to_mob, next_to_dir)
+ self.__init_mobs(True, True, True, True, next_to_mob, next_to_dir)
def fetch_mob_square(self) -> Square:
"""Fetches the :class:`manim.Square` that represents the element body.
@@ -215,6 +264,42 @@ def fetch_mob_index(self) -> Text:
return self.__mob_index
+ def fetch_mob_label(self) -> Text:
+ """Fetches the :class:`manim.Text` that represents the element label.
+
+ Returns
+ -------
+ :class:`manim.Text`
+ Represents the element label.
+ """
+
+ return self.__mob_label
+
+ def fetch_mob(self, mob_target: MArrayElementComp) -> Mobject:
+ """Fetches :class:`manim.Mobject` based on enum :class:`m_enum.MArrayElementComp`.
+
+ Parameters
+ ----------
+ mob_target : :class:`m_enum.MArrayElementComp`
+ Specifies the component of :class:`MArrayElement` to fetch.
+
+ Returns
+ -------
+ :class:`manim.Mobject`
+ Represents the component of :class:`MArrayElement`.
+ """
+
+ if mob_target == MArrayElementComp.BODY:
+ return self.fetch_mob_square()
+ elif mob_target == MArrayElementComp.VALUE:
+ return self.fetch_mob_value()
+ elif mob_target == MArrayElementComp.INDEX:
+ return self.fetch_mob_index()
+ elif mob_target == MArrayElementComp.LABEL:
+ return self.fetch_mob_label()
+ else:
+ return self
+
def update_mob_value(self, mob_value_args: dict = {}) -> Text:
"""Re-intializes the :class:`manim.Text` that represents the element value.
@@ -255,6 +340,26 @@ def update_mob_index(self, mob_index_args: dict = {}) -> Text:
self.add(self.__mob_index)
return self.__mob_index
+ def update_mob_label(self, mob_label_args: dict = {}) -> Text:
+ """Re-intializes the :class:`manim.Text` that represents the element label.
+
+ Parameters
+ ----------
+ mob_label_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element label.
+
+ Returns
+ -------
+ :class:`manim.Text`
+ Represents the updated element label.
+ """
+
+ self.__update_props(mob_label_args=mob_label_args)
+ self.remove(self.__mob_label)
+ self.__init_mobs(init_label=True)
+ self.add(self.__mob_label)
+ return self.__mob_label
+
def animate_mob_square(self) -> "_AnimationBuilder": # type: ignore
"""Invokes the :meth:`manim.Square.animate` property of :class:`manim.Square` for the element body.
@@ -288,6 +393,17 @@ def animate_mob_index(self) -> "_AnimationBuilder": # type: ignore
return self.__mob_index.animate
+ def animate_mob_label(self) -> "_AnimationBuilder": # type: ignore
+ """Invokes the :meth:`manim.Text.animate` property of :class:`manim.Text` for the element label.
+
+ Returns
+ -------
+ :class:`_AnimationBuilder`
+ Value returned by :meth:`manim.Text.animate` property of :class:`manim.Text`.
+ """
+
+ return self.__mob_label.animate
+
class MArray(VGroup):
"""A class that represents an array.
@@ -331,6 +447,7 @@ class MArray(VGroup):
Specifies whether to display indices or not.
__arr_dir : :class:`.m_enum.MArrayDirection`, default: :attr:`.m_enum.MArrayDirection.RIGHT`
Specifies the growing direction of array.
+ __mob_arr_label_props
"""
__dir_map = [
@@ -341,6 +458,127 @@ class MArray(VGroup):
]
"""Maps :class:`.m_enum.MArrayDirection` to correct :class:`MArrayElement` placement."""
+ def __init_props(self) -> None:
+ """Initializes the attributes for the class."""
+
+ self.__mob_arr_label_props = {"text": "", "color": BLUE_A, "font_size": 38}
+
+ def __update_props(
+ self,
+ label: str = "",
+ mob_arr_label_args: dict = {},
+ ) -> None:
+ """Updates the attributes of the class.
+
+ Parameters
+ ----------
+ label : :class:`str`, default `''`
+ Specifies the textual value for :attr:`__mob_arr_label`
+ mob_arr_label_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the array label.
+ """
+
+ self.__mob_arr_label_props["text"] = label
+ self.__mob_arr_label_props.update(mob_arr_label_args)
+
+ if type(self.__mob_arr_label_props["text"]) != str:
+ self.__mob_arr_label_props["text"] = str(self.__mob_arr_label_props["text"])
+
+ def __sum_elem_len(self, index_start: int, index_end: int) -> int:
+ """Sums the length of :class:`manim.Square` elements between the specified bound.
+
+ Parameters
+ ----------
+ index_start : :class:`int`
+ Starting index of the bound (inclusive).
+ index_end : :class:`int`
+ Ending index of the bound (inclusive).
+
+ Returns
+ -------
+ :class:`int`
+ Total length of the elements.
+ """
+
+ if (
+ index_start < 0
+ or index_end < 0
+ or index_start > len(self.__mob_arr)
+ or index_end > len(self.__mob_arr)
+ ):
+ raise Exception("Index out of bounds!")
+
+ total_len = 0
+ for i in range(index_start, index_end + 1):
+ total_len += self.__mob_arr[i].fetch_mob_square().side_length
+ return total_len
+
+ def __calc_label_pos_and_mob(self) -> typing.Tuple[Square, np.ndarray]:
+ """Calculates the position of the label relative to :class:`MArrayElement` 's :class:`manim.Square` and returns them.
+
+ Returns
+ -------
+ :class:`Manim.Square`
+ Represents the :class:`manim.Mobject` next to which the label is positioned.
+ :class:`np.ndarray`
+ Represents the relative label's position.
+ """
+
+ # Label position is parallel to array growth direction
+ if np.array_equal(
+ self.__dir_map[self.__arr_label_pos.value]["arr"],
+ self.__dir_map[self.__arr_dir.value]["arr"],
+ ):
+ return (
+ self.__mob_arr[-1].fetch_mob_square(),
+ self.__dir_map[self.__arr_label_pos.value]["arr"],
+ )
+ elif np.array_equal(
+ self.__dir_map[self.__arr_label_pos.value]["arr"],
+ -self.__dir_map[self.__arr_dir.value]["arr"],
+ ):
+ return (
+ self.__mob_arr[0].fetch_mob_square(),
+ self.__dir_map[self.__arr_label_pos.value]["arr"],
+ )
+
+ # Label position is perpendicular to array growth direction
+ else:
+ middle_index = len_before = len_after = 0
+ if len(self.__mob_arr) > 1:
+ middle_index = int(len(self.__mob_arr) / 2)
+ len_before = self.__sum_elem_len(0, middle_index - 1)
+ len_after = self.__sum_elem_len(
+ middle_index + 1, len(self.__mob_arr) - 1
+ )
+ return (
+ self.__mob_arr[middle_index].fetch_mob_square(),
+ self.__dir_map[self.__arr_label_pos.value]["arr"]
+ + self.__dir_map[self.__arr_dir.value]["arr"]
+ * ((len_after - len_before) / 2),
+ )
+
+ def __init_mobs(
+ self,
+ init_arr_label: bool = False,
+ ) -> None:
+ """Initializes the :class:`Mobject`s for the class.
+
+ Parameters
+ ----------
+ init_arr_label : :class:`bool`, default: `False`
+ Instantiates a :class:`manim.Text` and adds it to :attr:`__mob_arr_label`.
+ """
+
+ if init_arr_label:
+ self.__mob_arr_label = Text(**self.__mob_arr_label_props)
+ if len(self.__mob_arr):
+ (next_to_mob, label_pos) = self.__calc_label_pos_and_mob()
+ self.__mob_arr_label.next_to(
+ next_to_mob, label_pos, self.__arr_label_gap
+ )
+ self.add(self.__mob_arr_label)
+
def __calc_index(self, index: int) -> typing.Union[int, str]:
"""Calculates and returns the index based on attributes set at initialization.
@@ -380,25 +618,68 @@ def __calc_index_pos(self) -> np.ndarray:
else self.__dir_map[self.__arr_dir.value]["index"] * -1
)
+ def __calc_label_shift_factor(self, mob: MArrayElement) -> float:
+ """Calculates how much to shift the :attr:`__mob_arr_label` after insertion/removal of an :class:`MArrayElement`.
+
+ Parameters
+ ----------
+ mob : :class:`MArrayElement`
+ The element that has been inserted or removed.
+
+ Returns
+ -------
+ :class:`int`
+ Factor by which to shift the :attr:`__mob_arr_label`.
+ """
+
+ if np.array_equal(
+ self.__dir_map[self.__arr_label_pos.value]["arr"],
+ self.__dir_map[self.__arr_dir.value]["arr"],
+ ):
+ return mob.fetch_mob_square().side_length
+ elif not np.array_equal(
+ self.__dir_map[self.__arr_label_pos.value]["arr"],
+ -self.__dir_map[self.__arr_dir.value]["arr"],
+ ):
+ return mob.fetch_mob_square().side_length / 2
+ return 0
+
def __append_elem(
self,
value,
+ shift_label: bool = True,
+ append_anim: Animation = Write,
+ append_anim_args: dict = {},
+ append_anim_target: MArrayElementComp = None,
mob_square_args: dict = {},
mob_value_args: dict = {},
mob_index_args: dict = {},
- ) -> None:
+ ) -> typing.List[Animation]:
"""Creates a new :class:`MArrayElement` and appends it to :attr:`__mob_arr`.
Parameters
----------
value
Value to append.
+ shift_label: :class:`bool`, default: `True`
+ Specifies whether to shift the :class:`__mob_arr_label` or not.
+ append_anim : :class:`manim.Animation`, default: :class:`manim.Write`
+ Specifies the :class:`manim.Animation` to be played on the :class:`MArrayElement` being appended.
+ append_anim_args : :class:`dict`, default: `{}`
+ Arguments for append :class:`manim.Animation`.
+ append_anim_target : :class:`.m_enum.MArrayElementComp`, default: `None`
+ Specifies the :class:`manim.Mobject` of the :class:`MArrayElement` on which the append :class:`manim.Animation` is to be played.
mob_square_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Square` that represents the element body of :class:`MArrayElement`.
mob_value_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Text` that represents the element value of :class:`MArrayElement`.
mob_index_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Text` that represents the element index of :class:`MArrayElement`.
+
+ Returns
+ -------
+ List[:class:`manim.Animation`]
+ List of animations for appending.
"""
mob_value_args["text"] = value
@@ -415,15 +696,135 @@ def __append_elem(
)
self.add(self.__mob_arr[-1])
+ anim_list = [
+ append_anim(
+ self.__mob_arr[-1].fetch_mob(append_anim_target), **append_anim_args
+ )
+ ]
+
+ if shift_label:
+ label_shift_factor = self.__calc_label_shift_factor(self.__mob_arr[-1])
+ anim_list.append(
+ ApplyMethod(
+ self.__mob_arr_label.shift,
+ self.__dir_map[self.__arr_dir.value]["arr"] * label_shift_factor,
+ )
+ )
+
+ return anim_list
+
+ def __remove_elem(
+ self,
+ index: int,
+ removal_anim: Animation = FadeOut,
+ update_anim: Animation = Write,
+ removal_anim_args: dict = {},
+ update_anim_args: dict = {},
+ removal_anim_target: MArrayElementComp = None,
+ update_anim_target: MArrayElementComp = MArrayElementComp.INDEX,
+ ) -> typing.Tuple[Succession, typing.Callable[[], typing.List[Animation]]]:
+ """Removes the :class:`MArrayElement` from :attr:`__mob_arr` at the specified index.
+
+ Parameters
+ ----------
+ index : :class:`int`
+ Index of :class:`MArrayElement` to remove.
+ removal_anim : :class:`manim.Animation`, default: :class:`manim.FadeOut`
+ Specifies the :class:`manim.Animation` to be played on the :class:`MArrayElement` being removed.
+ update_anim : :class:`manim.Animation`, default: :class:`manim.Write`
+ Specifies the :class:`manim.Animation` to be played on the :class:`MArrayElement`(s) after the removed element.
+ removal_anim_args : :class:`dict`, default: `{}`
+ Arguments for removal :class:`manim.Animation`.
+ update_anim_args : :class:`dict`, default: `{}`
+ Arguments for update :class:`manim.Animation`.
+ removal_anim_target : :class:`.m_enum.MArrayElementComp`, default: `None`
+ Specifies the :class:`manim.Mobject` of the :class:`MArrayElement` on which the removal :class:`manim.Animation` is to be played.
+ update_anim_target : :class:`.m_enum.MArrayElementComp`, default: :attr:`.m_enum.MArrayElementComp.INDEX`
+ Specifies the :class:`manim.Mobject` of the :class:`MArrayElement` on which the update :class:`manim.Animation` is to be played.
+
+ Returns
+ -------
+ :class:`manim.Succession`
+ Contains :class:`manim.Animations` played for removal and shifting of :class:`MArrayElement`.
+ Callable[[], List[:class:`manim.Animation`]]
+ Method that updates the indices of :class:`MArrayElement`(s) that occur after the removal and returns a list of update :class:`manim.Animation`(s).
+ """
+
+ if index < 0 or index > len(self.__mob_arr):
+ raise Exception("Index out of bounds!")
+
+ self.remove(self.__mob_arr[index])
+ removed_mob = self.__mob_arr[index]
+ self.__mob_arr = self.__mob_arr[0:index] + self.__mob_arr[index + 1 :]
+
+ anims_shift = []
+ for i in range(index, len(self.__mob_arr)):
+ anims_shift.append(
+ ApplyMethod(
+ self.__mob_arr[i].shift,
+ -(
+ self.__dir_map[self.__arr_dir.value]["arr"]
+ * removed_mob.fetch_mob_square().side_length
+ ),
+ )
+ )
+
+ label_shift_factor = self.__calc_label_shift_factor(removed_mob)
+
+ if label_shift_factor != 0:
+ anims_shift.append(
+ ApplyMethod(
+ self.__mob_arr_label.shift,
+ -self.__dir_map[self.__arr_dir.value]["arr"] * label_shift_factor,
+ )
+ )
+
+ def update_indices() -> typing.List[Animation]:
+ """Updates the indices of :class:`MArrayElement`(s) that occur after the removal.
+
+ Returns
+ -------
+ List[:class:`manim.Animation`]
+ Represents :class:`Animation` for indices update.
+ """
+
+ anims_index = []
+ for i in range(index, len(self.__mob_arr)):
+ self.__mob_arr[i].update_mob_index(
+ mob_index_args={"text": self.__calc_index(i)}
+ )
+ anims_index.append(
+ update_anim(
+ (self.__mob_arr[i].fetch_mob(update_anim_target)),
+ **update_anim_args
+ )
+ )
+
+ return anims_index
+
+ return (
+ Succession(
+ removal_anim(
+ removed_mob.fetch_mob(removal_anim_target), **removal_anim_args
+ ),
+ AnimationGroup(*anims_shift),
+ ),
+ update_indices,
+ )
+
def __init__(
self,
arr: list = [],
+ label="",
index_offset: int = 1,
index_start: int = 0,
index_hex_display: bool = False,
hide_index: bool = False,
arr_dir: MArrayDirection = MArrayDirection.RIGHT,
switch_index_pos: bool = False,
+ arr_label_pos: MArrayDirection = MArrayDirection.LEFT,
+ arr_label_gap: float = 0.5,
+ mob_arr_label_args: dict = {},
mob_square_args: dict = {},
mob_value_args: dict = {},
mob_index_args: dict = {},
@@ -445,6 +846,12 @@ def __init__(
Specifies whether to display indices or not.
arr_dir : :class:`.m_enum.MArrayDirection`, default: :attr:`.m_enum.MArrayDirection.RIGHT`
Specifies the growing direction of array.
+ arr_label_pos : :class:`.enum.MArrayDirection`, default: :attr:`.m_enum.MArrayDirection.LEFT`
+ Specifies the position of :attr:`__mob_arr_label`.
+ arr_label_gap : :class:`float`, default: `0.5`
+ Specifies the distance between :attr:`__mob_arr` and :attr:`__mob_arr_label`.
+ mob_arr_label_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the label for :class:`MArray`.
mob_square_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Square` that represents the element body of :class:`MArrayElement`.
mob_value_args : :class:`dict`, default: `{}`
@@ -464,9 +871,22 @@ def __init__(
self.__hide_index = hide_index
self.__arr_dir = arr_dir
self.__switch_index_pos = switch_index_pos
+ self.__arr_label_pos = arr_label_pos
+ self.__arr_label_gap = arr_label_gap
+
+ self.__init_props()
+ self.__update_props(label=label, mob_arr_label_args=mob_arr_label_args)
for v in arr:
- self.__append_elem(v, mob_square_args, mob_value_args, mob_index_args)
+ self.__append_elem(
+ v,
+ False,
+ mob_square_args=mob_square_args,
+ mob_value_args=mob_value_args,
+ mob_index_args=mob_index_args,
+ )
+
+ self.__init_mobs(True)
def update_elem_value(self, index: int, value, mob_value_args: dict = {}) -> Text:
"""Updates the elements value.
@@ -596,31 +1016,102 @@ def animate_elem_index(self, index: int) -> "_AnimationBuilder": # type: ignore
def append_elem(
self,
value,
+ append_anim: Animation = Write,
+ append_anim_args: dict = {},
+ append_anim_target: MArrayElementComp = None,
mob_square_args: dict = {},
mob_value_args: dict = {},
mob_index_args: dict = {},
- ) -> MArrayElement:
+ ) -> typing.List[Animation]:
"""Appends the `value` to :attr:`__arr` and creates a new :class:`MArrayElement` and appends it to :attr:`__mob_arr`.
Parameters
----------
value
Value to append.
+ append_anim : :class:`manim.Animation`, default: :class:`manim.Write`
+ Specifies the :class:`manim.Animation` to be played on the :class:`MArrayElement` being appended.
+ append_anim_args : :class:`dict`, default: `{}`
+ Arguments for append :class:`manim.Animation`.
+ append_anim_target : :class:`.m_enum.MArrayElementComp`, default: `None`
+ Specifies the :class:`manim.Mobject` of the :class:`MArrayElement` on which the append :class:`manim.Animation` is to be played.
mob_square_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Square` that represents the element body of :class:`MArrayElement`.
mob_value_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Text` that represents the element value of :class:`MArrayElement`.
mob_index_args : :class:`dict`, default: `{}`
Arguments for :class:`manim.Text` that represents the element index of :class:`MArrayElement`.
+ shift_label: :class:`bool`, default: `True`
+ Specifies whether to shift the :class:`__mob_arr_label` or not.
Returns
-------
- :class:`MArrayElement`
- Represents the appended element.
+ List[:class:`manim.Animation`]
+ List of animations for appending.
"""
+
self.__arr.append(value)
- self.__append_elem(value, mob_square_args, mob_value_args, mob_index_args)
- return self.__mob_arr[-1]
+ return self.__append_elem(
+ value,
+ mob_square_args=mob_square_args,
+ mob_value_args=mob_value_args,
+ mob_index_args=mob_index_args,
+ append_anim=append_anim,
+ append_anim_args=append_anim_args,
+ append_anim_target=append_anim_target,
+ )
+
+ def remove_elem(
+ self,
+ index,
+ removal_anim: Animation = FadeOut,
+ update_anim: Animation = Write,
+ removal_anim_args: dict = {},
+ update_anim_args: dict = {},
+ removal_anim_target: MArrayElementComp = None,
+ update_anim_target: MArrayElementComp = MArrayElementComp.INDEX,
+ ) -> typing.Tuple[Succession, typing.Callable[[], typing.List[Animation]]]:
+ """Removes the element from :attr:`__arr` and removes :class:`MArrayElement` from :attr:`__mob_arr` at the specified index.
+
+ Parameters
+ ----------
+ index : :class:`int`
+ Index of :class:`MArrayElement` to remove.
+ removal_anim : :class:`manim.Animation`, default: :class:`manim.FadeOut`
+ Specifies the :class:`manim.Animation` to be played on the :class:`MArrayElement` being removed.
+ update_anim : :class:`manim.Animation`, default: :class:`manim.Write`
+ Specifies the :class:`manim.Animation` to be played on the :class:`MArrayElement`(s) after the removed element.
+ removal_anim_args : :class:`dict`, default: `{}`
+ Arguments for removal :class:`manim.Animation`.
+ update_anim_args : :class:`dict`, default: `{}`
+ Arguments for update :class:`manim.Animation`.
+ removal_anim_target : :class:`.m_enum.MArrayElementComp`, default: `None`
+ Specifies the :class:`manim.Mobject` of the :class:`MArrayElement` on which the removal :class:`manim.Animation` is to be played.
+ update_anim_target : :class:`.m_enum.MArrayElementComp`, default: :attr:`.m_enum.MArrayElementComp.INDEX`
+ Specifies the :class:`manim.Mobject` of the :class:`MArrayElement` on which the update :class:`manim.Animation` is to be played.
+
+ Returns
+ -------
+ :class:`manim.Succession`
+ Contains :class:`manim.Animations` played for removal and shifting of :class:`MArrayElement`.
+ Callable[[], List[:class:`manim.Animation`]]
+ Method that updates the indices of :class:`MArrayElement`(s) that occur after the removal and returns a list of update :class:`manim.Animation`(s).
+ """
+
+ if index < 0 or index > len(self.__mob_arr):
+ raise Exception("Index out of bounds!")
+
+ self.__arr = self.__arr[0:index] + self.__arr[index + 1 :]
+
+ return self.__remove_elem(
+ index,
+ removal_anim,
+ update_anim,
+ removal_anim_args,
+ update_anim_args,
+ removal_anim_target,
+ update_anim_target,
+ )
def fetch_arr(self) -> list:
"""Fetches :attr:`__arr`.
@@ -643,3 +1134,14 @@ def fetch_mob_arr(self) -> typing.List[MArrayElement]:
"""
return self.__mob_arr
+
+ def fetch_arr_label(self) -> Text:
+ """Fetches the :class:`manim.Text` that represents the array label.
+
+ Returns
+ -------
+ :class:`manim.Text`
+ Represents the array label.
+ """
+
+ return self.__mob_arr_label
diff --git a/src/manim_data_structures/m_enum.py b/src/manim_data_structures/m_enum.py
index e5f3ecc..d4b1015 100644
--- a/src/manim_data_structures/m_enum.py
+++ b/src/manim_data_structures/m_enum.py
@@ -3,6 +3,22 @@
from enum import Enum
+class MArrayElementComp(Enum):
+ """Refers to the individual component :class:`manim.Mobject` of :class:`MArrayElement`."""
+
+ BODY = 0
+ """Body :class:`manim.Square`"""
+
+ VALUE = 1
+ """Value :class:`manim.Text`"""
+
+ INDEX = 2
+ """Index :class:`manim.Text`"""
+
+ LABEL = 3
+ """Label :class:`manim.Text`"""
+
+
class MArrayDirection(Enum):
"""Specifies the growth direction of the :class:`MArray`."""
diff --git a/src/manim_data_structures/m_variable.py b/src/manim_data_structures/m_variable.py
new file mode 100644
index 0000000..a5eeca9
--- /dev/null
+++ b/src/manim_data_structures/m_variable.py
@@ -0,0 +1,173 @@
+"""Contains classes to construct variable."""
+
+from manim import *
+
+from .m_array import MArrayElement
+
+
+class MVariable(MArrayElement):
+ """A class that represents a variable.
+
+ Parameters
+ ----------
+ value
+ Specifies the value of the variable.
+ index
+ Specifies the index of the variable.
+ label
+ Specifies the label of the variable.
+ mob_square_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Square` that represents the element body.
+ mob_value_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element value.
+ mob_index_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element index.
+
+ Attributes
+ ----------
+ __value
+ Specifies the value of the variable.
+ __index
+ Specifies the index of the variable.
+ __label
+ Specifies the label of the variable.
+ """
+
+ def __init__(
+ self,
+ value="",
+ index="",
+ label="",
+ mob_value_args: dict = {},
+ mob_index_args: dict = {},
+ mob_label_args: dict = {},
+ **kwargs
+ ) -> None:
+ """Initializes the class.
+
+ Parameters
+ ----------
+ value
+ Specifies the value of the variable.
+ index
+ Specifies the index of the variable.
+ label
+ Specifies the label of the variable.
+ mob_square_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Square` that represents the element body.
+ mob_value_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element value.
+ mob_index_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element index.
+
+ Attributes
+ ----------
+ __value
+ Specifies the value of the variable.
+ __index
+ Specifies the index of the variable.
+ __label
+ Specifies the label of the variable.
+ """
+
+ self.__value = value
+ self.__index = index
+ self.__label = label
+
+ mob_value_args["text"] = value
+ mob_index_args["text"] = index
+ mob_label_args["text"] = label
+
+ super().__init__(
+ mob_value_args=mob_value_args,
+ mob_index_args=mob_index_args,
+ mob_label_args=mob_label_args,
+ **kwargs
+ )
+
+ def fetch_value(self):
+ """Fetches :attr:`__value`.
+
+ Returns
+ -------
+ Any
+ Value of :class:`MVariable`.
+ """
+
+ return self.__value
+
+ def fetch_index(self):
+ """Fetches :attr:`__index`.
+
+ Returns
+ -------
+ Any
+ Index of :class:`MVariable`.
+ """
+
+ return self.__index
+
+ def fetch_label(self):
+ """Fetches :attr:`__label`.
+
+ Returns
+ -------
+ Any
+ Label of :class:`MVariable`.
+ """
+
+ return self.__label
+
+ def update_value(self, value, mob_value_args: dict = {}) -> Text:
+ """Updates :attr:`__value` and the :class:`manim.Text` that represents the element value.
+
+ Parameters
+ ----------
+ mob_value_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element value.
+
+ Returns
+ -------
+ :class:`manim.Text`
+ Represents the updated element value.
+ """
+
+ self.__value = value
+ mob_value_args["text"] = value
+ return self.update_mob_value(mob_value_args)
+
+ def update_index(self, index, mob_index_args: dict = {}) -> Text:
+ """Updates :attr:`__index` and the :class:`manim.Text` that represents the element index.
+
+ Parameters
+ ----------
+ mob_index_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element index.
+
+ Returns
+ -------
+ :class:`manim.Text`
+ Represents the updated element index.
+ """
+
+ self.__index = index
+ mob_index_args["text"] = index
+ return self.update_mob_index(mob_index_args)
+
+ def update_label(self, label, mob_label_args: dict = {}) -> Text:
+ """Updates :attr:`__label` and the :class:`manim.Text` that represents the element label.
+
+ Parameters
+ ----------
+ mob_label_args : :class:`dict`, default: `{}`
+ Arguments for :class:`manim.Text` that represents the element label.
+
+ Returns
+ -------
+ :class:`manim.Text`
+ Represents the updated element label.
+ """
+
+ self.__value = label
+ mob_label_args["text"] = label
+ return self.update_mob_label(mob_label_args)