ManimCommunity / ManimCommunity/manim

TracedPath stroke_opacity acting weird, with working version for comparison

Open
#3,854 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
40.9k
Forks
3.1k
Avg merge
3d 12h
Merged PRs (30d)
25

Description

## Description of bug / unexpected behavior
TracedPath stroke_opacity gradient has weird bug where the opacity gradient direction suddenly flips mid animation

## Expected behavior
Fading trail behind a moving Dot3D in a ThreeDScene, this trail should have a opacity gradient with a color gradient and disappear over time.

## How to reproduce the issue
```py
from manim import *
from manim import config
import pandas as pd
import numpy as np
from tqdm import tqdm

SCALE_FACTOR = 4 # Manifold scaling factor
ORBITS = 1.2 # Number of revolutions, can be float
TRAIL_LENGTH = config.frame_rate * 1.2 # Length of trail behind mercury
DESIRED_TOTAL_TIME = 12 # Desired length of animation, may not be this length

def prepare_data(file_name):
"""
Prepare Data Method

Scales data to fit on screen

:param file_name: The name of the CSV file containing the data.
:return: The processed dataframe.

"""
orbit_points = int(90023 / (5 / 0.2408467) * ORBITS) # 90023 points = 5 yrs -> 0.2408467 yrs = 1 rev, * # of revs

df = pd.read_csv(file_name, nrows=orbit_points, usecols=[1, 2, 3, 8], dtype=np.float64)
df.reset_index(drop=True, inplace=True)
max_value = df['RG'].values.max()

for col in ['X', 'Y', 'Z']:
df[col] = df[col] / max_value * SCALE_FACTOR
return df

class Orbit(ThreeDScene):
"""Class for visualizing the orbit of Mercury.

This class inherits from the ThreeDScene class
"""
def construct(self):
axes = ThreeDAxes(num_axis_pieces=20,
color=GREY,
axis_config={
'stroke_opacity': 0.8,
'stroke_width': 0.5,
'include_tip': False
})

self.camera.background_color = BLACK # Dark grey

df = prepare_data('horizons_half_hour.csv') # This processes Mercury's orbit data

# list of points, with each point of the form [x, y, z]
points = df[['X', 'Y', 'Z']].values

# Mercury representation
mercury = Dot3D(radius=SCALE_FACTOR * 0.01, color=GREY)
mercury.move_to(points[0])
index_tracker = ValueTracker(0) # Tracker for locus of mercury
mercury.add_updater(lambda m: m.move_to(
points[min(int(index_tracker.get_value()), len(points) - 1)]
))

# Sun representation (radius close to 4.375% of orbit)
sun = Dot3D(radius=.6 * SCALE_FACTOR * 0.04375, color=ORANGE)

traced_path = TracedPath(mercury.get_center,
stroke_color=[PURE_BLUE, PINK],
stroke_opacity=[0, 1],
dissipating_time=1,
stroke_width=SCALE_FACTOR / 1.5)

fading_path = VMobject(stroke_color=[DARK_BLUE, LIGHT_PINK], stroke_width=4)
fading_path.start_new_path(mercury.get_center())

def update_path(path_group):
new_line = Line(path_group[-1].get_end(), mercury.get_center())
start_color = PURE_BLUE
end_color = PINK

# calculate linear gradient function for opacity
max_opacity = 1
min_opacity = 0
opacity_step = (max_opacity - min_opacity) / len(path_group)

new_line.set_opacity(max_opacity)
new_line.set_color(end_color)
path_group.add(new_line)

if len(path_group) > TRAIL_LENGTH:
for line in path_group:
# If the opacity of a line is lower than threshold, remove it from path_group
if line.get_stroke_opacity() < 0.05:
path_group.remove(line)

for i, line in enumerate(path_group):
# update opacity for each line in the path group
new_opacity = min_opacity + i * opacity_step
line.set_stroke(opacity=new_opacity)

# update color of each line in the path group
ratio = i / len(path_group)
new_color = interpolate_color(start_color, end_color, ratio)
line.set_color(new_color)

fading_path.add_updater(update_path)

self.add(axes, mercury, sun, traced_path)
self.set_camera_orientation(phi=65 * DEGREES, theta=-120 * DEGREES)

# initialize trackers for camera orientation
phi: ValueTracker = self.camera.phi_tracker
theta: ValueTracker = self.camera.theta_tracker

step = 100
total_steps = (len(points) - 2) // step
run_time = DESIRED_TOTAL_TIME / total_steps

for _ in tqdm(range(0, int(total_steps))):
# animate orientation and increment
self.play(phi.animate.increment_value(-0.004),
theta.animate.increment_value(0.006),
ApplyMethod(index_tracker.increment_value, step),
rate_func=linear,
run_time=run_time
)

mercury.clear_updaters()
self.remove(phi, theta)
self.wait()
```
## Necessary Information
The real 'horizons_half_hour.csv' is 36 million rows long, so I have provided a shortened version
[shortened_data.csv](https://github.com/user-attachments/files/16200351/shortened_data.csv)

By running the code, as is right now, you will notice the traced path has weird behavior, the opacity gradient suddenly flips at random moments in the orbit.

By modifying the code to replace ```traced_path``` with ```fading_path``` in the ```self.add()``` line, you will see what the desired behavior is

By debugging you will find that it is not due to the color gradient, or dissipation time, but the stroke_opacity gradient passed by the list, as when this is removed everything behaves normally.

This functionality is not specifically mentioned in the documentation, however it is used in the example on the [TracedPath documentation page](https://docs.manim.community/en/stable/reference/manim.animation.changing.TracedPath.html#tracedpath) so I am unsure of many details regarding this feature.

## Screenshots:
BUG:
Screenshot 2024-07-12 at 5 46 05 PM

WORKING:
Screenshot 2024-07-12 at 5 46 20 PM

## System specifications

System Details

- OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)): macOS Sonoma 14.3
- RAM: 16GB
- Python version (`python/py/python3 --version`): 3.9.6
- Installed modules (provide output from `pip list`):
```
(.venv) (base) usr@mac project % pip list
Package Version
---------------------------- ------------------
absl-py 2.1.0
anyio 4.4.0
appnope 0.1.4
argon2-cffi 23.1.0
argon2-cffi-bindings 21.2.0
arrow 1.3.0
astropy 6.0.1
astropy-iers-data 0.2024.7.8.0.31.19
asttokens 2.4.1
astunparse 1.6.3
async-lru 2.0.4
attrs 23.2.0
Babel 2.15.0
beautifulsoup4 4.12.3
bleach 6.1.0
certifi 2024.6.2
cffi 1.16.0
charset-normalizer 3.3.2
click 8.1.7
cloup 3.0.5
colour 0.1.5
comm 0.2.2
contourpy 1.2.1
cycler 0.12.1
Cython 3.0.10
debugpy 1.8.2
decorator 5.1.1
defusedxml 0.7.1
exceptiongroup 1.2.1
executing 2.0.1
fastjsonschema 2.20.0
flatbuffers 24.3.25
fonttools 4.53.0
fqdn 1.5.1
gast 0.6.0
glcontext 2.5.0
google-pasta 0.2.0
grpcio 1.64.1
h11 0.14.0
h5py 3.11.0
httpcore 1.0.5
httpx 0.27.0
idna 3.7
importlib_metadata 8.0.0
importlib_resources 6.4.0
ipykernel 6.29.5
ipython 8.18.1
ipywidgets 8.1.3
isoduration 20.11.0
isosurfaces 0.1.2
jedi 0.19.1
Jinja2 3.1.4
json5 0.9.25
jsonpointer 3.0.0
jsonschema 4.22.0
jsonschema-specifications 2023.12.1
jupyter 1.0.0
jupyter_client 8.6.2
jupyter-console 6.6.3
jupyter_core 5.7.2
jupyter-events 0.10.0
jupyter-lsp 2.2.5
jupyter_server 2.14.1
jupyter_server_terminals 0.5.3
jupyterlab 4.2.3
jupyterlab_pygments 0.3.0
jupyterlab_server 2.27.2
jupyterlab_widgets 3.0.11
keras 3.4.1
kiwisolver 1.4.5
libclang 18.1.1
manim 0.18.1
manimgl 1.6.1
ManimPango 0.5.0
mapbox-earcut 1.0.1
Markdown 3.6
markdown-it-py 3.0.0
MarkupSafe 2.1.5
matplotlib 3.9.0
matplotlib-inline 0.1.7
mdurl 0.1.2
mistune 3.0.2
ml-dtypes 0.3.2
moderngl 5.10.0
moderngl-window 2.4.6
mpmath 1.3.0
multipledispatch 1.0.0
namex 0.0.8
nbclient 0.10.0
nbconvert 7.16.4
nbformat 5.10.4
nest-asyncio 1.6.0
networkx 3.2.1
nodejs 0.1.1
notebook 7.2.1
notebook_shim 0.2.4
npm 0.1.1
numpy 1.26.4
opt-einsum 3.3.0
optional-django 0.1.0
optree 0.11.0
overrides 7.7.0
packaging 24.1
pandas 2.2.2
pandocfilters 1.5.1
parso 0.8.4
pexpect 4.9.0
pillow 10.4.0
pip 24.1.1
platformdirs 4.2.2
prometheus_client 0.20.0
prompt_toolkit 3.0.47
protobuf 4.25.3
psutil 6.0.0
ptyprocess 0.7.0
pure-eval 0.2.2
pycairo 1.26.1
pycparser 2.22
pydub 0.25.1
pyerfa 2.0.1.4
pyglet 2.0.15
Pygments 2.18.0
pyobjc-core 10.3.1
pyobjc-framework-Cocoa 10.3.1
PyOpenGL 3.1.7
pyparsing 3.1.2
pyrr 0.10.3
python-dateutil 2.9.0.post0
python-json-logger 2.0.7
pytz 2024.1
PyYAML 6.0.1
pyzmq 26.0.3
qtconsole 5.5.2
QtPy 2.4.1
referencing 0.35.1
requests 2.32.3
rfc3339-validator 0.1.4
rfc3986-validator 0.1.1
rich 13.7.1
rpds-py 0.18.1
scipy 1.13.1
screeninfo 0.8.1
seaborn 0.13.2
Send2Trash 1.8.3
setuptools 68.2.0
six 1.16.0
skia-pathops 0.8.0.post1
sniffio 1.3.1
soupsieve 2.5
srt 3.5.3
stack-data 0.6.3
svgelements 1.9.6
sympy 1.12.1
tensorboard 2.16.2
tensorboard-data-server 0.7.2
tensorflow 2.16.2
tensorflow-io-gcs-filesystem 0.37.1
termcolor 2.4.0
terminado 0.18.1
tinycss2 1.3.0
tomli 2.0.1
tornado 6.4.1
tqdm 4.66.4
traitlets 5.14.3
types-python-dateutil 2.9.0.20240316
typing_extensions 4.12.2
tzdata 2024.1
uri-template 1.3.0
urllib3 2.2.2
validators 0.29.0
vg 2.0.0
watchdog 4.0.1
wcwidth 0.2.13
webcolors 24.6.0
webencodings 0.5.1
websocket-client 1.8.0
Werkzeug 3.0.3
wheel 0.41.2
widgetsnbextension 4.0.11
wrapt 1.16.0
zipp 3.19.2

```

LaTeX details

+ LaTeX distribution (e.g. TeX Live 2020): TeX Live 2024
+ Installed LaTeX packages: A lot and I don't think this is important so I save the space, if needed let me know

## Additional comments

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with the provided shortened_data.csv and the TracedPath example, then inspect how TracedPath handles a list passed as stroke_opacity during animation and dissipation. Compare it with the working fading_path behavior. Done means the opacity gradient no longer reverses unexpectedly while the documented example still produces the intended fading trail.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
computer-graphics
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.