ManimCommunity / manim

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

Incorrect graphing? #1126

Closed 6r1d closed 10 months ago

6r1d commented 3 years ago

Description of bug / unexpected behavior

Manim's GraphScene output is showing some artefacts in one context: "calculating modulo of 1.0" or x % 1.0.

I'll compare it to GeoGebra, because I have a GeoGebra tab open, it has a mod command and it shows a different result.

There's some "ripple" on the function output I'd like to fix. Also, function output reaches values less than 0. Also, I don't mind the vertical lines.

Expected behavior

GeoGebra shows (I believe) a correct plot.

How to reproduce the issue

Code for reproducing the problem ```python from manim import * import numpy as np class PhaseValuePlot(GraphScene): def __init__(self, **kwargs): GraphScene.__init__( self, x_min=-3, x_max=3, num_graph_anchor_points=100, y_min=-2.0, y_max=2.0, graph_origin=ORIGIN, axes_color=WHITE, x_labeled_nums=range(-3, 3, 1), y_axis_label="Phase [p]", x_axis_label="Time [T]", **kwargs ) self.function_color = BLUE def construct(self): self.setup_axes() func_graph = self.get_graph(lambda t: t % 1.0, x_min=-2, x_max=2, discontinuities=[1], color=self.function_color) phase_formula = MathTex(r"p = T \mod 1.0") phase_formula.next_to(func_graph, RIGHT + UP) self.play(ShowCreation(self.axes)) self.play(Write(phase_formula)) self.play(Write(func_graph)) self.wait(3) self.play(FadeOut(func_graph)) self.play(FadeOut(self.axes)) ```

Additional media files

What Manim shows:

System specifications

