ManimCommunity / ManimCommunity/manim

`Mobject.set` does not set `VMobject` attributes

Open
#2,320 3 comments 0 reactions 0 assignees View on GitHub
issue:bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.