Source code for simetri.helpers.modifiers
from random import random, choice
import inspect
from ..graphics.all_enums import Control, State
[docs]
class Modifier:
"""Used to modify the properties of a Batch object.
Attributes:
function (callable): The function to modify the property.
life_span (int): The number of times the modifier can be applied.
randomness (float or callable): Determines the randomness of the modification.
condition (bool or callable): Condition to apply the modification.
state (State): The current state of the modifier.
_d_state (dict): Mapping of control states to modifier states.
count (int): Counter for the number of times the modifier has been applied.
args (tuple): Additional arguments for the function.
kwargs (dict): Additional keyword arguments for the function.
"""
def __init__(
self, function, life_span=10000, randomness=1.0, condition=True, *args, **kwargs
):
"""
Args:
function (callable): The function to modify the property.
life_span (int, optional): The number of times the modifier can be applied. Defaults to 10000.
randomness (float or callable, optional): Determines the randomness of the modification. Defaults to 1.0.
condition (bool or callable, optional): Condition to apply the modification. Defaults to True.
*args: Additional arguments for the function.
**kwargs: Additional keyword arguments for the function.
"""
self.function = function # it can be a list of functions
signature = inspect.signature(function)
self.n_func_args = len(signature.parameters)
self.life_span = life_span
self.randomness = randomness
self.condition = condition
self.state = State.INITIAL
self._d_state = {
Control.INITIAL: State.INITIAL,
Control.STOP: State.STOPPED,
Control.PAUSE: State.PAUSED,
Control.RESUME: State.RUNNING,
Control.RESTART: State.RESTARTING,
}
self.count = 0
self.args = args
self.kwargs = kwargs
def __repr__(self):
"""Returns a string representation of the Modifier object.
Returns:
str: String representation of the Modifier object.
"""
return (
f"Modifier(function:{self.function}, lifespan:{self.life_span},"
f"randomness:{self.randomness})"
)
def __str__(self):
"""Returns a string representation of the Modifier object.
Returns:
str: String representation of the Modifier object.
"""
return self.__repr__()
[docs]
def set_state(self, control):
"""Sets the state of the modifier based on the control value.
Args:
control (Control): The control value to set the state.
"""
self.state = self._d_state[control]
[docs]
def get_value(self, obj, target, *args, **kwargs):
"""Gets the value from an object or callable.
Args:
obj (object or callable): The object or callable to get the value from.
target (object): The target object.
*args: Additional arguments for the callable.
**kwargs: Additional keyword arguments for the callable.
Returns:
object: The value obtained from the object or callable.
"""
if callable(obj):
res = obj(target, *args, **kwargs)
if res in Control:
self.set_state(res)
else:
res = obj
return res
[docs]
def apply(self, element):
"""Applies the modifier to an element.
If a function returns a control value, it will be applied to the modifier.
Control.STOP, Control.PAUSE, Control.RESUME, and Control.RESTART are the only control values.
Functions should have the following signature:
def funct(target, modifier, *args, **kwargs):
Args:
element (object): The element to apply the modifier to.
"""
if self.can_continue(element):
if self.n_func_args == 1:
self.function(element)
else:
self.function(element, self, *self.args, **self.kwargs)
self._update_state()
else:
self.state = State.STOPPED
[docs]
def can_continue(self, target):
"""Checks if the modifier can continue to be applied.
Args:
target (object): The target object.
Returns:
bool: True if the modifier can continue, False otherwise.
"""
if callable(self.randomness):
randomness = self.get_value(self.randomness, target)
elif type(self.randomness) == float:
randomness = self.randomness >= random()
elif type(self.randomness) in [list, tuple]:
randomness = choice(self.randomness)
if callable(self.condition):
condition = self.get_value(self.condition, target)
else:
condition = self.condition
if callable(self.life_span):
life_span = self.get_value(self.life_span, target)
else:
life_span = self.life_span
if life_span > 0 and condition and randomness:
if self.state in [State.INITIAL, State.RUNNING, State.RESTARTING]:
res = True
else:
res = False
else:
res = False
return res
def _update_state(self):
"""Updates the state of the modifier based on its life span and count."""
self.count += 1
if self.count == 1:
self.state = State.RUNNING
if self.life_span > 0:
if self.state == State.RESTARTING:
self.state = State.RUNNING
elif self.state == State.RUNNING:
self.life_span -= 1
if self.life_span == 0:
self.state = State.STOPPED
else:
self.state = State.STOPPED
[docs]
def stop(self):
"""Stops the modifier."""
self.state = State.STOPPED