System Details - OS: Ubuntu Bionic 18.04.5 LTS - RAM: 8 Gb - Python version: Python 3.6.9 - Installed modules: ``` absl-py 0.7.1 aiohttp 3.6.2 aioice 0.6.18 alabaster 0.7.8 apturl 0.5.2 argh 0.26.2 asciinema 2.0.0 asn1crypto 0.24.0 astor 0.8.0 astroid 2.2.5 async-timeout 3.0.1 attrs 19.3.0 Automat 20.2.0 autopy 3.1.0 Babel 2.4.0 beautifulsoup4 4.6.0 binwalk 2.1.1 bleach 2.1.2 bleak 0.9.1 bottle 0.12.18 breathe 4.20.0 Brlapi 0.6.6 bs4 0.0.1 certifi 2019.9.11 cffi 1.14.0 chardet 3.0.4 cldoc 1.11.2 Click 7.0 cloudpickle 1.3.0 colorama 0.4.3 colour 0.1.5 command-not-found 0.3 commonmark 0.9.1 constantly 15.1.0 cpp-coveralls 0.4.2 cryptography 3.4.6 cssselect 1.1.0 cupshelpers 1.0 cycler 0.10.0 dataclasses 0.7 decorator 4.4.2 defer 1.0.6 devscripts 2.17.12ubuntu1.1 discord.py 1.5.0 distro-info 0.18ubuntu0.18.04.1 Django 2.2.5 django-modelcluster 4.4 django-taggit 0.24.0 django-treebeard 4.3 djangorestframework 3.10.3 docutils 0.14 draftjs-exporter 2.1.6 entrypoints 0.2.3.post1 enum34 1.1.10 exhale 0.2.3 Flask 1.1.1 freetype-py 2.1.0.post1 future 0.18.2 gast 0.2.2 gevent 1.4.0 gpg 1.10.0 greenlet 0.4.15 groundwork-sphinx-theme 1.1.1 grpcio 1.20.1 gym 0.17.2 h5py 2.9.0 hawkmoth 0.7.0 html5lib 1.0.1 httplib2 0.9.2 hyperlink 20.0.1 idna 2.8 idna-ssl 1.1.0 imagesize 0.7.1 importlib-metadata 3.7.3 incremental 17.5.0 inputs 0.5 ipykernel 4.8.2 ipython 5.5.0 ipython-genutils 0.2.0 isodate 0.6.0 isort 4.3.18 itsdangerous 1.1.0 jedi 0.11.1 Jinja2 2.10.1 joblib 0.14.0 jsonschema 2.6.0 jupyter-client 5.2.2 jupyter-core 4.4.0 Keras 2.2.4 Keras-Applications 1.0.7 Keras-Preprocessing 1.0.9 keyring 10.6.0 keyrings.alt 3.0 kiwisolver 1.2.0 language-selector 0.1 launchpadlib 1.10.6 lazr.restfulclient 0.13.5 lazr.uri 1.0.3 lazy-object-proxy 1.3.1 libsass 0.19.4 logilab-common 1.4.1 louis 3.5.0 lxml 4.4.1 macaroonbakery 1.1.3 Mako 1.0.7 manim 0.4.0 ManimPango 0.2.3 Markdown 3.1.1 MarkupSafe 1.1.1 matplotlib 2.1.1 mccabe 0.6.1 meld 3.18.0 MIDIUtil 1.2.1 mido 1.2.9 mistune 0.8.3 mock 3.0.5 MouseInfo 0.1.2 mpi4py 3.0.3 multidict 4.7.2 music21 5.7.2 musthe 1.0.0 nbconvert 5.3.1 nbformat 4.4.0 netifaces 0.10.4 networkx 2.5 nmap 0.0.1 numpy 1.16.3 numpydoc 0.7.0 oauth 1.0.1 olefile 0.45.1 opencv-python 4.1.1.26 openshot-qt 2.4.1 packaging 19.2 pandocfilters 1.4.2 parsimonious 0.8.1 parso 0.1.1 pathtools 0.1.2 pexpect 4.2.1 pick 0.6.4 pickleshare 0.7.4 Pillow 6.1.0 pip 21.0.1 Pivy 0.6.5a0 plaidml 0.7.0 ply 3.11 pony 0.7.13 prompt-toolkit 1.0.15 protobuf 3.7.1 psutil 5.4.2 PyAutoGUI 0.9.48 pybase62 0.4.3 pybullet 2.8.3 pycairo 1.19.1 pycodestyle 2.3.1 pycparser 2.20 pycrypto 2.6.1 pycups 1.9.73 pydub 0.25.1 pyflakes 1.6.0 pygame 1.9.6 PyGetWindow 0.0.8 pyglet 1.5.0 Pygments 2.8.1 pygobject 3.26.1 PyHamcrest 2.0.2 pylint 2.3.1 pymacaroons 0.13.0 PyMsgBox 1.0.7 PyNaCl 1.1.2 pynmea2 1.15.0 PyOpenGL 3.1.0 pyOpenSSL 20.0.1 pyparsing 2.2.0 pyperclip 1.7.0 pyqtgraph 0.10.0 pyquery 1.4.1 PyRect 0.1.4 pyRFC3339 1.0 PyScreeze 0.1.26 pyserial 3.4 pyserial-asyncio 0.4 python-apt 1.6.5+ubuntu0.5 python-dateutil 2.8.1 python-debian 0.1.32 python-dotenv 0.7.1 python-magic 0.4.16 python3-xlib 0.15 PyTweening 1.0.3 pytz 2019.2 pyxdg 0.25 PyYAML 3.12 pyzmq 16.0.2 QtAwesome 0.4.4 qtconsole 4.3.1 QtPy 1.3.1 reportlab 3.4.0 requests 2.22.0 requests-unixsocket 0.1.5 requirements-parser 0.2.0 rich 6.2.0 roman 2.0.0 rope 0.10.5 russian-names 0.1.2 scapy 2.4.2 scikit-learn 0.21.3 scipy 0.19.1 scour 0.36 screen-resolution-extra 0.0.0 SecretStorage 2.3.1 selenium 3.141.0 setuptools 41.0.1 simplegeneric 0.8.1 simplejson 3.13.2 six 1.12.0 sklearn 0.0 slacker 0.13.0 snowballstemmer 2.0.0 SoundCard 0.3.3 Sphinx 3.2.1 sphinx-rtd-theme 0.4.3 sphinxcontrib-applehelp 1.0.1 sphinxcontrib-devhelp 1.0.1 sphinxcontrib-htmlhelp 1.0.2 sphinxcontrib-inlinesyntaxhighlight 0.2 sphinxcontrib-jsmath 1.0.1 sphinxcontrib-qthelp 1.0.2 sphinxcontrib-serializinghtml 1.1.3 spyder 3.2.6 sqlparse 0.3.0 ssh-import-id 5.7 system-service 0.3 systemd-python 234 tensorboard 1.13.1 tensorflow 1.13.1 tensorflow-estimator 1.13.0 termcolor 1.1.0 terminaltables 3.1.0 testpath 0.3.1 tornado 4.5.3 tqdm 4.59.0 traitlets 4.3.2 Twisted 20.3.0 txdbus 1.1.2 typed-ast 1.3.5 typing-extensions 3.7.4.1 ubuntu-drivers-common 0.0.0 ufw 0.36 unattended-upgrades 0.1 Unidecode 1.1.1 unidiff 0.5.4 urllib3 1.25.3 usb-creator 0.3.3 uWSGI 2.0.18 vboxapi 1.0 virtualenv 16.6.0 vispy 0.6.3 wadllib 1.3.2 wagtail 2.6.1 watchdog 0.9.0 wcwidth 0.1.7 webencodings 0.5.1 websocket-client 0.57.0 Werkzeug 0.15.6 wheel 0.33.4 Willow 1.1 wrapt 1.11.1 wxPython 4.0.1 xkit 0.0.0 yarl 1.4.2 youtube-dl 2020.12.12 zipp 3.4.1 zope.interface 5.1.2 ```
LaTeX details + LaTeX distribution (e.g. TeX Live 2020): TeX Live 2017.20180305-1 + Installed LaTeX packages: (running on Debian, switching to user mode!) Cannot determine type of tlpdb from /home/grid/texmf! cannot setup TLPDB in /home/grid/texmf at /usr/bin/tlmgr line 6424.
FFMPEG Output of `ffmpeg -version`: ``` ffmpeg version 3.4.8-0ubuntu0.2 Copyright (c) 2000-2020 the FFmpeg developers ```

