manim icon indicating copy to clipboard operation
manim copied to clipboard

Added `colorscale` to `axes.plot()`

Open alembcke opened this issue 2 years ago • 15 comments

Overview: What does this pull request change?

Added colorscale and colorscale_axis to axes.plot() to allow for colorscales by value for line plots.

Motivation and Explanation: Why and how do your changes improve the library?

Adds functionality to the library consistent with Surface and OpenGLSurface.

Links to added or changed documentation pages

https://manimce--3148.org.readthedocs.build/en/3148/reference/manim.mobject.graphing.coordinate_systems.CoordinateSystem.html#manim.mobject.graphing.coordinate_systems.CoordinateSystem.plot

Further Information and Comments

Example code:

from manim import *

class PlotExample(Scene):
    def construct(self):
        # construct the axes
        axes = Axes(
            x_range=[0, 6],
            y_range=[-2, 2],
            tips=False,
        )

        curve = axes.plot(
                lambda x: 2 * np.sin(x), x_range=(0.0, 6, 0.001), colorscale=[BLUE, GREEN, YELLOW, ORANGE, RED]
        )

        self.add(axes, curve)

PlotExample_ManimCE_v0 17 2

Reviewer Checklist

  • [ ] The PR title is descriptive enough for the changelog, and the PR is labeled correctly
  • [ ] If applicable: newly added non-private functions and classes have a docstring including a short summary and a PARAMETERS section
  • [ ] If applicable: newly added functions and classes are tested

alembcke avatar Feb 04 '23 18:02 alembcke

What about the pivots? To me those are key as I set values below 0 to be one color and above 0 to be another color. Is it possible to set pivots with the sheen direction?

alembcke avatar Mar 09 '23 11:03 alembcke

What about the pivots? To me those are key as I set values below 0 to be one color and above 0 to be another color. Is it possible to set pivots with the sheen direction?

True! I don't think our current implementation of gradients allows to manually set pivots. Maybe this is something that we could consider after we have #3020 and implement our own ManimColorGradient class... Until then, your "manual" implementation might be a more suitable way to go.

behackl avatar Mar 28 '23 20:03 behackl

Added a test to round out this PR.

@behackl is there anything else I need to do to get this PR in shape to be merged? Or are we waiting on #3020 to be merged so that we implement ManimColorGradient? If so, any idea on when that would happen?

alembcke avatar Jul 21 '23 14:07 alembcke

It doesn't work with opengl renderer

  • CurvesAsSubmobjects doesn't seem to work in OpenGL

MrDiver avatar Aug 04 '23 22:08 MrDiver

Can i just say this is hellishly slow ? I am trying to render this and it takes about a second on a good run i think that might be problematic for a lot of cases if people really wanted to use it

grafik

That is the output i can currently achieve when i use it with opengl. I am not quite sure how your solution works but there might still be something missing.

grafik

We basically spend 90% of the time just creating mobjects

grafik

In cairo this doesn't happen for some reason. But 6 seconds for a singular curve is pretty slow in my opinion.

MrDiver avatar Aug 04 '23 22:08 MrDiver

It takes 155 seconds in cairo to draw this. I don't think this is acceptable tbh. grafik

https://github.com/ManimCommunity/manim/assets/17803932/28b29753-4dd8-4ae4-9c67-8ad10504e16e

MrDiver avatar Aug 04 '23 23:08 MrDiver

I would suggest a solution which somehow exploits curve.set_stroke([BLUE, RED, GREEN, YELLOW]) because that might be the faster way to change the colors without making thousands of little mobjects when it's not needed.

MrDiver avatar Aug 04 '23 23:08 MrDiver

@MrDiver the number of sub-mobjects created is up to the user. It comes from x_range=(0.0, 6, 0.001) in axes.plot(). I choose 0.001 to ensure a nice smooth transition of colors. But changing it to 0.1 still results in a decent transition of colors and takes a fraction of the time:

CurveExample_ManimCE_v0 17 3

