ManimCommunity / ManimCommunity/manim
`Mobject.set` does not set `VMobject` attributes
- Dominant language
- Python
- Stars
- 40.9k
- Forks
- 3.1k
- Avg merge
- 3d 12h
- Merged PRs (30d)
- 25
Description
## Description of bug / unexpected behavior
`VMobject` attributes are unable to be set with `Mobject.set`.
In the docs for [Mobject.set](https://docs.manim.community/en/latest/reference/manim.mobject.mobject.Mobject.html#manim.mobject.mobject.Mobject.set), the warning recommends one use the `set` method instead of the explicit setters, but this has no effect on `VMobject`s.
## Expected behavior
For example, the default color of a `Circle()` is `#fc6255`, but this will *not* change the color of the circle to white:
```py
a0 = Circle()
a0.set(color=WHITE)
```
## How to reproduce the issue
See above.
But, I also whipped up a quick hack of a test for each of the parameters for `VMobject`:
Code for reproducing the problem
```py
from manim import *
from operator import methodcaller
# "key": value # default_value
attrs = {
"color": WHITE, # "#fc6255"
"fill_color": WHITE, # None
"fill_opacity": 0.5, # 0.0
"stroke_color": WHITE, # None
"stroke_opacity": 0.5, # 1.0
"stroke_width": 10, # 4
"background_stroke_color": WHITE, # BLACK
"background_stroke_opacity": 0.5, # 1.0
"background_stroke_width": 10, # 0
"sheen_factor": 10, # 0.0
# "sheen_direction": RIGHT, # UL # array comparison needed (can't use ==)
"close_new_points": True, # False
"pre_function_handle_to_anchor_scale_factor": 0.05, # 0.01
"make_smooth_after_applying_functions": True, # False
"background_image": "test", # None
"shade_in_3d": True, # False
"tolerance_for_point_equality": 1e-04, # 1e-06
"n_points_per_cubic_curve": 3, # 4
}
# indicate the boolean
def bid(b: bool):
return "Pass" if b else "Fail"
print(f"\na0 = Circle()\na1 = Circle()\n")
for key, val in attrs.items():
a0 = Circle()
a1 = Circle()
a0.set(key=val)
methodcaller("set_" + key, val)(a1) # e.g. a1.set_color(WHITE)
get_a0 = getattr(a0, key)
m_get_a0 = methodcaller("get_" + key)(a0)
print(f"a0.set({key}={val})")
print(f"getattr(a0, {key}) # => {get_a0} # {bid(get_a0 == val)}")
print(f"a0.get_{key}() # => {m_get_a0} # {bid(m_get_a0 == val)}\n")
get_a1 = getattr(a1, key)
m_get_a1 = methodcaller("get_" + key)(a1)
print(f"a1.set_{key}({val})")
print(f"getattr(a1, {key}) # => {get_a1} # {bid(get_a1 == val)}")
print(f"a1.get_{key}() # => {m_get_a1} # {bid(m_get_a1 == val)}\n\n\n")
```
The output on my machine (I corrected a few false negatives):
```py
a0 = Circle()
a1 = Circle()
a0.set(color=#FFFFFF)
getattr(a0, color) # => #fc6255 # Fail
a0.get_color() # => #fc6255 # Fail
a1.set_color(#FFFFFF)
getattr(a1, color) # => white # Fail (OK)
a1.get_color() # => white # Fail (OK)
a0.set(fill_color=#FFFFFF)
getattr(a0, fill_color) # => #fc6255 # Fail
a0.get_fill_color() # => #fc6255 # Fail
a1.set_fill_color(#FFFFFF)
getattr(a1, fill_color) # => #FFFFFF # Pass
a1.get_fill_color() # => #fc6255 # Fail
a0.set(fill_opacity=0.5)
getattr(a0, fill_opacity) # => 0.0 # Fail
a0.get_fill_opacity() # => 0.0 # Fail
a1.set_fill_opacity(0.5)
getattr(a1, fill_opacity) # => 0.5 # Pass
a1.get_fill_opacity() # => 0.0 # Fail
a0.set(stroke_color=#FFFFFF)
getattr(a0, stroke_color) # => #fc6255 # Fail
a0.get_stroke_color() # => #fc6255 # Fail
a1.set_stroke_color(#FFFFFF)
getattr(a1, stroke_color) # => #FFFFFF # Pass
a1.get_stroke_color() # => #fc6255 # Fail
a0.set(stroke_opacity=0.5)
getattr(a0, stroke_opacity) # => 1.0 # Fail
a0.get_stroke_opacity() # => 1.0 # Fail
a1.set_stroke_opacity(0.5)
getattr(a1, stroke_opacity) # => 0.5 # Pass
a1.get_stroke_opacity() # => 1.0 # Fail
a0.set(stroke_width=10)
getattr(a0, stroke_width) # => 4 # Fail
a0.get_stroke_width() # => 4 # Fail
a1.set_stroke_width(10)
getattr(a1, stroke_width) # => 10 # Pass
a1.get_stroke_width() # => 10 # Pass
a0.set(background_stroke_color=#FFFFFF)
getattr(a0, background_stroke_color) # => #000000 # Fail
a0.get_background_stroke_color() # => #000000 # Fail
a1.set_background_stroke_color(#FFFFFF)
getattr(a1, background_stroke_color) # => #FFFFFF # Pass
a1.get_background_stroke_color() # => #FFFFFF # Pass
a0.set(background_stroke_opacity=0.5)
getattr(a0, background_stroke_opacity) # => 1.0 # Fail
a0.get_background_stroke_opacity() # => 1.0 # Fail
a1.set_background_stroke_opacity(0.5)
getattr(a1, background_stroke_opacity) # => 0.5 # Pass
a1.get_background_stroke_opacity() # => 0.5 # Pass
a0.set(background_stroke_width=10)
getattr(a0, background_stroke_width) # => 0 # Fail
a0.get_background_stroke_width() # => 0 # Fail
a1.set_background_stroke_width(10)
getattr(a1, background_stroke_width) # => 10 # Pass
a1.get_background_stroke_width() # => 10 # Pass
a0.set(sheen_factor=10)
getattr(a0, sheen_factor) # => 0.0 # Fail
a0.get_sheen_factor() # => 0.0 # Fail
a1.set_sheen_factor(10)
getattr(a1, sheen_factor) # => 10 # Pass
a1.get_sheen_factor() # => 10 # Pass
a0.set(close_new_points=True)
getattr(a0, close_new_points) # => False # Fail
a0.get_close_new_points() # => False # Fail
a1.set_close_new_points(True)
getattr(a1, close_new_points) # => True # Pass
a1.get_close_new_points() # => True # Pass
a0.set(pre_function_handle_to_anchor_scale_factor=0.05)
getattr(a0, pre_function_handle_to_anchor_scale_factor) # => 0.01 # Fail
a0.get_pre_function_handle_to_anchor_scale_factor() # => 0.01 # Fail
a1.set_pre_function_handle_to_anchor_scale_factor(0.05)
getattr(a1, pre_function_handle_to_anchor_scale_factor) # => 0.05 # Pass
a1.get_pre_function_handle_to_anchor_scale_factor() # => 0.05 # Pass
a0.set(make_smooth_after_applying_functions=True)
getattr(a0, make_smooth_after_applying_functions) # => False # Fail
a0.get_make_smooth_after_applying_functions() # => False # Fail
a1.set_make_smooth_after_applying_functions(True)
getattr(a1, make_smooth_after_applying_functions) # => True # Pass
a1.get_make_smooth_after_applying_functions() # => True # Pass
a0.set(background_image=test)
getattr(a0, background_image) # => None # Fail
a0.get_background_image() # => None # Fail
a1.set_background_image(test)
getattr(a1, background_image) # => test # Pass
a1.get_background_image() # => test # Pass
a0.set(shade_in_3d=True)
getattr(a0, shade_in_3d) # => False # Fail
a0.get_shade_in_3d() # => False # Fail
a1.set_shade_in_3d(True)
getattr(a1, shade_in_3d) # => True # Pass
a1.get_shade_in_3d() # => True # Pass
a0.set(tolerance_for_point_equality=0.0001)
getattr(a0, tolerance_for_point_equality) # => 1e-06 # Fail
a0.get_tolerance_for_point_equality() # => 1e-06 # Fail
a1.set_tolerance_for_point_equality(0.0001)
getattr(a1, tolerance_for_point_equality) # => 0.0001 # Pass
a1.get_tolerance_for_point_equality() # => 0.0001 # Pass
a0.set(n_points_per_cubic_curve=3)
getattr(a0, n_points_per_cubic_curve) # => 4 # Fail
a0.get_n_points_per_cubic_curve() # => 4 # Fail
a1.set_n_points_per_cubic_curve(3)
getattr(a1, n_points_per_cubic_curve) # => 3 # Pass
a1.get_n_points_per_cubic_curve() # => 3 # Pass
```
## System specifications
System Details
- OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)):
- RAM:
- Python version (`python/py/python3 --version`):
- Installed modules (provide output from `pip list`):
```
macOS 10.15.7
python 3.9.9
Manim Community v0.12.0
```
## Additional comments
@behackl may already have a fix (from discord):
> Ah, I've found the issue: for `VMobject`, `set_color` explicitly calls `set_stroke` and `set_fill`. This does not happen when just the attribute is set; it might make sense to turn the attribute into a property.
I figured I would still open an issue just to document a fix, and to also make sure that the other values to set are accounted for.
Contributor guide
Research direction
Start with Mobject.set and the VMobject setters referenced in the issue, then run the supplied Circle reproduction comparing set with explicit setters. Check each listed VMobject attribute, including color and fill/stroke settings. Done means Mobject.set has the same effect as the corresponding explicit setter for these attributes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100