ManimCommunity / manim

A community-maintained Python framework for creating mathematical animations.
https://www.manim.community
MIT License
26.81k stars 1.84k forks source link

`Table.get_cell()` returns a range of cells instead of a single table cell? #4025

Open v-poghosyan opened 5 days ago

v-poghosyan commented 5 days ago

Description of bug / unexpected behavior

Hello, I have this bit of Manim code that gets a 2D matrix and visualizes DFS starting from a given source (start_x, start_y). However,the animation that's produced doesn't make sense. I am not sure if this is a bug, or if I'm doing something wrong but I'm leaning towards bug because of the following reason:

Inside a given iteration of the while loop of DFS, x,y are always just a pair of natural numbers. But get_cell((x,y)) seems to give a whole cluster of cells...

Here's the code, the first line is just me using manim-jupyter to render Scenes inside Jupyter notes:

%%manim -qm ConnectedSinksAndSourcesDemo

from manim import *

class ConnectedSinksAndSourcesDemo(Scene):
    def construct(self):    
               # Create table
        table = Table(simple_expanded_input)
        # A small modification to align the Manim table to the formatted input
        table.rotate(90 * DEGREES)
        for cell in table.get_entries():
            cell.rotate(-90 * DEGREES)
        # Animation sequence
        self.play(Write(table))
        self.dfs_iterative(expanded_input, table, 0, 2) # The DFS algorithm still requires the original `expanded_input` to work
        self.wait(5)

    # Same DFS algorthm as before, but modified to inject Manim effects...
    def dfs_iterative(self, grid, manim_table, start_x, start_y):
        dim_cols, dim_rows = len(grid), len(grid[0])

        # Initialize the visited set
        visited = set()
        # Initialize the stack with the starting position
        stack = [(start_x, start_y)]

        while stack:
            x, y = stack.pop()

            if (x, y) in visited:
                continue

            visited.add((x, y))
            self.play(manim_table.get_cell((x,y)).animate.set_fill(GREEN))

            if y + 1 >= 0 and y + 1 <= dim_rows - 1 and grid[x][y + 1] is not None:
                if connected_at_top(grid[x][y], grid[x][y + 1]):
                    stack.append((x, y + 1))
            # Check the bottom neighbor
            if y - 1 >= 0 and y - 1 <= dim_rows - 1 and grid[x][y - 1] is not None: 
                if connected_at_bottom(grid[x][y], grid[x][y - 1]):
                    stack.append((x, y - 1))
            # Check the right neighbor
            if x + 1 >= 0 and x + 1 <= dim_cols - 1 and grid[x + 1][y] is not None:
                if connected_at_right(grid[x][y], grid[x + 1][y]):
                    stack.append((x + 1, y))
            # Check the left neighbor
            if x - 1 >= 0 and x - 1 <=  dim_cols - 1 and grid[x - 1][y] is not None:
                if connected_at_left(grid[x][y], grid[x - 1][y]):
                    stack.append((x - 1, y))

Expected behavior

I would expect get_cell((x,y)) inside self.play(manim_table.get_cell((x,y)).animate.set_fill(GREEN)) to set the fill of once cell, rather than a bunch of them at once. I would also expect the color to be GREEN and for the fill, as opposed to the strokeof the cells, to be affected.

Let's focus on the first iteration of the while loop. x=2 and y=0. Yet, in the very first frame the table looks like:

Screenshot 2024-11-23 at 5 28 38 PM

How to reproduce the issue

Code for reproducing the problem ```py from manim import * class ConnectedSinksAndSourcesDemoV2(Scene): def construct(self): # Create table table = Table(simple_expanded_input) # A small modification to align the Manim table to the formatted input table.rotate(90 * DEGREES) for cell in table.get_entries(): cell.rotate(-90 * DEGREES) # Animation sequence self.play(Write(table)) self.highlight_row(expanded_input, table, 0, 2) # The DFS algorithm still requires the original `expanded_input` to work self.wait(5) # Same DFS algorthm as before, but modified to inject Manim effects... def highlight_row(self, grid, manim_table, start_x, start_y): self.play(manim_table.get_cell((start_x,start_y)).animate.set_fill(GREEN)) ```

Additional media files

The output of the minimal code to reproduce the problem is consistent with the finding.

