ManimCommunity / ManimCommunity/manim

Use overrides of children in family operations

Open
#5,018 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
40.9k
Forks
3.1k
Avg merge
3d 12h
Merged PRs (30d)
25

Description

Currently, operations that work on family members don't take overidden methods of children into account. So the result can be quite different when children implement custom logic.

This is quite prominent with the `Arrow.scale` method:
```py
def construct(self) -> None:
mob1 = VGroup(Arrow().scale(3))
mob2 = VGroup(Arrow()).scale(3)
self.add(VGroup(mob1, mob2).arrange(DOWN))
```

Image

Ideally, when calling an operation on a parent, it should still use the overridden methods of children.

Aditional things to consider:
* the submobject tree can contain loops and/or duplicates
* parents should have control over how the operation is applied to children
* overrides should be able to have custom logic before and/or after the operation
* parents should be able to have custom logic before and/or after the operation is applied to their children
* users should be able to pass (custom) parameters to all members

Some ideas and their ad-/disadvantages were already discussed in #4994. See [here](https://github.com/ManimCommunity/manim/pull/4994#issuecomment-5655813621)

## Ideas

### Dump recursion
> Assumes no duplicates and/or loops.
```py
def some_operation(...):
... # apply to self
for sub in self.submobjects:
sub.some_operation(...)
```

### Internal method for self
```py
def some_operation(...):
for mob in self.get_family():
mob._some_operation(...)

def _some_operation(...):
... # apply to self
```

#### `apply_to_family`
```py
def some_operation(..., apply_to_family=True):
... # logic for self
if apply to family:
for mob in self.get_family():
mob.some_operation(..., apply_to_family=False)
```

### `already_seen`
```py
def some_operation(..., already_seen= None):
... # apply to self
if already_seen is None:
already_seen = {self}
for sub in self.submobjects:
if sub not in already_seen:
already_seen.add(sub)
sub.some_operation(..., already_seen=already_seen)
```

### `apply_to_family` internally
```py
def some_operation(...):
return self._some_operation(...)

def _some_operation(..., apply_to_family=True):
... # apply to self
if apply to family:
for mob in self.get_family():
mob.some_operation(..., apply_to_family=False)
```

### `already_seen` internally
```py
def some_operation(...):
return self._some_operation(...)

def _some_operation(..., already_seen= None):
... # apply to self
if already_seen is None:
already_seen = {self}
for sub in self.submobjects:
if sub not in already_seen:
already_seen.add(sub)
sub.some_operation(..., already_seen=already_seen)
```

Contributor guide

Open the contributing guide

Research direction

Start with the family operations involved in the Arrow.scale example and review the alternatives and discussion in issue #4994. Define a design that dispatches parent operations through child overrides while handling loops, duplicates, parent control, custom logic, and shared parameters; done requires agreement on the approach and coverage of these cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
computer-graphics
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.