UPD: discontinuities parameter is broken, more info in this message.

NicolasCaous commented 3 years ago

Hi from the other issue :)

Try adding t_min and t_max since it appears that the parametric function utilizes them to trim the discontinuities

def generate_points(self):
    t_min, t_max = self.t_min, self.t_max
    dt = self.dt

    discontinuities = filter(lambda t: t_min <= t <= t_max, self.discontinuities)
    discontinuities = np.array(list(discontinuities))
6r1d commented 3 years ago

Hey! :-)

Do you see the difference when you add a generate_points method in your copy of PhaseValuePlot? I don't see it yet. How have you replaced x_min with t_min in your code? I only changed the labels, really. If I add your method, it's not even running, so it doesn't throw an error about no "t_min" and "t_max" being there.

NicolasCaous commented 3 years ago

I meant to add both x_min, x_max, t_min, and t_max. Unfortunately I can't run manin at the moment because I'm at work :'(

NicolasCaous commented 3 years ago

t_min and t_max have a default value of 0 and 1 respectively. The generate_points function is a internal function of ParametricFunction

NicolasCaous commented 3 years ago

Like so

    def construct(self):
        self.setup_axes()
        func_graph = self.get_graph(lambda t: t % 1.0, x_min=-2, x_max=2, t_min=-2, t_max=2, discontinuities=[ REPLACE THIS ], color=self.function_color)
        phase_formula = MathTex(r"p = T \mod 1.0")
        phase_formula.next_to(func_graph, RIGHT + UP)

        self.play(ShowCreation(self.axes))
        self.play(Write(phase_formula))
        self.play(Write(func_graph))
        self.wait(3)
        self.play(FadeOut(func_graph))
        self.play(FadeOut(self.axes))
6r1d commented 3 years ago

