From ce65f8c4c39d57ba545fe4c54d2c4d4855776bc1 Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Thu, 14 Sep 2023 20:59:23 -0400 Subject: [PATCH 01/15] m_narray_tree bugged (edges improperly defined?) --- src/manim_data_structures/m_narry_tree.py | 0 tests/test_mtree.py | 11 +++++++++++ 2 files changed, 11 insertions(+) create mode 100644 src/manim_data_structures/m_narry_tree.py create mode 100644 tests/test_mtree.py diff --git a/src/manim_data_structures/m_narry_tree.py b/src/manim_data_structures/m_narry_tree.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_mtree.py b/tests/test_mtree.py new file mode 100644 index 0000000..9843417 --- /dev/null +++ b/tests/test_mtree.py @@ -0,0 +1,11 @@ +# TODO: Fill with appropriate tests +def test_getitem(): + pass + + +def test_setitem(): + pass + + +def test_iteration(): + pass From 9c2f66ac9c78818bb1a45947a65f880f672ee973 Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Thu, 14 Sep 2023 21:43:25 -0400 Subject: [PATCH 02/15] added m_tree --- src/manim_data_structures/m_tree.py | 260 ++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 src/manim_data_structures/m_tree.py diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py new file mode 100644 index 0000000..8ace28e --- /dev/null +++ b/src/manim_data_structures/m_tree.py @@ -0,0 +1,260 @@ +import random +from collections import defaultdict +from copy import copy +from typing import Dict, Hashable, List, Tuple + +import manim +import networkx as nx +import numpy as np +from manim import * + +# def _tree_layout_ltr( +# T: nx.classes.graph.Graph, +# root_vertex: Hashable | None, +# scale: float | tuple | None = 2.0, +# vertex_spacing: tuple | None = None, +# orientation: str = "down", +# children: dict[Hashable, list] = None +# ): +# if not nx.is_tree(T): +# raise ValueError("The tree layout must be used with trees") +# if root_vertex is None: +# raise ValueError("The tree layout requires the root_vertex parameter") +# +# # The following code is SageMath's tree layout implementation, taken from +# # https://github.com/sagemath/sage/blob/cc60cfebc4576fed8b01f0fc487271bdee3cefed/src/sage/graphs/graph_plot.py#L1447 +# +# # Always make a copy of the children because they get eaten +# stack = [list(children[root_vertex]).copy()] +# stick = [root_vertex] +# parent = {u: root_vertex for u in children[root_vertex]} +# pos = {} +# obstruction = [0.0] * len(T) +# orientation = -1 if orientation == "down" else 1 +# +# def slide(v, dx): +# """ +# Shift the vertex v and its descendants to the right by dx. +# Precondition: v and its descendents have already had their +# positions computed. +# """ +# level = [v] +# while level: +# nextlevel = [] +# for u in level: +# x, y = pos[u] +# x += dx +# obstruction[y] = max(x + 1, obstruction[y]) +# pos[u] = x, y +# nextlevel += children[u] +# level = nextlevel +# +# while stack: +# curr_children = stack[-1] +# if not curr_children: +# p = stick.pop() +# stack.pop() +# cp = children[p] +# y = orientation * len(stack) +# if not cp: +# x = obstruction[y] +# pos[p] = x, y +# else: +# x = sum(pos[c][0] for c in cp) / float(len(cp)) +# pos[p] = x, y +# ox = obstruction[y] +# if x < ox: +# slide(p, ox - x) +# x = ox +# obstruction[y] = x + 1 +# continue +# +# t = curr_children.pop() +# pt = parent[t] +# +# ct = [u for u in list(T.neighbors(t)) if u != pt] +# for c in ct: +# parent[c] = t +# children[t] = copy(ct) +# +# stack.append(ct) +# stick.append(t) +# +# # the resulting layout is then rescaled again to fit on Manim's canvas +# +# x_min = min(pos.values(), key=lambda t: t[0])[0] +# x_max = max(pos.values(), key=lambda t: t[0])[0] +# y_min = min(pos.values(), key=lambda t: t[1])[1] +# y_max = max(pos.values(), key=lambda t: t[1])[1] +# center = np.array([x_min + x_max, y_min + y_max, 0]) / 2 +# height = y_max - y_min +# width = x_max - x_min +# if vertex_spacing is None: +# if isinstance(scale, (float, int)) and (width > 0 or height > 0): +# sf = 2 * scale / max(width, height) +# elif isinstance(scale, tuple): +# if scale[0] is not None and width > 0: +# sw = 2 * scale[0] / width +# else: +# sw = 1 +# +# if scale[1] is not None and height > 0: +# sh = 2 * scale[1] / height +# else: +# sh = 1 +# +# sf = np.array([sw, sh, 0]) +# else: +# sf = 1 +# else: +# sx, sy = vertex_spacing +# sf = np.array([sx, sy, 0]) +# return {v: (np.array([x, y, 0]) - center) * sf for v, (x, y) in pos.items()} + + +def _tree_layout_ltr( + T: nx.classes.graph.Graph, + root_vertex: Hashable | None, + scale: float | tuple | None = 2.0, + vertex_spacing: tuple | None = None, + orientation: str = "down", + children: dict[Hashable, list] = None, +): + """ + Arranges the tree from left to right + """ + curr_children = [list(children[root_vertex]).copy()] + curr_parent = [root_vertex] + pos = {} + x_min = min(pos.values(), key=lambda t: t[0])[0] + x_max = max(pos.values(), key=lambda t: t[0])[0] + y_min = min(pos.values(), key=lambda t: t[1])[1] + y_max = max(pos.values(), key=lambda t: t[1])[1] + center = np.array([x_min + x_max, y_min + y_max, 0]) / 2 + sx, sy = vertex_spacing + sf = np.array([sx, sy, 0]) + return {v: (np.array([x, y, 0]) - center) * sf for v, (x, y) in pos.items()} + + +class Tree(VMobject): + """Computer Science Tree Data Structure""" + + __graph: Graph + __parents: list + __children: dict[Hashable, list] = defaultdict(list) + + def __init__( + self, vmobjects: List[Mobject], parents: List[Hashable], edge_buff=0.0, **kwargs + ): + super().__init__(**kwargs) + self.__parents = parents + vertices: List[Hashable] = [] + edges: List[Tuple[Hashable, Hashable]] = [] + mobjects: Dict[int, Mobject] = {} + for i, node in enumerate(vmobjects): + mobjects[i] = node + vertices.append(i) + for i, parent in enumerate(parents): + if parent is not None: + edges.append((parent, i)) + for parent, child in edges: + self.__children[parent].append(child) + + self.__graph = Graph( + vertices, + edges, + vertex_mobjects=mobjects, + layout="tree", + root_vertex=0, + layout_scale=len(vmobjects) * 0.5, + edge_config={"stroke_width": 1, "stroke_color": WHITE}, + ) + # self.__graph.change_layout( + # layout=_tree_layout_ltr(children=self.__children, root_vertex=0, T=self.__graph._graph), + # layout_scale=len(self.__parents) * 0.5) + if edge_buff is not None: + for (u, v), edge in self.__graph.edges.items(): + buff_vec = ( + edge_buff + * (self.__graph[u].get_center() - self.__graph[v].get_center()) + / np.linalg.norm( + self.__graph[u].get_center() - self.__graph[v].get_center() + ) + ) + edge.put_start_and_end_on( + self.__graph[u].get_center() - buff_vec, + self.__graph[v].get_center() + buff_vec, + ) + + def update_edges(graph: Graph): + """Updates edges of graph""" + for (u, v), edge in graph.edges.items(): + buff_vec = ( + edge_buff + * (graph[u].get_center() - graph[v].get_center()) + / np.linalg.norm(graph[u].get_center() - graph[v].get_center()) + ) + edge.put_start_and_end_on( + graph[u].get_center() - buff_vec, graph[v].get_center() + buff_vec + ) + + self.__graph.updaters.clear() + self.__graph.updaters.append(update_edges) + self.add(self.__graph) + + def __setitem__(self, __index: Hashable, __value: Mobject) -> None: + """Sets the value of a node in the tree""" + node_center = self.__graph[__index].get_center() + node_parent = self.__parents[__index] + self.__graph.remove_vertices(__index) + self.__graph.add_vertices( + __index, + vertex_mobjects={__index: __value}, + positions={__index: node_center}, + ) + self.__graph.add_edges((node_parent, __index)) + for i, parent in enumerate(self.__parents): + if parent == __index: + self.__graph.add_edges((__index, i)) + + def __insert__( + self, __parent: Hashable, __value: Mobject, __index: Hashable = 0 + ) -> None: + self.__graph.add_vertices(__index, vertex_mobjects={__index: __value}) + self.__graph.add_edges((__parent, __index)) + self.__graph.change_layout( + layout=_tree_layout_ltr( + children=self.__children, + root_vertex=0, + T=self.__graph._graph, + vertex_spacing=(1, 1), + ), + layout_scale=len(self.__parents) * 0.5, + ) + + +if __name__ == "__main__": + + class TestScene(Scene): + def construct(self): + # make a parent list for a tree + pars = [None, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2] + # make a list of mobjects random integers + nodes = [Integer(2 * x) for x in range(len(p))] + tree = Tree(nodes, pars, edge_buff=0.4) + tree.__setitem__(8, Integer(1)) + self.play(Create(tree)) + self.wait() + # graph = manim.Graph([0, 2], [(0, 2)], root_vertex=0, layout='tree', vertex_type=Integer) + # graph.add_vertices(1, vertex_type=Integer) + # graph.add_vertices(3, vertex_type=Integer) + # graph.add_edges((0, 1)) + # graph.add_edges((1, 3)) + # graph.change_layout('tree', root_vertex=0) + # self.play(Create(graph)) + # self.wait() + + config.preview = True + config.renderer = "cairo" + config.quality = "low_quality" + TestScene().render(preview=True) From a47aaac2e77e91b145d0cf4fdc0519636d5be54c Mon Sep 17 00:00:00 2001 From: Nikhil Iyer Date: Thu, 21 Sep 2023 17:31:31 -0400 Subject: [PATCH 03/15] basic left->right tree --- src/manim_data_structures/m_tree.py | 294 ++++++++-------------------- 1 file changed, 78 insertions(+), 216 deletions(-) diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py index 8ace28e..b62975a 100644 --- a/src/manim_data_structures/m_tree.py +++ b/src/manim_data_structures/m_tree.py @@ -1,236 +1,101 @@ import random from collections import defaultdict from copy import copy -from typing import Dict, Hashable, List, Tuple +from typing import Any, Callable, Dict, Hashable, List, Tuple -import manim -import networkx as nx import numpy as np from manim import * - -# def _tree_layout_ltr( -# T: nx.classes.graph.Graph, -# root_vertex: Hashable | None, -# scale: float | tuple | None = 2.0, -# vertex_spacing: tuple | None = None, -# orientation: str = "down", -# children: dict[Hashable, list] = None -# ): -# if not nx.is_tree(T): -# raise ValueError("The tree layout must be used with trees") -# if root_vertex is None: -# raise ValueError("The tree layout requires the root_vertex parameter") -# -# # The following code is SageMath's tree layout implementation, taken from -# # https://github.com/sagemath/sage/blob/cc60cfebc4576fed8b01f0fc487271bdee3cefed/src/sage/graphs/graph_plot.py#L1447 -# -# # Always make a copy of the children because they get eaten -# stack = [list(children[root_vertex]).copy()] -# stick = [root_vertex] -# parent = {u: root_vertex for u in children[root_vertex]} -# pos = {} -# obstruction = [0.0] * len(T) -# orientation = -1 if orientation == "down" else 1 -# -# def slide(v, dx): -# """ -# Shift the vertex v and its descendants to the right by dx. -# Precondition: v and its descendents have already had their -# positions computed. -# """ -# level = [v] -# while level: -# nextlevel = [] -# for u in level: -# x, y = pos[u] -# x += dx -# obstruction[y] = max(x + 1, obstruction[y]) -# pos[u] = x, y -# nextlevel += children[u] -# level = nextlevel -# -# while stack: -# curr_children = stack[-1] -# if not curr_children: -# p = stick.pop() -# stack.pop() -# cp = children[p] -# y = orientation * len(stack) -# if not cp: -# x = obstruction[y] -# pos[p] = x, y -# else: -# x = sum(pos[c][0] for c in cp) / float(len(cp)) -# pos[p] = x, y -# ox = obstruction[y] -# if x < ox: -# slide(p, ox - x) -# x = ox -# obstruction[y] = x + 1 -# continue -# -# t = curr_children.pop() -# pt = parent[t] -# -# ct = [u for u in list(T.neighbors(t)) if u != pt] -# for c in ct: -# parent[c] = t -# children[t] = copy(ct) -# -# stack.append(ct) -# stick.append(t) -# -# # the resulting layout is then rescaled again to fit on Manim's canvas -# -# x_min = min(pos.values(), key=lambda t: t[0])[0] -# x_max = max(pos.values(), key=lambda t: t[0])[0] -# y_min = min(pos.values(), key=lambda t: t[1])[1] -# y_max = max(pos.values(), key=lambda t: t[1])[1] -# center = np.array([x_min + x_max, y_min + y_max, 0]) / 2 -# height = y_max - y_min -# width = x_max - x_min -# if vertex_spacing is None: -# if isinstance(scale, (float, int)) and (width > 0 or height > 0): -# sf = 2 * scale / max(width, height) -# elif isinstance(scale, tuple): -# if scale[0] is not None and width > 0: -# sw = 2 * scale[0] / width -# else: -# sw = 1 -# -# if scale[1] is not None and height > 0: -# sh = 2 * scale[1] / height -# else: -# sh = 1 -# -# sf = np.array([sw, sh, 0]) -# else: -# sf = 1 -# else: -# sx, sy = vertex_spacing -# sf = np.array([sx, sy, 0]) -# return {v: (np.array([x, y, 0]) - center) * sf for v, (x, y) in pos.items()} - - -def _tree_layout_ltr( - T: nx.classes.graph.Graph, - root_vertex: Hashable | None, - scale: float | tuple | None = 2.0, - vertex_spacing: tuple | None = None, - orientation: str = "down", - children: dict[Hashable, list] = None, -): - """ - Arranges the tree from left to right - """ - curr_children = [list(children[root_vertex]).copy()] - curr_parent = [root_vertex] - pos = {} - x_min = min(pos.values(), key=lambda t: t[0])[0] - x_max = max(pos.values(), key=lambda t: t[0])[0] - y_min = min(pos.values(), key=lambda t: t[1])[1] - y_max = max(pos.values(), key=lambda t: t[1])[1] - center = np.array([x_min + x_max, y_min + y_max, 0]) / 2 - sx, sy = vertex_spacing - sf = np.array([sx, sy, 0]) - return {v: (np.array([x, y, 0]) - center) * sf for v, (x, y) in pos.items()} +from manim import WHITE, Graph, Mobject, VMobject class Tree(VMobject): """Computer Science Tree Data Structure""" __graph: Graph - __parents: list - __children: dict[Hashable, list] = defaultdict(list) + __layout_config: dict + __layout_scale: float + __vertex_type: Callable[..., Mobject] + # __parents: list + # __children: dict[Hashable, list] = defaultdict(list) def __init__( - self, vmobjects: List[Mobject], parents: List[Hashable], edge_buff=0.0, **kwargs + self, + vertices: list[Any], + edges: list[tuple[int, int]], + vertex_type: Callable[..., Mobject], + edge_buff=0.4, + layout_config={"vertex_spacing": (-1, 1)}, + root_vertex=0, + **kwargs ): super().__init__(**kwargs) - self.__parents = parents - vertices: List[Hashable] = [] - edges: List[Tuple[Hashable, Hashable]] = [] - mobjects: Dict[int, Mobject] = {} - for i, node in enumerate(vmobjects): - mobjects[i] = node - vertices.append(i) - for i, parent in enumerate(parents): - if parent is not None: - edges.append((parent, i)) - for parent, child in edges: - self.__children[parent].append(child) + + vertex_mobjects = {i: vertex_type(v) for i, v in enumerate(vertices)} + self.__layout_config = layout_config + self.__layout_scale = len(vertices) * 0.5 + self.__vertex_type = vertex_type self.__graph = Graph( vertices, edges, - vertex_mobjects=mobjects, + vertex_mobjects=vertex_mobjects, layout="tree", root_vertex=0, - layout_scale=len(vmobjects) * 0.5, + layout_config=self.__layout_config, + layout_scale=len(vertices) * 0.5, edge_config={"stroke_width": 1, "stroke_color": WHITE}, ) - # self.__graph.change_layout( - # layout=_tree_layout_ltr(children=self.__children, root_vertex=0, T=self.__graph._graph), - # layout_scale=len(self.__parents) * 0.5) - if edge_buff is not None: - for (u, v), edge in self.__graph.edges.items(): - buff_vec = ( - edge_buff - * (self.__graph[u].get_center() - self.__graph[v].get_center()) - / np.linalg.norm( - self.__graph[u].get_center() - self.__graph[v].get_center() - ) - ) - edge.put_start_and_end_on( - self.__graph[u].get_center() - buff_vec, - self.__graph[v].get_center() + buff_vec, - ) - - def update_edges(graph: Graph): - """Updates edges of graph""" - for (u, v), edge in graph.edges.items(): - buff_vec = ( - edge_buff - * (graph[u].get_center() - graph[v].get_center()) - / np.linalg.norm(graph[u].get_center() - graph[v].get_center()) - ) - edge.put_start_and_end_on( - graph[u].get_center() - buff_vec, graph[v].get_center() + buff_vec - ) - self.__graph.updaters.clear() - self.__graph.updaters.append(update_edges) + # if edge_buff is not None: + # for (u, v), edge in self.__graph.edges.items(): + # buff_vec = ( + # edge_buff + # * (self.__graph[u].get_center() - self.__graph[v].get_center()) + # / np.linalg.norm( + # self.__graph[u].get_center() - self.__graph[v].get_center() + # ) + # ) + # edge.put_start_and_end_on( + # self.__graph[u].get_center() - buff_vec, + # self.__graph[v].get_center() + buff_vec, + # ) + + # def update_edges(graph: Graph): + # """Updates edges of graph""" + # for (u, v), edge in graph.edges.items(): + # buff_vec = ( + # edge_buff + # * (graph[u].get_center() - graph[v].get_center()) + # / np.linalg.norm(graph[u].get_center() - graph[v].get_center()) + # ) + # edge.put_start_and_end_on( + # graph[u].get_center() - buff_vec, graph[v].get_center() + buff_vec + # ) + + # self.__graph.updaters.clear() + # self.__graph.updaters.append(update_edges) self.add(self.__graph) - def __setitem__(self, __index: Hashable, __value: Mobject) -> None: - """Sets the value of a node in the tree""" - node_center = self.__graph[__index].get_center() - node_parent = self.__parents[__index] - self.__graph.remove_vertices(__index) - self.__graph.add_vertices( - __index, - vertex_mobjects={__index: __value}, - positions={__index: node_center}, - ) - self.__graph.add_edges((node_parent, __index)) - for i, parent in enumerate(self.__parents): - if parent == __index: - self.__graph.add_edges((__index, i)) - - def __insert__( - self, __parent: Hashable, __value: Mobject, __index: Hashable = 0 - ) -> None: - self.__graph.add_vertices(__index, vertex_mobjects={__index: __value}) - self.__graph.add_edges((__parent, __index)) - self.__graph.change_layout( - layout=_tree_layout_ltr( - children=self.__children, - root_vertex=0, - T=self.__graph._graph, - vertex_spacing=(1, 1), - ), - layout_scale=len(self.__parents) * 0.5, - ) + # def __setitem__(self, __index: Hashable, __value: Mobject) -> None: + # """Sets the value of a node in the tree""" + # node_center = self.__graph[__index].get_center() + # node_parent = self.__parents[__index] + # self.__graph.remove_vertices(__index) + # self.__graph.add_vertices( + # __index, + # vertex_mobjects={__index: __value}, + # positions={__index: node_center}, + # ) + # self.__graph.add_edges((node_parent, __index)) + # for i, parent in enumerate(self.__parents): + # if parent == __index: + # self.__graph.add_edges((__index, i)) + + # def __insert__(self, value: Any, parent_index: int) -> None: + # __index = len(self.__graph.vertices) + # self.__graph.add_vertices(__index, vertex_mobjects={__index: self.__vertex_type(value)}) + # self.__graph.add_edges((parent_index, __index)) + # self.__graph.change_layout("tree", root_vertex=0, layout_config=self.__layout_config, layout_scale=self.__layout_scale) if __name__ == "__main__": @@ -238,18 +103,15 @@ def __insert__( class TestScene(Scene): def construct(self): # make a parent list for a tree - pars = [None, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2] - # make a list of mobjects random integers - nodes = [Integer(2 * x) for x in range(len(p))] - tree = Tree(nodes, pars, edge_buff=0.4) - tree.__setitem__(8, Integer(1)) + tree = Tree(list(range(5)), [(0, 1), (0, 2), (1, 3), (1, 4)], Integer) self.play(Create(tree)) + # tree.__insert__(5, 4) self.wait() - # graph = manim.Graph([0, 2], [(0, 2)], root_vertex=0, layout='tree', vertex_type=Integer) - # graph.add_vertices(1, vertex_type=Integer) - # graph.add_vertices(3, vertex_type=Integer) - # graph.add_edges((0, 1)) - # graph.add_edges((1, 3)) + + # tree.__insert__(6, 5) + # self.wait() + + # self.play(tree._Tree__graph.vertices[0].animate.shift(UP * 2)) # graph.change_layout('tree', root_vertex=0) # self.play(Create(graph)) # self.wait() From 8e010fe0dc19f89c86e590cdb197273f706b20f0 Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Tue, 26 Sep 2023 18:06:28 -0400 Subject: [PATCH 04/15] Insertion and deletion tree --- src/manim_data_structures/m_tree.py | 70 +++++++++++++++++++---------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py index b62975a..11862a0 100644 --- a/src/manim_data_structures/m_tree.py +++ b/src/manim_data_structures/m_tree.py @@ -15,6 +15,7 @@ class Tree(VMobject): __layout_config: dict __layout_scale: float __vertex_type: Callable[..., Mobject] + # __parents: list # __children: dict[Hashable, list] = defaultdict(list) @@ -60,20 +61,20 @@ def __init__( # self.__graph[v].get_center() + buff_vec, # ) - # def update_edges(graph: Graph): - # """Updates edges of graph""" - # for (u, v), edge in graph.edges.items(): - # buff_vec = ( - # edge_buff - # * (graph[u].get_center() - graph[v].get_center()) - # / np.linalg.norm(graph[u].get_center() - graph[v].get_center()) - # ) - # edge.put_start_and_end_on( - # graph[u].get_center() - buff_vec, graph[v].get_center() + buff_vec - # ) - - # self.__graph.updaters.clear() - # self.__graph.updaters.append(update_edges) + def update_edges(graph: Graph): + """Updates edges of graph""" + for (u, v), edge in graph.edges.items(): + buff_vec = ( + edge_buff + * (graph[u].get_center() - graph[v].get_center()) + / np.linalg.norm(graph[u].get_center() - graph[v].get_center()) + ) + edge.put_start_and_end_on( + graph[u].get_center() - buff_vec, graph[v].get_center() + buff_vec + ) + + self.__graph.updaters.clear() + self.__graph.updaters.append(update_edges) self.add(self.__graph) # def __setitem__(self, __index: Hashable, __value: Mobject) -> None: @@ -91,11 +92,32 @@ def __init__( # if parent == __index: # self.__graph.add_edges((__index, i)) - # def __insert__(self, value: Any, parent_index: int) -> None: - # __index = len(self.__graph.vertices) - # self.__graph.add_vertices(__index, vertex_mobjects={__index: self.__vertex_type(value)}) - # self.__graph.add_edges((parent_index, __index)) - # self.__graph.change_layout("tree", root_vertex=0, layout_config=self.__layout_config, layout_scale=self.__layout_scale) + def __insert__(self, parent_index: int, value: Any) -> None: + """Inserts a node into the tree""" + self.__graph.add_vertices( + len(self.__graph.vertices), + vertex_mobjects={len(self.__graph.vertices): self.__vertex_type(value)}, + ) + self.__graph.add_edges((parent_index, len(self.__graph.vertices) - 1)) + self.__graph.change_layout( + "tree", + root_vertex=0, + layout_config=self.__layout_config, + layout_scale=self.__layout_scale, + ) + self.__graph.update() + + # def __animate_insert__(self, ): + def __remove__(self, index: int) -> None: + """Removes a node from the tree""" + self.__graph.remove_vertices(index) + self.__graph.change_layout( + "tree", + root_vertex=0, + layout_config=self.__layout_config, + layout_scale=self.__layout_scale, + ) + self.__graph.update() if __name__ == "__main__": @@ -105,12 +127,12 @@ def construct(self): # make a parent list for a tree tree = Tree(list(range(5)), [(0, 1), (0, 2), (1, 3), (1, 4)], Integer) self.play(Create(tree)) - # tree.__insert__(5, 4) + # for i in range(5): + # self.wait() + # tree.__insert__(2, i + 5) + tree.__remove__(2) + self.play(tree.animate) self.wait() - - # tree.__insert__(6, 5) - # self.wait() - # self.play(tree._Tree__graph.vertices[0].animate.shift(UP * 2)) # graph.change_layout('tree', root_vertex=0) # self.play(Create(graph)) From 9396e904aab9b8e4871fa83f16392305e19ae14d Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Thu, 12 Oct 2023 18:25:26 -0400 Subject: [PATCH 05/15] wip: n-ary tree with list input --- src/manim_data_structures/m_narry_tree.py | 0 src/manim_data_structures/m_nary_tree.py | 63 +++++++++++++++++++++++ 2 files changed, 63 insertions(+) delete mode 100644 src/manim_data_structures/m_narry_tree.py create mode 100644 src/manim_data_structures/m_nary_tree.py diff --git a/src/manim_data_structures/m_narry_tree.py b/src/manim_data_structures/m_narry_tree.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/m_nary_tree.py new file mode 100644 index 0000000..70cab9b --- /dev/null +++ b/src/manim_data_structures/m_nary_tree.py @@ -0,0 +1,63 @@ +from m_tree import Tree +from manim import * +from manim import WHITE, Graph, Mobject, VMobject + + +class N_ary_tree(Tree): + def __init__( + self, + nary_tree_list: list[Any], + num_child: int, + vertex_type: Callable[..., Mobject], + edge_buff=0.4, + layout_config={"vertex_spacing": (-1, 1)}, + root_vertex=0, + **kwargs + ): + self.num_child = num_child + height = 1 + int(np.log(len(nary_tree_list) + 1) / np.log(num_child)) + total_vertices = num_child**height - 1 + vertices = list(nary_tree_list) + edges = [(e // num_child, e + 1) for e in range(len(vertices) - 1)] + # vertices = vertices + vertices_null + # + # vertices = vertices + vertices_null + print(vertices) + + print(edges) + + super().__init__( + vertices, + edges, + vertex_type, + edge_buff, + layout_config, + root_vertex, + **kwargs + ) + + +if __name__ == "__main__": + + class TestScene(Scene): + def construct(self): + # make a parent list for a tree + tree = N_ary_tree( + [0, 1, 2, 3, 4, 5, 6, 7, 244, 4], num_child=3, vertex_type=Integer + ) + self.play(Create(tree)) + # for i in range(5): + # self.wait() + # tree.__insert__(2, i + 5) + # tree.__remove__(2) + self.play(tree.animate) + self.wait() + # self.play(tree._Tree__graph.vertices[0].animate.shift(UP * 2)) + # graph.change_layout('tree', root_vertex=0) + # self.play(Create(graph)) + # self.wait() + + config.preview = True + config.renderer = "cairo" + config.quality = "low_quality" + TestScene().render(preview=True) From c11bd741c9dff8a0490e3d2c73ac06473a439d68 Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Thu, 12 Oct 2023 18:31:54 -0400 Subject: [PATCH 06/15] fixed indexing issue with tree --- src/manim_data_structures/m_tree.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py index 11862a0..e39ae4d 100644 --- a/src/manim_data_structures/m_tree.py +++ b/src/manim_data_structures/m_tree.py @@ -30,14 +30,13 @@ def __init__( **kwargs ): super().__init__(**kwargs) - vertex_mobjects = {i: vertex_type(v) for i, v in enumerate(vertices)} self.__layout_config = layout_config self.__layout_scale = len(vertices) * 0.5 self.__vertex_type = vertex_type self.__graph = Graph( - vertices, + list(range(len(vertices))), edges, vertex_mobjects=vertex_mobjects, layout="tree", @@ -105,7 +104,7 @@ def __insert__(self, parent_index: int, value: Any) -> None: layout_config=self.__layout_config, layout_scale=self.__layout_scale, ) - self.__graph.update() + # self.__graph.update() # def __animate_insert__(self, ): def __remove__(self, index: int) -> None: @@ -125,7 +124,7 @@ def __remove__(self, index: int) -> None: class TestScene(Scene): def construct(self): # make a parent list for a tree - tree = Tree(list(range(5)), [(0, 1), (0, 2), (1, 3), (1, 4)], Integer) + tree = Tree([0, 1, 2, 3, 5], [(0, 1), (0, 2), (1, 3), (1, 4)], Integer) self.play(Create(tree)) # for i in range(5): # self.wait() From 36d2790bdb16d15e197696b66e03a1a381ef6d1d Mon Sep 17 00:00:00 2001 From: RyanMachado Date: Thu, 26 Oct 2023 19:07:07 -0400 Subject: [PATCH 07/15] n_ary tree layout --- .pre-commit-config.yaml | 22 ++++++++-------- src/manim_data_structures/m_nary_tree.py | 32 ++++++++++++++++++++++++ src/manim_data_structures/m_tree.py | 3 ++- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a7b28a6..604e6eb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,14 +32,14 @@ repos: flake8-rst-docstrings==0.3.0, flake8-simplify==0.19.3, ] - - repo: local - hooks: - - id: pytest - name: pytest - entry: poetry run pytest -cov=src tests/ - language: system - pass_filenames: false - # alternatively you could `types: [python]` so it only runs when python files change - # though tests might be invalidated if you were to say change a data file - always_run: true - stages: [push] +# - repo: local +# hooks: +# - id: pytest +# name: pytest +# entry: poetry run pytest -cov=src tests/ +# language: system +# pass_filenames: false +# # alternatively you could `types: [python]` so it only runs when python files change +# # though tests might be invalidated if you were to say change a data file +# always_run: true +# stages: [push] diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/m_nary_tree.py index 70cab9b..930834a 100644 --- a/src/manim_data_structures/m_nary_tree.py +++ b/src/manim_data_structures/m_nary_tree.py @@ -1,6 +1,10 @@ +from typing import Hashable from m_tree import Tree from manim import * from manim import WHITE, Graph, Mobject, VMobject +import networkx as nx + + class N_ary_tree(Tree): @@ -14,6 +18,33 @@ def __init__( root_vertex=0, **kwargs ): + n = num_child + def n_ary_layout( + T: nx.classes.graph.Graph, + root_vertex: Hashable | None, + scale: float | tuple | None = 2, + vertex_spacing: tuple | None = None, + orientation: str = "down", + ): + if not n: + raise ValueError("the n-ary tree layout requires the n parameter") + if not nx.is_tree(T): + raise ValueError("The tree layout must be used with trees") + if root_vertex is None: + raise ValueError("The tree layout requires the root_vertex parameter") + def calc_pos(i): + if n == 1: + return (1, i+1) + k=0 + sum = 0 + while True: + sum += n**k + if i Date: Thu, 2 Nov 2023 20:19:10 -0400 Subject: [PATCH 08/15] N-ary tree layout working for complete tree --- src/manim_data_structures/m_nary_tree.py | 64 ++++++++++++------------ src/manim_data_structures/m_tree.py | 10 ++-- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/m_nary_tree.py index 930834a..645820f 100644 --- a/src/manim_data_structures/m_nary_tree.py +++ b/src/manim_data_structures/m_nary_tree.py @@ -1,16 +1,15 @@ from typing import Hashable + +import networkx as nx from m_tree import Tree from manim import * from manim import WHITE, Graph, Mobject, VMobject -import networkx as nx - - class N_ary_tree(Tree): def __init__( self, - nary_tree_list: list[Any], + nary_tree_list: list[Hashable], num_child: int, vertex_type: Callable[..., Mobject], edge_buff=0.4, @@ -19,6 +18,7 @@ def __init__( **kwargs ): n = num_child + def n_ary_layout( T: nx.classes.graph.Graph, root_vertex: Hashable | None, @@ -32,41 +32,35 @@ def n_ary_layout( raise ValueError("The tree layout must be used with trees") if root_vertex is None: raise ValueError("The tree layout requires the root_vertex parameter") - def calc_pos(i): + + def calc_loc(i): if n == 1: - return (1, i+1) - k=0 - sum = 0 - while True: - sum += n**k - if i None: # """Sets the value of a node in the tree""" From 777312f37b80a3f0b9cd71c909703aab991a1d1f Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Tue, 7 Nov 2023 19:41:51 -0500 Subject: [PATCH 09/15] Edge creation for n-ary tree - breaks m_tree interface --- src/manim_data_structures/m_nary_tree.py | 118 ++++++++++++----------- src/manim_data_structures/m_tree.py | 75 +++++++------- 2 files changed, 97 insertions(+), 96 deletions(-) diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/m_nary_tree.py index 645820f..cf4b84a 100644 --- a/src/manim_data_structures/m_nary_tree.py +++ b/src/manim_data_structures/m_nary_tree.py @@ -6,61 +6,77 @@ from manim import WHITE, Graph, Mobject, VMobject +def _nary_layout( + T: nx.classes.graph.Graph, + vertex_spacing: tuple | None = None, + n: int | None = None, +): + if not n: + raise ValueError("the n-ary tree layout requires the n parameter") + if not nx.is_tree(T): + raise ValueError("The tree layout must be used with trees") + + max_height = N_ary_tree.calc_loc(max(T), n)[1] + + def calc_pos(x, y): + """ + Scales the coordinates to the desired spacing + """ + return (x - (n**y - 1) / 2) * vertex_spacing[0] * n ** ( + max_height - y + ), y * vertex_spacing[1] + + return { + i: np.array([x, y, 0]) + for i, (x, y) in ((i, calc_pos(*N_ary_tree.calc_loc(i, n))) for i in T) + } + + class N_ary_tree(Tree): def __init__( self, - nary_tree_list: list[Hashable], + nodes: dict[int, Any], num_child: int, vertex_type: Callable[..., Mobject], edge_buff=0.4, - layout_config={"vertex_spacing": (-1, 1)}, - root_vertex=0, + layout_config=None, **kwargs ): - n = num_child - - def n_ary_layout( - T: nx.classes.graph.Graph, - root_vertex: Hashable | None, - scale: float | tuple | None = 2, - vertex_spacing: tuple | None = None, - orientation: str = "down", - ): - if not n: - raise ValueError("the n-ary tree layout requires the n parameter") - if not nx.is_tree(T): - raise ValueError("The tree layout must be used with trees") - if root_vertex is None: - raise ValueError("The tree layout requires the root_vertex parameter") - - def calc_loc(i): - if n == 1: - return 1, i + 1 - height = int(np.emath.logn(n, i * (n - 1) + 1)) - node_shift = (1 - n**height) // (1 - n) - return i - node_shift, height - - max_height = calc_loc(max(T))[1] - - def calc_pos(x, y): - print(x, y) - return (x - (n**y - 1) / 2) * vertex_spacing[0] * n ** ( - max_height - y - ), y * vertex_spacing[1] - - return { - i: np.array([x, y, 0]) - for i, (x, y) in ((i, calc_pos(*calc_loc(i))) for i in T) - } - + if layout_config is None: + layout_config = {"vertex_spacing": (-1, 1)} self.num_child = num_child - height = 1 + int(np.log(len(nary_tree_list) + 1) / np.log(num_child)) - total_vertices = num_child**height - 1 - vertices = list(nary_tree_list) - edges = [(e // num_child, e + 1) for e in range(len(vertices) - 1)] - super().__init__(vertices, edges, vertex_type, edge_buff, **kwargs) - dict_layout = n_ary_layout(self._graph._graph, root_vertex, **layout_config) - self._graph.change_layout(dict_layout, root_vertex=root_vertex) + + edges = [(self.get_parent(e), e) for e in nodes if e != 0] + super().__init__(nodes, edges, vertex_type, edge_buff, **kwargs) + dict_layout = _nary_layout(self._graph._graph, n=num_child, **layout_config) + self._graph.change_layout(dict_layout) + + @staticmethod + def calc_loc(i, n): + """ + Calculates the coordinates in terms of the shifted level order x position and level height + """ + if n == 1: + return 1, i + 1 + height = int(np.emath.logn(n, i * (n - 1) + 1)) + node_shift = (1 - n**height) // (1 - n) + return i - node_shift, height + + @staticmethod + def calc_idx(loc, n): + """ + Calculates the index from the coordinates + """ + x, y = loc + if n == 1: + return y - 1 + + return int(x + (1 - n**y) // (1 - n)) + + def get_parent(self, idx): + x, y = N_ary_tree.calc_loc(idx, self.num_child) + new_loc = x // self.num_child, y - 1 + return N_ary_tree.calc_idx(new_loc, self.num_child) if __name__ == "__main__": @@ -69,23 +85,15 @@ class TestScene(Scene): def construct(self): # make a parent list for a tree tree = N_ary_tree( - list(range(19)), + {0: 0, 1: 1, 2: 2, 4: 4}, num_child=2, vertex_type=Integer, layout_config={"vertex_spacing": (0.75, -1)}, ) tree.shift(UP * 2) self.play(Create(tree)) - # for i in range(5): - # self.wait() - # tree.__insert__(2, i + 5) - # tree.__remove__(2) self.play(tree.animate) self.wait() - # self.play(tree._Tree__graph.vertices[0].animate.shift(UP * 2)) - # graph.change_layout('tree', root_vertex=0) - # self.play(Create(graph)) - # self.wait() config.preview = True config.renderer = "cairo" diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py index 2ad004e..4fb5eb3 100644 --- a/src/manim_data_structures/m_tree.py +++ b/src/manim_data_structures/m_tree.py @@ -21,7 +21,7 @@ class Tree(VMobject): def __init__( self, - vertices: list[Any], + nodes: dict[int, Any], edges: list[tuple[int, int]], vertex_type: Callable[..., Mobject], edge_buff=0.4, @@ -31,19 +31,20 @@ def __init__( **kwargs ): super().__init__(**kwargs) - vertex_mobjects = {i: vertex_type(v) for i, v in enumerate(vertices)} + vertex_mobjects = {k: vertex_type(v) for k, v in nodes.items()} self.__layout_config = layout_config - self.__layout_scale = len(vertices) * 0.5 + self.__layout_scale = len(nodes) * 0.5 self.__vertex_type = vertex_type - + print(nodes) + print(edges) self._graph = Graph( - list(range(len(vertices))), + list(nodes), edges, vertex_mobjects=vertex_mobjects, layout=layout, root_vertex=0, layout_config=self.__layout_config, - layout_scale=len(vertices) * 0.5, + layout_scale=len(nodes) * 0.5, edge_config={"stroke_width": 1, "stroke_color": WHITE}, ) @@ -92,32 +93,32 @@ def update_edges(graph: Graph): # if parent == __index: # self.__graph.add_edges((__index, i)) - def __insert__(self, parent_index: int, value: Any) -> None: - """Inserts a node into the tree""" - self.__graph.add_vertices( - len(self.__graph.vertices), - vertex_mobjects={len(self.__graph.vertices): self.__vertex_type(value)}, - ) - self.__graph.add_edges((parent_index, len(self.__graph.vertices) - 1)) - self.__graph.change_layout( - "tree", - root_vertex=0, - layout_config=self.__layout_config, - layout_scale=self.__layout_scale, - ) - # self.__graph.update() - - # def __animate_insert__(self, ): - def __remove__(self, index: int) -> None: - """Removes a node from the tree""" - self.__graph.remove_vertices(index) - self.__graph.change_layout( - "tree", - root_vertex=0, - layout_config=self.__layout_config, - layout_scale=self.__layout_scale, - ) - self.__graph.update() + # def __insert__(self, parent_index: int, value: Any) -> None: + # """Inserts a node into the tree""" + # self.__graph.add_vertices( + # len(self.__graph.vertices), + # vertex_mobjects={len(self.__graph.vertices): self.__vertex_type(value)}, + # ) + # self.__graph.add_edges((parent_index, len(self.__graph.vertices) - 1)) + # self.__graph.change_layout( + # "tree", + # root_vertex=0, + # layout_config=self.__layout_config, + # layout_scale=self.__layout_scale, + # ) + # # self.__graph.update() + # + # # def __animate_insert__(self, ): + # def __remove__(self, index: int) -> None: + # """Removes a node from the tree""" + # self.__graph.remove_vertices(index) + # self.__graph.change_layout( + # "tree", + # root_vertex=0, + # layout_config=self.__layout_config, + # layout_scale=self.__layout_scale, + # ) + # self.__graph.update() if __name__ == "__main__": @@ -125,18 +126,10 @@ def __remove__(self, index: int) -> None: class TestScene(Scene): def construct(self): # make a parent list for a tree - tree = Tree([0, 1, 2, 3, 5], [(0, 1), (0, 2), (1, 3), (1, 4)], Integer) + tree = Tree([0, 1, 2, 3], [(0, 1), (0, 2), (1, 3)], Integer) self.play(Create(tree)) - # for i in range(5): - # self.wait() - # tree.__insert__(2, i + 5) - tree.__remove__(2) self.play(tree.animate) self.wait() - # self.play(tree._Tree__graph.vertices[0].animate.shift(UP * 2)) - # graph.change_layout('tree', root_vertex=0) - # self.play(Create(graph)) - # self.wait() config.preview = True config.renderer = "cairo" From 39556b7416dfa580c2b19ff9ae56840f9d34fc51 Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Tue, 28 Nov 2023 19:00:53 -0500 Subject: [PATCH 10/15] insert and removal for tree --- src/manim_data_structures/m_nary_tree.py | 24 ++++++-- src/manim_data_structures/m_tree.py | 70 +++++------------------- 2 files changed, 33 insertions(+), 61 deletions(-) diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/m_nary_tree.py index cf4b84a..6f01539 100644 --- a/src/manim_data_structures/m_nary_tree.py +++ b/src/manim_data_structures/m_nary_tree.py @@ -44,6 +44,7 @@ def __init__( ): if layout_config is None: layout_config = {"vertex_spacing": (-1, 1)} + self.__layout_config = layout_config self.num_child = num_child edges = [(self.get_parent(e), e) for e in nodes if e != 0] @@ -74,10 +75,22 @@ def calc_idx(loc, n): return int(x + (1 - n**y) // (1 - n)) def get_parent(self, idx): + """ + Returns the index of the parent of the node at the given index + """ x, y = N_ary_tree.calc_loc(idx, self.num_child) new_loc = x // self.num_child, y - 1 return N_ary_tree.calc_idx(new_loc, self.num_child) + def insert_node(self, node: Any, index: Hashable): + """Inserts a node into the graph""" + res = super().insert_node(node, (self.get_parent(index), index)) + dict_layout = _nary_layout( + self._graph._graph, n=self.num_child, **self.__layout_config + ) + self._graph.change_layout(dict_layout) + return res + if __name__ == "__main__": @@ -85,14 +98,17 @@ class TestScene(Scene): def construct(self): # make a parent list for a tree tree = N_ary_tree( - {0: 0, 1: 1, 2: 2, 4: 4}, + {0: 0, 1: 1, 4: 4}, num_child=2, vertex_type=Integer, - layout_config={"vertex_spacing": (0.75, -1)}, + layout_config={"vertex_spacing": (1, -1)}, ) - tree.shift(UP * 2) + tree.insert_node(1, 3) + tree.remove_node(4) + # tree._graph.change_layout(root_vertex=0, layout_config=tree._Tree__layout_config, + # layout_scale=tree._Tree__layout_scale) self.play(Create(tree)) - self.play(tree.animate) + self.wait() config.preview = True diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py index 4fb5eb3..7cb002e 100644 --- a/src/manim_data_structures/m_tree.py +++ b/src/manim_data_structures/m_tree.py @@ -14,6 +14,7 @@ class Tree(VMobject): _graph: Graph __layout_config: dict __layout_scale: float + __layout: str | dict __vertex_type: Callable[..., Mobject] # __parents: list @@ -34,9 +35,8 @@ def __init__( vertex_mobjects = {k: vertex_type(v) for k, v in nodes.items()} self.__layout_config = layout_config self.__layout_scale = len(nodes) * 0.5 + self.__layout = layout self.__vertex_type = vertex_type - print(nodes) - print(edges) self._graph = Graph( list(nodes), edges, @@ -48,20 +48,6 @@ def __init__( edge_config={"stroke_width": 1, "stroke_color": WHITE}, ) - # if edge_buff is not None: - # for (u, v), edge in self.__graph.edges.items(): - # buff_vec = ( - # edge_buff - # * (self.__graph[u].get_center() - self.__graph[v].get_center()) - # / np.linalg.norm( - # self.__graph[u].get_center() - self.__graph[v].get_center() - # ) - # ) - # edge.put_start_and_end_on( - # self.__graph[u].get_center() - buff_vec, - # self.__graph[v].get_center() + buff_vec, - # ) - def update_edges(graph: Graph): """Updates edges of graph""" for (u, v), edge in graph.edges.items(): @@ -78,47 +64,16 @@ def update_edges(graph: Graph): self._graph.updaters.append(update_edges) self.add(self._graph) - # def __setitem__(self, __index: Hashable, __value: Mobject) -> None: - # """Sets the value of a node in the tree""" - # node_center = self.__graph[__index].get_center() - # node_parent = self.__parents[__index] - # self.__graph.remove_vertices(__index) - # self.__graph.add_vertices( - # __index, - # vertex_mobjects={__index: __value}, - # positions={__index: node_center}, - # ) - # self.__graph.add_edges((node_parent, __index)) - # for i, parent in enumerate(self.__parents): - # if parent == __index: - # self.__graph.add_edges((__index, i)) + def insert_node(self, node: Any, edge: tuple[Hashable, Hashable]): + """Inserts a node into the graph as (parent, node)""" + self._graph.add_vertices( + edge[1], vertex_mobjects={edge[1]: self.__vertex_type(node)} + ) + self._graph.add_edges(edge) - # def __insert__(self, parent_index: int, value: Any) -> None: - # """Inserts a node into the tree""" - # self.__graph.add_vertices( - # len(self.__graph.vertices), - # vertex_mobjects={len(self.__graph.vertices): self.__vertex_type(value)}, - # ) - # self.__graph.add_edges((parent_index, len(self.__graph.vertices) - 1)) - # self.__graph.change_layout( - # "tree", - # root_vertex=0, - # layout_config=self.__layout_config, - # layout_scale=self.__layout_scale, - # ) - # # self.__graph.update() - # - # # def __animate_insert__(self, ): - # def __remove__(self, index: int) -> None: - # """Removes a node from the tree""" - # self.__graph.remove_vertices(index) - # self.__graph.change_layout( - # "tree", - # root_vertex=0, - # layout_config=self.__layout_config, - # layout_scale=self.__layout_scale, - # ) - # self.__graph.update() + def remove_node(self, node: Hashable): + """Removes a node from the graph""" + self._graph.remove_vertices(node) if __name__ == "__main__": @@ -126,7 +81,8 @@ def update_edges(graph: Graph): class TestScene(Scene): def construct(self): # make a parent list for a tree - tree = Tree([0, 1, 2, 3], [(0, 1), (0, 2), (1, 3)], Integer) + tree = Tree({0: 0, 1: 1, 2: 2, 3: 3}, [(0, 1), (0, 2), (1, 3)], Integer) + tree.insert_node(4, (2, 4)) self.play(Create(tree)) self.play(tree.animate) self.wait() From af09f8cfbaefcb761bc322621a318e5077d13515 Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Tue, 13 Feb 2024 17:33:12 -0500 Subject: [PATCH 11/15] open anim --- src/manim_data_structures/m_nary_tree.py | 54 ++++++++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/m_nary_tree.py index 6f01539..823b347 100644 --- a/src/manim_data_structures/m_nary_tree.py +++ b/src/manim_data_structures/m_nary_tree.py @@ -98,17 +98,53 @@ class TestScene(Scene): def construct(self): # make a parent list for a tree tree = N_ary_tree( - {0: 0, 1: 1, 4: 4}, - num_child=2, - vertex_type=Integer, - layout_config={"vertex_spacing": (1, -1)}, + { + 0: "*", + 1: "O", + 2: "P", + 3: "E", + 4: "N", + }, + num_child=4, + vertex_type=Text, + layout_config={"vertex_spacing": (0.6, -1)}, ) - tree.insert_node(1, 3) - tree.remove_node(4) - # tree._graph.change_layout(root_vertex=0, layout_config=tree._Tree__layout_config, - # layout_scale=tree._Tree__layout_scale) - self.play(Create(tree)) + # Construct \ + self.play(Create(tree)) + self.wait(0.2) + # Shift tree up + tree.insert_node("A", 5) + self.play(tree.animate.shift(2 * UP)) + self.wait(0.2) + # Apply a shaking effect + self.play( + Rotate( + tree, + angle=PI / 6, + about_point=ORIGIN, + rate_func=linear, + run_time=0.3, + ) + ) + self.play( + Rotate( + tree, + angle=-PI / 3, + about_point=ORIGIN, + rate_func=linear, + run_time=0.5, + ) + ) + self.play( + Rotate( + tree, + angle=PI / 6, + about_point=ORIGIN, + rate_func=linear, + run_time=0.3, + ) + ) self.wait() config.preview = True From 00c129a086eb6ce9e933eedb7a5eef91415b2fce Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Tue, 13 Feb 2024 18:01:04 -0500 Subject: [PATCH 12/15] fixed n-ary insertion issue --- src/manim_data_structures/m_nary_tree.py | 57 +++++------------------- 1 file changed, 11 insertions(+), 46 deletions(-) diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/m_nary_tree.py index 823b347..3b10a68 100644 --- a/src/manim_data_structures/m_nary_tree.py +++ b/src/manim_data_structures/m_nary_tree.py @@ -89,6 +89,7 @@ def insert_node(self, node: Any, index: Hashable): self._graph._graph, n=self.num_child, **self.__layout_config ) self._graph.change_layout(dict_layout) + self.update() return res @@ -96,55 +97,19 @@ def insert_node(self, node: Any, index: Hashable): class TestScene(Scene): def construct(self): - # make a parent list for a tree tree = N_ary_tree( - { - 0: "*", - 1: "O", - 2: "P", - 3: "E", - 4: "N", - }, - num_child=4, - vertex_type=Text, - layout_config={"vertex_spacing": (0.6, -1)}, + {0: 0, 1: 1, 4: 4}, + num_child=2, + vertex_type=Integer, + layout_config={"vertex_spacing": (1, -1)}, ) - # Construct \ - + # tree._graph.change_layout(root_vertex=0, layout_config=tree._Tree__layout_config, + # layout_scale=tree._Tree__layout_scale) self.play(Create(tree)) - self.wait(0.2) - # Shift tree up - tree.insert_node("A", 5) - self.play(tree.animate.shift(2 * UP)) - self.wait(0.2) - # Apply a shaking effect - self.play( - Rotate( - tree, - angle=PI / 6, - about_point=ORIGIN, - rate_func=linear, - run_time=0.3, - ) - ) - self.play( - Rotate( - tree, - angle=-PI / 3, - about_point=ORIGIN, - rate_func=linear, - run_time=0.5, - ) - ) - self.play( - Rotate( - tree, - angle=PI / 6, - about_point=ORIGIN, - rate_func=linear, - run_time=0.3, - ) - ) + self.wait() + tree.insert_node(1, 3) + self.wait() + tree.remove_node(4) self.wait() config.preview = True From e4c327b9079dd241ebdac7b76737ca2f51cb765d Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Tue, 13 Feb 2024 18:05:48 -0500 Subject: [PATCH 13/15] updated git ignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fc56ef9..a2b0785 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ generated .vscode rendering_times.csv media/ -.coverage +venv +.coverage .python-version From bdee839d41be32f19a2367781fe4be68fa1a6202 Mon Sep 17 00:00:00 2001 From: levyjonathan31 Date: Tue, 13 Feb 2024 18:30:19 -0500 Subject: [PATCH 14/15] work in progress m_tree insertion animation --- src/manim_data_structures/m_tree.py | 55 +++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py index 7cb002e..625ac5a 100644 --- a/src/manim_data_structures/m_tree.py +++ b/src/manim_data_structures/m_tree.py @@ -1,6 +1,8 @@ +import operator as op import random from collections import defaultdict from copy import copy +from functools import partialmethod, reduce from typing import Any, Callable, Dict, Hashable, List, Tuple import numpy as np @@ -70,11 +72,60 @@ def insert_node(self, node: Any, edge: tuple[Hashable, Hashable]): edge[1], vertex_mobjects={edge[1]: self.__vertex_type(node)} ) self._graph.add_edges(edge) + return self + + def insert_node2(self, node: Any, edge: tuple[Hashable, Hashable]): + """Inserts a node into the graph as (parent, node)""" + self._graph.change_layout( + self.__layout, + layout_scale=self.__layout_scale, + layout_config=self.__layout_config, + root_vertex=0, + ) + for mob in self.family_members_with_points(): + if (mob.get_center() == self._graph[edge[1]].get_center()).all(): + mob.points = mob.points.astype("float") + return self + + def insert_node3(self, node: Any, edge: tuple[Hashable, Hashable]): + """Inserts a node into the graph as (parent, node)""" + self.suspend_updating() + self.insert_node(node, edge) + # self.resume_updating() + self.insert_node2(node, edge) + + return self def remove_node(self, node: Hashable): """Removes a node from the graph""" self._graph.remove_vertices(node) + # def insert_node2(self): + # """Shift by the given vectors. + # + # Parameters + # ---------- + # vectors + # Vectors to shift by. If multiple vectors are given, they are added + # together. + # + # Returns + # ------- + # :class:`Mobject` + # ``self`` + # + # See also + # -------- + # :meth:`move_to` + # """ + # + # total_vector = reduce(op.add, vectors) + # for mob in self.family_members_with_points(): + # mob.points = mob.points.astype("float") + # mob.points += total_vector + # + # return self + if __name__ == "__main__": @@ -82,9 +133,9 @@ class TestScene(Scene): def construct(self): # make a parent list for a tree tree = Tree({0: 0, 1: 1, 2: 2, 3: 3}, [(0, 1), (0, 2), (1, 3)], Integer) - tree.insert_node(4, (2, 4)) self.play(Create(tree)) - self.play(tree.animate) + self.wait() + self.play(tree.animate.insert_node3(4, (2, 4)), run_time=0) self.wait() config.preview = True From 3352a47d9db1f0af3069179c613f7ec8b3b8c418 Mon Sep 17 00:00:00 2001 From: Nikhil Iyer Date: Tue, 29 Oct 2024 17:54:47 -0400 Subject: [PATCH 15/15] Refactor and clean up NaryTree --- .../{m_nary_tree.py => nary_tree.py} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename src/manim_data_structures/{m_nary_tree.py => nary_tree.py} (88%) diff --git a/src/manim_data_structures/m_nary_tree.py b/src/manim_data_structures/nary_tree.py similarity index 88% rename from src/manim_data_structures/m_nary_tree.py rename to src/manim_data_structures/nary_tree.py index 3b10a68..0c23737 100644 --- a/src/manim_data_structures/m_nary_tree.py +++ b/src/manim_data_structures/nary_tree.py @@ -1,9 +1,8 @@ -from typing import Hashable +from typing import Any, Callable, Hashable import networkx as nx from m_tree import Tree -from manim import * -from manim import WHITE, Graph, Mobject, VMobject +from manim import Mobject def _nary_layout( @@ -16,7 +15,7 @@ def _nary_layout( if not nx.is_tree(T): raise ValueError("The tree layout must be used with trees") - max_height = N_ary_tree.calc_loc(max(T), n)[1] + max_height = NaryTree.calc_loc(max(T), n)[1] def calc_pos(x, y): """ @@ -28,11 +27,11 @@ def calc_pos(x, y): return { i: np.array([x, y, 0]) - for i, (x, y) in ((i, calc_pos(*N_ary_tree.calc_loc(i, n))) for i in T) + for i, (x, y) in ((i, calc_pos(*NaryTree.calc_loc(i, n))) for i in T) } -class N_ary_tree(Tree): +class NaryTree(Tree): def __init__( self, nodes: dict[int, Any], @@ -78,9 +77,9 @@ def get_parent(self, idx): """ Returns the index of the parent of the node at the given index """ - x, y = N_ary_tree.calc_loc(idx, self.num_child) + x, y = NaryTree.calc_loc(idx, self.num_child) new_loc = x // self.num_child, y - 1 - return N_ary_tree.calc_idx(new_loc, self.num_child) + return NaryTree.calc_idx(new_loc, self.num_child) def insert_node(self, node: Any, index: Hashable): """Inserts a node into the graph""" @@ -94,10 +93,11 @@ def insert_node(self, node: Any, index: Hashable): if __name__ == "__main__": + from manim import * class TestScene(Scene): def construct(self): - tree = N_ary_tree( + tree = NaryTree( {0: 0, 1: 1, 4: 4}, num_child=2, vertex_type=Integer,