Is that a reasonable solution? It still doesn't work in OpenGL, but that as you point out is because CurvesAsSubmobjects doesn't work in OpenGL and so the appropriate solution would be to get CurvesAsSubmobjects working in OpenGL in a separate PR.

As for using curve.set_stroke([BLUE, RED, GREEN, YELLOW]), I don't see how that can be done while allowing to set at least a mid-point for the colors. I say that as I often set certain colors for less than 0 and others for greater than 0. To me this is necessary and is already how Surface and OpenGLSurface work with colorscales (as well as other charting libraries like Plotly). I think it is worth it to discuss how colorscales should be implemented across all three, as there is some duplication of code. That is why I asked @behackl above about #3020 and implementing ManimColorGradient, as I would prefer that colorscales be implemented there and then each of these just use that implementation.

alembcke avatar Aug 05 '23 18:08 alembcke

I already got the OpenGL thing to work, that's why i have the flamegraph for the 95 seconds. That's what it takes to render this I still think it is a little bit much to create so many mobjects for that. As far as i understand it colors may be applied independently of the number of mobjects.

MrDiver avatar Aug 05 '23 20:08 MrDiver

@alembcke

class PlotExample(Scene):
    def construct(self):
        # construct the axes
        axes = Axes(
            x_range=[0, 6],
            y_range=[-2, 2],
            tips=False,
        )

        curve = axes.plot(
            lambda x: 2 * np.sin(x), x_range=(0.0, 6), use_vectorized=True
        )
        colors = [
            "#ff0000",
            "#ffa500",
            "#ffff00",
            "#008000",
            "#0000ff",
            "#4b0082",
            "#ee82ee",
        ]
        # curve.set_stroke(colors)
        curve.set_color(colors)
        # curve.set_fill(colors, opacity=1)
        self.play(Write(axes), Write(curve))
        self.play(curve.animate.set_sheen_direction(UP))
        self.play(curve.animate.set_sheen_direction(RIGHT))
        self.play(curve.animate.set_sheen_direction(LEFT))
        self.wait()

You might wanna take a look at this 👀 i just realized you can do that so it might make realizing the color stuff a lot easier because it is not dependend on the number of curves

https://github.com/ManimCommunity/manim/assets/17803932/fd0eb1b1-30f1-4cc6-ad4a-aaf2d8f96163

MrDiver avatar Aug 05 '23 20:08 MrDiver

https://github.com/ManimCommunity/manim/assets/17803932/8cdf0038-ab4b-4f78-a19b-90e941f88f20

And you can scale the amount of colors without increasing the rendering time at all.

grafik For 100 different colors the whole animation not just an image

Doing only an image: grafik

So instead of generating all the curves and then iterating over them and setting all the fancy colors i would suggest just generating a color list and then setting the colors directly

Or we might just take a look at the implementation of that and see if we can borrow some color setting things such that you can get a custom pivot.

MrDiver avatar Aug 05 '23 20:08 MrDiver

I added some changes to the PR, you can check if they have the same behavior that you intended. I just reused all of your code. I also added a test to check if the color axis parameter actually works so that it doesn't get missed in the graphical tests.

MrDiver avatar Aug 05 '23 22:08 MrDiver

Just to reiterate here what was discussed on Discord, I rendered a bunch of videos I have that use this functionality and they all behave the same way as before but render much faster.

alembcke avatar Aug 05 '23 22:08 alembcke

This is weird because the tests pass on my machine. I even deleted the folder with Manim in it and did a git clone and reran the tests - they still pass.

I double and triple checked that I am on the line_gradient branch and I ran poetry install and poetry run pytest. Am I doing anything wrong here?

alembcke avatar Dec 17 '23 15:12 alembcke

I double and triple checked that I am on the line_gradient branch and I ran poetry install and poetry run pytest. Am I doing anything wrong here?

Maybe you also needed to run poetry shell to enter your environment :thinking:

As MrDiver said: the tests needed an update, because they stopped working after certain commit in main. Don't worry: I already updated the tests myself and your PR has passed all checks :wink:

chopan050 avatar Dec 21 '23 06:12 chopan050