To be clear, what initial set of discontinuities do you access in the "generate points" filter? I think it's best to try later, I rearranged code many times and it still fails the same way. Last attempt, where I feed discontinuities into func_graph manually:

    def construct(self):
        self.setup_axes()
        func_graph = self.get_graph(lambda t: t % 1.0, x_min=-2, x_max=2, discontinuities=[-3, -2, -1, 0, 1, 2, 3],color=self.function_color)
        phase_formula = MathTex(r"p = T \mod 1.0")
        phase_formula.next_to(func_graph, RIGHT + UP)

        t_min, t_max = 0, 1
        x_min, x_max = self.x_min, self.x_max
        discontinuities = filter(lambda t: t_min <= t <= t_max, func_graph.discontinuities)
        discontinuities = np.array(list(discontinuities))
        func_graph.discontinuities = discontinuities

And, in any case, it's not about this particular drawing, it's more about how Manim is displaying things. I.e. while this GLSL example will give ugly output in Shadertoy, it'll be a more correct one:

float plot (vec2 uv, float pct) {
  return smoothstep(pct-0.0001, pct, uv.y) - smoothstep(pct, pct+0.01, uv.y);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = fragCoord / iResolution.xy;
    vec3 col = vec3(.0);
    col += plot(uv, mod(uv.x * 10.0, 1.0));
    fragColor.rgb = col;
}

So, maybe some option like "don't smooth the function output" or something like that will help. Or some "smoothing function" user can replace?

NicolasCaous commented 3 years ago

After fiddling around, I have noticed that the discontinuities parameter of ParametricFunction is broken and does not work.

However, you can still achieve your desired effect like so:

image

Code ```python from manim import * import numpy as np class PhaseValuePlot(GraphScene): def __init__(self, **kwargs): GraphScene.__init__( self, x_min=-3, x_max=3, num_graph_anchor_points=100, y_min=-2.0, y_max=2.0, graph_origin=ORIGIN, axes_color=WHITE, x_labeled_nums=range(-3, 3, 1), y_axis_label="Phase [p]", x_axis_label="Time [T]", **kwargs ) self.function_color = BLUE def construct(self): self.setup_axes() dx = 0.00001 func_graph1 = self.get_graph(lambda t: t % 1.0, x_min=-2, x_max=-1 - dx, color=self.function_color) func_graph2 = self.get_graph(lambda t: t % 1.0, x_min=-1, x_max=0 - dx, color=self.function_color) func_graph3 = self.get_graph(lambda t: t % 1.0, x_min=0, x_max=1 - dx, color=self.function_color) func_graph4 = self.get_graph(lambda t: t % 1.0, x_min=1, x_max=2 - dx, color=self.function_color) phase_formula = MathTex(r"p = T \mod 1.0") phase_formula.next_to(func_graph4, RIGHT + UP) self.play(ShowCreation(self.axes)) self.play(Write(phase_formula)) self.play(*[Write(func) for func in [func_graph1, func_graph2, func_graph3, func_graph4]]) self.wait(3) self.play(*[FadeOut(func) for func in [func_graph1, func_graph2, func_graph3, func_graph4]]) self.play(FadeOut(self.axes)) ```

If you want your strokes to be plotted sequentially, use this code:

image

Code ```python from manim import * import numpy as np class PhaseValuePlot(GraphScene): def __init__(self, **kwargs): GraphScene.__init__( self, x_min=-3, x_max=3, num_graph_anchor_points=100, y_min=-2.0, y_max=2.0, graph_origin=ORIGIN, axes_color=WHITE, x_labeled_nums=range(-3, 3, 1), y_axis_label="Phase [p]", x_axis_label="Time [T]", **kwargs ) self.function_color = BLUE def construct(self): self.setup_axes() dx = 0.00001 func_graph1 = self.get_graph(lambda t: t % 1.0, x_min=-2, x_max=-1 - dx, color=self.function_color) func_graph2 = self.get_graph(lambda t: t % 1.0, x_min=-1, x_max=0 - dx, color=self.function_color) func_graph3 = self.get_graph(lambda t: t % 1.0, x_min=0, x_max=1 - dx, color=self.function_color) func_graph4 = self.get_graph(lambda t: t % 1.0, x_min=1, x_max=2 - dx, color=self.function_color) phase_formula = MathTex(r"p = T \mod 1.0") phase_formula.next_to(func_graph4, RIGHT + UP) self.play(ShowCreation(self.axes)) self.play(Write(phase_formula)) self.play(Write(func_graph1, run_time=1/4)) self.play(Write(func_graph2, run_time=1/4)) self.play(Write(func_graph3, run_time=1/4)) self.play(Write(func_graph4, run_time=1/4)) self.wait(3) self.play(FadeOut(func_graph1, run_time=1/4)) self.play(FadeOut(func_graph2, run_time=1/4)) self.play(FadeOut(func_graph3, run_time=1/4)) self.play(FadeOut(func_graph4, run_time=1/4)) self.play(FadeOut(self.axes)) ```
NicolasCaous commented 3 years ago