Images/GIFs ![Screenshot 2024-11-23 at 5 42 42 PM](https://github.com/user-attachments/assets/985da065-8ef3-483b-bf47-1a28f130d436)

Logs

Terminal output ``` PASTE HERE OR PROVIDE LINK TO https://pastebin.com/ OR SIMILAR ```

System specifications

System Details - OS (with version, e.g., macOS M3 (Catalina)): - RAM: - Python version (`python/py/python3 --version`): Python 3.9.20 - Installed modules (provide output from `pip list`): ``` Package Version ---------------------- ----------- appnope 0.1.4 asttokens 2.4.1 click 8.1.7 cloup 3.0.5 colour 0.1.5 comm 0.2.2 Cython 3.0.11 debugpy 1.8.8 decorator 5.1.1 exceptiongroup 1.2.2 executing 2.1.0 glcontext 3.0.0 importlib_metadata 8.5.0 ipykernel 6.29.5 ipython 8.18.1 isosurfaces 0.1.2 jedi 0.19.2 jupyter_client 8.6.3 jupyter_core 5.7.2 jupyter-manim 1.3 manim 0.18.1 manimlib 0.2.0 ManimPango 0.6.0 mapbox_earcut 1.0.2 markdown-it-py 3.0.0 matplotlib-inline 0.1.7 mdurl 0.1.2 moderngl 5.12.0 moderngl-window 2.4.6 multipledispatch 1.0.0 nest-asyncio 1.6.0 networkx 3.2.1 numpy 1.26.4 opencv-python 4.10.0.84 packaging 24.2 parso 0.8.4 pexpect 4.9.0 pillow 11.0.0 pip 24.2 platformdirs 4.3.6 progressbar 2.5 prompt_toolkit 3.0.48 psutil 6.1.0 ptyprocess 0.7.0 pure_eval 0.2.3 pycairo 1.27.0 pydub 0.25.1 pyglet 2.0.18 Pygments 2.18.0 pyobjc-core 10.3.1 pyobjc-framework-Cocoa 10.3.1 pyrr 0.10.3 python-dateutil 2.9.0.post0 pyzmq 26.2.0 rich 13.9.4 scipy 1.13.1 screeninfo 0.8.1 setuptools 74.1.2 six 1.16.0 skia-pathops 0.8.0.post2 srt 3.5.3 stack-data 0.6.3 svgelements 1.9.6 tornado 6.4.1 tqdm 4.67.0 traitlets 5.14.3 typing_extensions 4.12.2 watchdog 6.0.0 wcwidth 0.2.13 wheel 0.44.0 zipp 3.21.0```
LaTeX details + LaTeX distribution (e.g. TeX Live 2020): + Installed LaTeX packages:

Additional comments

uwezi commented 4 days ago

Please give working example codes - in your second code still the definition of your expanded_input is not given.

But apart from that I can copy your observations with the following code, but also note that cell numbering in Manim starts at 1 and not at 0:

class ConnectedSinksAndSourcesDemoV2(Scene):
    def construct(self):
        # Create table
        expanded_input=[["A","B","C"],["D","E","F"],["G","H","I"],]
        table = Table(expanded_input)
        # A small modification to align the Manim table to the formatted input
        table.rotate(90 * DEGREES)
        for cell in table.get_entries():
            cell.rotate(-90 * DEGREES)
        # Animation sequence
        self.play(Write(table))
        self.highlight_row(expanded_input, table, 1, 2) # The DFS algorithm still requires the original `expanded_input` to work
        self.wait(5)

    # Same DFS algorthm as before, but modified to inject Manim effects...
    def highlight_row(self, grid, manim_table, start_x, start_y):
            self.play(manim_table.get_cell([start_x,start_y]).animate.set_fill(GREEN, opacity=0.6))

image

However, the problem is not present when you don't rotate the table:

class ConnectedSinksAndSourcesDemoV2(Scene):
    def construct(self):
        # Create table
        expanded_input=[["A","B","C"],["D","E","F"],["G","H","I"],]
        table = Table(expanded_input)
        # A small modification to align the Manim table to the formatted input
        #table.rotate(90 * DEGREES)
        #for cell in table.get_entries():
        #    cell.rotate(-90 * DEGREES)
        # Animation sequence
        self.play(Write(table))
        self.highlight_row(expanded_input, table, 1, 2) # The DFS algorithm still requires the original `expanded_input` to work
        self.wait(5)

    # Same DFS algorthm as before, but modified to inject Manim effects...
    def highlight_row(self, grid, manim_table, start_x, start_y):
            self.play(manim_table.get_cell([start_x,start_y]).animate.set_fill(GREEN, opacity=0.6))

image

I assume no developer has ever tested the code on a rotated table with counter-rotated cells. Since it also would naturally fail on tables which are not rotated by a full multiple of 90 degrees (it returns a non-rotated rectangle) the simple solution to your problem would be to not rotate your table but only the elements beforehand:

class ConnectedSinksAndSourcesDemoV2(Scene):
    def construct(self):
        # Create table
        raw=[["A","B","C"],["D","E","F"],["G","H","I"],]
        expanded_input = [[raw[i][2-j] for i in range(3)] for j in range(3)]
        table = Table(expanded_input)
        # A small modification to align the Manim table to the formatted input
        #table.rotate(90 * DEGREES)
        #for cell in table.get_entries():
        #    cell.rotate(-90 * DEGREES)
        # Animation sequence
        self.play(Write(table))
        self.highlight_row(expanded_input, table, 1, 2) # The DFS algorithm still requires the original `expanded_input` to work
        self.wait(5)

    # Same DFS algorthm as before, but modified to inject Manim effects...
    def highlight_row(self, grid, manim_table, start_x, start_y):
            self.play(manim_table.get_cell([start_x,start_y]).animate.set_fill(GREEN, opacity=0.6))

image