Also, why is my shade of black different from yours?

NicolasCaous commented 3 years ago

Update: I was able to get it to work with the discontinuities parameter. However, this parameter is really broken and it wouldn't work if the space between the discontinuities wasn't constant. The API needs to be reworked.

image

Code ```python from manim import * import numpy as np class PhaseValuePlot(GraphScene): def __init__(self, **kwargs): GraphScene.__init__( self, x_min=-3, x_max=3, num_graph_anchor_points=100, y_min=-2.0, y_max=2.0, graph_origin=ORIGIN, axes_color=WHITE, x_labeled_nums=range(-3, 3, 1), y_axis_label="Phase [p]", x_axis_label="Time [T]", **kwargs ) self.function_color = BLUE def construct(self): self.setup_axes() func_graph = self.get_graph(lambda t: t % 1.0, x_min=0, x_max=1, t_min=-2, t_max=2, discontinuities=[-1,0,1,2], color=self.function_color) phase_formula = MathTex(r"p = T \mod 1.0") phase_formula.next_to(func_graph, RIGHT + UP) self.play(ShowCreation(self.axes)) self.play(Write(phase_formula)) self.play(Write(func_graph)) self.wait(3) self.play(FadeOut(func_graph)) self.play(FadeOut(self.axes)) ```
6r1d commented 3 years ago

Wow, first, thanks a lot for all the code you wrote! It's great to know the API issue is even more localized now, yeah, discontinuities parameter needs some work. I won't close this issue and will alter the first message so it's more evident if that makes sense.

Also, why is my shade of black different from yours?

I don't know, but if you'll have any Manim tests in mind, just write a reply to my email!

Now that I think about it, I can add videos here, not sure about the gamma, but there's a rendered one to compare.

huguesdevimeux commented 3 years ago

Hi there,

I believe this is the same issue as #1331.

A straightforward fix for this is changing get_graph call to this :

        func_graph = self.get_graph(
            lambda t: t % 1.0,
            x_min=-2 + 0.005,
            x_max=2,
            discontinuities=[inverse_interpolate(-2 + 0.005 ,2,k) for k in range(-2, 3)],
            color=self.function_color,
        )

It renders this. image

a bit of explanation of why does it work is in the linked issue, and the +0.005 is to avoid x = -2 to be handled by the graph because manim can't for some reason place a discontinuity on the precise point x = -2 (i think it's a boundary open/closed issue).

If the issue is resolved, I will close it.

6r1d commented 3 years ago

It renders this.

What's interesting is that blue lines don't have antialiasing on your picture. Is that something easy to fix?

If the issue is resolved, I will close it.

If yes and you'll make a PR fixing the whole thing, sure, I'll close it.

huguesdevimeux commented 3 years ago

What's interesting is that blue lines don't have antialiasing on your picture.

I rendered with -s in low quality.

Is that something easy to fix?

I opened #1345 for this :D

6r1d commented 3 years ago

I opened #1345 for this :D

Understood, thank you for fixing this issue!

behackl commented 3 years ago

The issue should remain open until the suggested solution is actually merged.

taesungh commented 12 months ago

Since GraphScene was removed in #1860, can this issue be closed?

uwezi commented 12 months ago

looking at the original post - this was never really an bug. Those artifacts are normal and come from the Bezier-rendering. In order to avoid these one just needs to increase of calculated points and define the discontinuities...

behackl commented 10 months ago

I think that as long as discontinuities are specified correctly, the rendered result should be okay. Closing.