MF-Algebra 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
MF_Algebra/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ __version__ = '0.5.0'
2
+ __author__ = 'John Connell - The Mathematic Fanatic'
3
+ __description__ = 'Manim plugin which aims to make it much easier to meaningfully transform algebra expressions.'
4
+
5
+ from .expressions import *
6
+ from .actions import *
7
+ from .timelines import *
@@ -0,0 +1,5 @@
1
+ from .action_core import *
2
+ from .animations import *
3
+ from .action_variants import *
4
+ from .combinations import *
5
+ from .action_common import *
@@ -0,0 +1,249 @@
1
+ from .action_core import *
2
+ from .action_variants import *
3
+ from .combinations import *
4
+ from ..expressions.operations import *
5
+ from ..expressions.variables import *
6
+ from ..expressions.relations import *
7
+ from MF_Tools.dual_compatibility import PI, FadeIn
8
+
9
+
10
+ class swap_children_(Action):
11
+ def __init__(self, preaddress='', mode="arc", arc_size=0.75*PI, **kwargs):
12
+ self.mode = mode
13
+ self.arc_size = arc_size
14
+ super().__init__(preaddress=preaddress,**kwargs)
15
+
16
+ @preaddressfunc
17
+ def get_output_expression(self, input_expression=None):
18
+ assert len(input_expression.children) == 2, f"Cannot swap children of {input_expression}, must have two children."
19
+ return type(input_expression)(input_expression.children[1], input_expression.children[0])
20
+
21
+ @preaddressmap
22
+ def get_addressmap(self, input_expression=None):
23
+ if self.mode == "arc":
24
+ return [
25
+ ["0", "1", {"path_arc": self.arc_size}],
26
+ ["1", "0", {"path_arc": self.arc_size}]
27
+ ]
28
+ elif self.mode == "straight":
29
+ return [
30
+ ["0", "1"],
31
+ ["1", "0"]
32
+ ]
33
+ else:
34
+ raise ValueError(f"Invalid mode: {self.mode}. Must be 'arc' or 'straight'.")
35
+
36
+
37
+ class apply_operation_(Action):
38
+ def __init__(self, OpClass, other, preaddress='', side="right", introducer=Write, **kwargs):
39
+ self.OpClass = OpClass
40
+ self.other = Smarten(other)
41
+ self.side = side
42
+ self.introducer = introducer
43
+ super().__init__(preaddress=preaddress,**kwargs)
44
+
45
+ @preaddressfunc
46
+ def get_output_expression(self, input_expression):
47
+ if self.side == "right":
48
+ output_expression = self.OpClass(input_expression, self.other) # Putting a .copy() on the input expression fixes the problem. Try to understand this and then probably fold this into the decorator
49
+ elif self.side == "left":
50
+ output_expression = self.OpClass(self.other, input_expression)
51
+ else:
52
+ raise ValueError(f"Invalid side: {self.side}. Must be left or right.")
53
+ return output_expression
54
+
55
+ @preaddressmap
56
+ def get_addressmap(self, input_expression):
57
+ if self.side == "right":
58
+ return [
59
+ ["", "0"],
60
+ [self.introducer, "+", {"delay":0.5}],
61
+ [self.introducer, "1", {"delay":0.6}]
62
+ ]
63
+ elif self.side == "left":
64
+ return [
65
+ ["", "1"],
66
+ [self.introducer, "0", {"delay":0.5}],
67
+ [self.introducer, "+", {"delay":0.6}]
68
+ ]
69
+ else:
70
+ raise ValueError(f"Invalid side: {self.side}. Must be left or right.")
71
+
72
+ def __repr__(self):
73
+ return type(self).__name__ + "(" + str(self.other) + ',' + self.preaddress + ")"
74
+
75
+ class add_(apply_operation_):
76
+ def __init__(self, other, *args, **kwargs):
77
+ super().__init__(Add, other, *args, **kwargs)
78
+
79
+ class sub_(apply_operation_):
80
+ def __init__(self, other, *args, **kwargs):
81
+ super().__init__(Sub, other, *args, **kwargs)
82
+
83
+ class mul_(apply_operation_):
84
+ def __init__(self, other, *args, **kwargs):
85
+ super().__init__(Mul, other, *args, **kwargs)
86
+
87
+ class div_(apply_operation_):
88
+ def __init__(self, other, *args, **kwargs):
89
+ super().__init__(Div, other, *args, **kwargs)
90
+
91
+ class pow_(apply_operation_):
92
+ def __init__(self, other, *args, **kwargs):
93
+ super().__init__(Pow, other, *args, **kwargs)
94
+
95
+ class equals_(apply_operation_):
96
+ def __init__(self, other, *args, **kwargs):
97
+ super().__init__(Equation, other, *args, **kwargs)
98
+
99
+
100
+ class substitute_(Action):
101
+ def __init__(self, sub_dict, preaddress='', mode="transform", arc_size=PI, fade_shift=DOWN*0.2, lag=0, **kwargs):
102
+ self.sub_dict = sub_dict
103
+ self.preaddress = preaddress
104
+ self.mode = mode
105
+ self.arc_size = arc_size
106
+ self.fade_shift = fade_shift
107
+ self.lag = lag #usually looks like shit but can be cool sometimes
108
+ super().__init__(preaddress=preaddress,**kwargs)
109
+
110
+ @preaddressfunc
111
+ def get_output_expression(self, input_expression=None):
112
+ return input_expression.substitute(self.sub_dict)
113
+
114
+ @preaddressmap
115
+ def get_addressmap(self, input_expression=None):
116
+ target_addresses = []
117
+ for var in self.sub_dict:
118
+ target_addresses += input_expression.get_subex(self.preaddress).get_addresses_of_subex(var)
119
+ addressmap = []
120
+ if self.mode == "transform":
121
+ for i,ad in enumerate(target_addresses):
122
+ addressmap.append([ad, ad, {"delay": self.lag*i}])
123
+ return addressmap
124
+ elif self.mode == "swirl":
125
+ for i,ad in enumerate(target_addresses):
126
+ addressmap.append([ad, ad, {"path_arc": self.arc_size, "delay": self.lag*i}])
127
+ return addressmap
128
+ elif self.mode == "fade":
129
+ for i,ad in enumerate(target_addresses):
130
+ addressmap.append([ad, FadeOut, {"shift": self.fade_shift, "delay": self.lag*i}])
131
+ addressmap.append([FadeIn, ad, {"shift": self.fade_shift, "delay": self.lag*i}])
132
+ return addressmap
133
+
134
+ def __repr__(self):
135
+ return type(self).__name__ + "(" + str(self.sub_dict) + (',' + self.preaddress if self.preaddress else '') + ")"
136
+
137
+
138
+ class substitute_into_(Action):
139
+ def __init__(self, outer_expression, substitution_variable=Variable('x'), **kwargs):
140
+ self.outer_expression = outer_expression
141
+ self.substitution_variable = substitution_variable
142
+ super().__init__(**kwargs)
143
+
144
+ @preaddressfunc
145
+ def get_output_expression(self, input_expression=None):
146
+ return self.outer_expression.substitute({self.substitution_variable: input_expression})
147
+
148
+ @preaddressmap
149
+ def get_addressmap(self, input_expression=None):
150
+ addressmap = []
151
+ sub_into_addresses = self.outer_expression.get_addresses_of_subex(self.substitution_variable)
152
+ for ad in sub_into_addresses:
153
+ if not input_expression.parentheses:
154
+ ad = ad + "_"
155
+ addressmap.append(['', ad])
156
+ return addressmap
157
+
158
+ def get_animation(self, *args, **kwargs):
159
+ return super().get_animation(*args, auto_fade=True, auto_resolve_delay=0.75, **kwargs)
160
+
161
+
162
+ class evaluate_(Action):
163
+ def __init__(self, preaddress='', mode="random leaf", **kwargs):
164
+ super().__init__(preaddress=preaddress,**kwargs)
165
+ # if mode == "random leaf":
166
+ # leaf_addresses = input_expression.get_all_leaf_addresses()
167
+ # leaf_address = np.random.choice(leaves)
168
+ # self.preaddress = leaf_address
169
+
170
+ @preaddressfunc
171
+ def get_output_expression(self, input_expression=None):
172
+ return input_expression.evaluate()
173
+
174
+ @preaddressmap
175
+ def get_addressmap(self, input_expression=None):
176
+ return [
177
+ ["", ""] #extension by preaddress is done by decorator!
178
+ ]
179
+
180
+
181
+ class distribute_(Action):
182
+ # Not done yet, multilayer does not work... this is so necessary but rather nontrivial... hm...
183
+ def __init__(self, preaddress='', mode="auto", multilayer=False, **kwargs):
184
+ self.mode = mode #"auto", "left", "right"
185
+ super().__init__(preaddress=preaddress,**kwargs)
186
+
187
+ @preaddressfunc
188
+ def get_output_expression(self, input_expression=None):
189
+ if self.mode == "auto":
190
+ self.determine_direction(input_expression)
191
+ if self.mode == "left":
192
+ new_children = [
193
+ type(input_expression)(input_expression.children[0], child)
194
+ for child in input_expression.children[-1].children
195
+ ]
196
+ return type(input_expression.children[-1])(*new_children)
197
+ elif self.mode == "right":
198
+ new_children = [
199
+ type(input_expression)(child, input_expression.children[-1])
200
+ for child in input_expression.children[0].children
201
+ ]
202
+ return type(input_expression.children[0])(*new_children)
203
+
204
+ def determine_direction(self, input_expression=None):
205
+ if self.mode == "auto":
206
+ if isinstance(input_expression, Mul):
207
+ left_distributable = isinstance(input_expression.children[-1], (Add, Sub))
208
+ right_distributable = isinstance(input_expression.children[0], (Add, Sub))
209
+ if left_distributable and right_distributable:
210
+ raise ValueError("Cannot auto-distribute if both sides are distributable, please set mode manually.")
211
+ elif left_distributable:
212
+ self.mode = "left"
213
+ elif right_distributable:
214
+ self.mode = "right"
215
+ else:
216
+ raise ValueError("Cannot distribute, neither side is distributable.")
217
+ elif isinstance(input_expression, Div):
218
+ right_distributable = isinstance(input_expression.children[0], (Add, Sub))
219
+ if right_distributable:
220
+ self.mode = "right"
221
+ else:
222
+ raise ValueError("Cannot distribute, right side is not distributable.")
223
+ elif isinstance(input_expression, Pow):
224
+ right_distributable = isinstance(input_expression.children[0], (Mul, Div))
225
+ if right_distributable:
226
+ self.mode = "right"
227
+ else:
228
+ raise ValueError("Cannot distribute, right side is not distributable.")
229
+ else:
230
+ raise ValueError("Cannot auto-distribute, must be a multiplication or division.")
231
+
232
+ @preaddressmap
233
+ def get_addressmap(self, input_expression=None):
234
+ return [
235
+ ["", ""] #standin idk what the fuck im doing here
236
+ ]
237
+
238
+
239
+
240
+
241
+
242
+
243
+ from .action_variants import AlgebraicAction
244
+ a = Variable('a')
245
+ b = Variable('b')
246
+ c = Variable('c')
247
+
248
+ square_binomial_ = AlgebraicAction((a+b)**2, a**2 + 2*a*b + b**2)
249
+
@@ -0,0 +1,186 @@
1
+ # actions.py
2
+ from MF_Tools.dual_compatibility import Write, FadeOut
3
+ from ..expressions.expression_core import *
4
+ from ..utils import *
5
+ from .animations import TransformByAddressMap
6
+
7
+
8
+ class Action:
9
+ """
10
+ Transforms Expressions into other Expressions,
11
+ both as static objects and also with an animation.
12
+
13
+ An action is defined by two main things:
14
+ the get_output_expression method, which controls how it acts on static expressions,
15
+ and the get_addressmap method, which controls how it acts as an animation.
16
+ Both set attributes of the corresponding name and return them.
17
+
18
+ It may also have a preaddress parameter/attribute which will determine the subexpression
19
+ address at which the action is applied, and a few other attributes which may adjust some
20
+ specifics.
21
+
22
+ self.input_expression is set to None during __init__. It is critical that actions
23
+ can exist prior to being given expressions, so that they can be combined together.
24
+ When an input expression is received, this attribute is set, and the method
25
+ .get_output_expression is called, setting self.output_expression.
26
+ This is all that is required for static actions, no animations.
27
+
28
+ Now, to create the animation between these expressions:
29
+
30
+ get_addressmap is also unique to each action, and returns something like
31
+ [
32
+ ["00", "01"],
33
+ ["01", "00", {"path_arc":PI/2}],
34
+ [FadeIn, "1"],
35
+ ["1", FadeOut]
36
+ ]
37
+ which contains all the expression-agnostic information about the animation.
38
+ Often this will simply define and return this list with no computation.
39
+
40
+ get_glyphmap combines the input_expression, output_expression, and addressmap
41
+ to create a list like
42
+ [
43
+ ([0,1,2], [5,6]),
44
+ ([3,4,5], [1,2,3], {"path_arc":PI/2}),
45
+ (FadeIn, [8,9]),
46
+ ([6], FadeOut)
47
+ ]
48
+ which tells which glyphs of the mobjects to send to which others, and how.
49
+
50
+ get_animation then simply parses this glyphmap list to create a list of
51
+ animations, probably to be passed to AnimationGroup, like
52
+ [
53
+ ReplacementTransform(A[0][0,1,2], B[0][5,6]),
54
+ ReplacementTransform(A[0][3,4,5], B[0][1,2,3], path_arc=PI/2),
55
+ FadeIn(B[0][8,9]),
56
+ FadeOut(A[0][6]),
57
+ ...
58
+ ]
59
+ or something like that, the syntax is partially made up. The ... is
60
+ ReplacementTransforms of all the individual glyphs not mentioned in the glyphmap,
61
+ whose lengths have to exactly match.
62
+
63
+ Broadly speaking, that's that!
64
+ """
65
+ def __init__(self,
66
+ introducer=Write,
67
+ remover=FadeOut,
68
+ preaddress=''
69
+ ):
70
+ self.introducer = introducer
71
+ self.remover = remover
72
+ self.preaddress = preaddress
73
+
74
+ def get_output_expression(self, input_expression):
75
+ # define in subclasses and decorate with @preaddressfunc
76
+ raise NotImplementedError
77
+
78
+ def get_addressmap(self, input_expression, **kwargs):
79
+ # define in subclasses and decorate with @preaddressmap
80
+ raise NotImplementedError
81
+
82
+ def get_animation(self, **kwargs):
83
+ def animation(input_exp, output_exp=None):
84
+ if output_exp is None:
85
+ output_exp = self.get_output_expression(input_exp)
86
+ return TransformByAddressMap(
87
+ input_exp,
88
+ output_exp,
89
+ *self.get_addressmap(input_exp),
90
+ default_introducer=self.introducer,
91
+ default_remover=self.remover,
92
+ **kwargs
93
+ )
94
+ return animation
95
+
96
+ def __call__(self, expr1, expr2=None, **kwargs):
97
+ return self.get_animation(**kwargs)(expr1, expr2)
98
+
99
+ def __or__(self, other):
100
+ from .combinations import ParallelAction
101
+ if isinstance(other, ParallelAction):
102
+ return ParallelAction(self, *other.actions)
103
+ elif isinstance(other, Action):
104
+ return ParallelAction(self, other)
105
+ else:
106
+ raise ValueError("Can only use | with other ParallelAction or Action")
107
+
108
+ def __rshift__(self, other):
109
+ other = Smarten(other)
110
+ from ..expressions.expression_core import Expression
111
+ from ..timelines.timeline_core import Timeline
112
+ if isinstance(other, Expression):
113
+ timeline = Timeline()
114
+ timeline.add_action_to_end(self).add_expression_to_end(other)
115
+ return timeline
116
+ elif isinstance(other, Action):
117
+ timeline = Timeline()
118
+ timeline.add_action_to_end(self).add_action_to_end(other)
119
+ return timeline
120
+ else:
121
+ return NotImplemented
122
+
123
+ def __rrshift__(self, other):
124
+ return Smarten(other).__rshift__(self)
125
+
126
+ def __repr__(self):
127
+ return type(self).__name__ + "(" + self.preaddress + ")"
128
+
129
+ def copy(self):
130
+ return deepcopy(self)
131
+
132
+ @property
133
+ def both(self, number_of_sides=2):
134
+ # Intended to turn an action on an expression into an action done to both sides of an equation.
135
+ # Can be passed a number to apply to more than 2 sides for, say, a triple equation or inequality.
136
+ from .combinations import ParallelAction
137
+ actions = []
138
+ for i in range(number_of_sides):
139
+ action = self.copy()
140
+ action.preaddress = action.preaddress + str(i)
141
+ actions.append(action)
142
+ return ParallelAction(*actions)
143
+
144
+
145
+
146
+ def preaddressfunc(func):
147
+ def wrapper(action, expr, *args, **kwargs):
148
+ expr = expr.copy()
149
+ preaddress = kwargs.get('preaddress', '') or action.preaddress
150
+ if len(preaddress)==0:
151
+ output_expression = func(action, expr)
152
+ else:
153
+ active_part = expr.get_subex(preaddress)
154
+ result = func(action, active_part)
155
+ output_expression = expr.substitute_at_address(result, preaddress)
156
+ output_expression.reset_parentheses()
157
+ return output_expression
158
+ return wrapper
159
+
160
+ def preaddressmap(getmap):
161
+ def wrapper(action, expr, *args, **kwargs):
162
+ expr = expr.copy()
163
+ preaddress = kwargs.get('preaddress', '') or action.preaddress
164
+ addressmap = getmap(action, expr, *args, **kwargs)
165
+ if preaddress:
166
+ for entry in addressmap:
167
+ for i, ad in enumerate(entry):
168
+ if isinstance(ad, str):
169
+ entry[i] = preaddress + ad
170
+ return addressmap
171
+ return wrapper
172
+
173
+
174
+ class IncompatibleExpression(Exception):
175
+ pass
176
+
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
185
+
186
+
@@ -0,0 +1,97 @@
1
+ from .action_core import *
2
+ from MF_Tools.dual_compatibility import AnimationGroup
3
+ from MF_Tools import TransformByGlyphMap
4
+
5
+
6
+ class AlgebraicAction(Action):
7
+ def __init__(self, template1, template2, var_kwarg_dict={}, **kwargs):
8
+ super().__init__(**kwargs)
9
+ self.template1 = template1
10
+ self.template2 = template2
11
+ self.var_kwarg_dict = var_kwarg_dict #{a:{"path_arc":PI}}
12
+
13
+ def get_output_expression(self, input_expression=None):
14
+ var_dict = match_expressions(self.template1, input_expression)
15
+ return self.template2.substitute(var_dict)
16
+
17
+ def get_addressmap(self, input_expression=None):
18
+ addressmap = []
19
+ def get_var_ad_dict(template):
20
+ template_leaves = {
21
+ template.get_subex(ad)
22
+ for ad in input_expression.get_all_leaf_addresses()
23
+ }
24
+ from ..expressions.variables import Variable
25
+ variables = [var for var in template_leaves if isinstance(var, Variable)]
26
+ return {var: template.get_addresses_of_subex(var) for var in variables}
27
+ self.template1_address_dict = get_var_ad_dict(self.template1)
28
+ self.template2_address_dict = get_var_ad_dict(self.template2)
29
+ variables = self.template1_address_dict.keys() | self.template2_address_dict.keys()
30
+ for var in variables:
31
+ kwargs = self.var_kwarg_dict.get(var, {})
32
+ if len(self.template1_address_dict[var]) == 1:
33
+ addressmap += [[self.template1_address_dict[var][0], t2ad, kwargs] for t2ad in self.template2_address_dict[var]]
34
+ elif len(self.template2_address_dict[var]) == 1:
35
+ addressmap += [[t1ad, self.template2_address_dict[var][0], kwargs] for t1ad in self.template1_address_dict[var]]
36
+ else:
37
+ raise ValueError("I don't know what to do when a variable appears more than once on both sides. Please set addressmap manually.")
38
+ return addressmap
39
+
40
+
41
+ class AddressMapAction(Action):
42
+ def __init__(self, *address_map, extra_animations=[], **kwargs):
43
+ super().__init__(**kwargs)
44
+ self.address_map = address_map
45
+ self.extra_animations = extra_animations
46
+
47
+ def get_animation(self, **kwargs):
48
+ def animation(input_exp, output_exp=None):
49
+ if output_exp is None:
50
+ output_exp = self.get_output_expression(input_exp)
51
+ return AnimationGroup(
52
+ TransformByAddressMap(
53
+ input_exp,
54
+ output_exp,
55
+ *self.address_map,
56
+ **kwargs
57
+ ),
58
+ *self.extra_animations
59
+ )
60
+ return animation
61
+
62
+
63
+ class GlyphMapAction(Action):
64
+ def __init__(self, *glyph_map, extra_animations=[], show_indices=False, **kwargs):
65
+ super().__init__(**kwargs)
66
+ self.glyph_map = glyph_map
67
+ self.extra_animations = extra_animations
68
+ self.show_indices = show_indices
69
+
70
+ def get_animation(self, **kwargs):
71
+ def animation(input_exp, output_exp=None):
72
+ if output_exp is None:
73
+ output_exp = self.get_output_expression(input_exp)
74
+ return AnimationGroup(
75
+ TransformByGlyphMap(
76
+ input_exp.mob,
77
+ output_exp.mob,
78
+ *self.glyph_map,
79
+ show_indices = self.show_indices,
80
+ **kwargs
81
+ ),
82
+ *self.extra_animations
83
+ )
84
+ return animation
85
+
86
+
87
+ class AnimationAction(Action):
88
+ def __init__(self, animation, **kwargs):
89
+ super().__init__(**kwargs)
90
+ self.animation = animation # callable on two mobjects
91
+
92
+ def get_animation(self, **kwargs):
93
+ def animation(self, input_exp, output_exp=None):
94
+ if output_exp is None:
95
+ output_exp = self.get_output_expression(input_exp)
96
+ return self.animation(input_exp.mob, output_exp.mob, **kwargs)
97
+ return animation
@@ -0,0 +1,23 @@
1
+ from MF_Tools.transforms import TransformByGlyphMap
2
+
3
+
4
+
5
+ class TransformByAddressMap(TransformByGlyphMap):
6
+ def __init__(self, expA, expB, *addressmap, **kwargs):
7
+ glyphmap = self.addressmap_to_glyphmap(expA, expB, addressmap)
8
+ super().__init__(expA.mob, expB.mob, *glyphmap, **kwargs)
9
+
10
+ def addressmap_to_glyphmap(self, expA, expB, addressmap):
11
+ glyphmap = [
12
+ (
13
+ expA.get_glyphs(entry[0]) if isinstance(entry[0], str) else entry[0],
14
+ expB.get_glyphs(entry[1]) if isinstance(entry[1], str) else entry[1],
15
+ entry[2] if len(entry) > 2 else {}
16
+ )
17
+ for entry in addressmap
18
+ ]
19
+ return glyphmap
20
+
21
+
22
+
23
+
@@ -0,0 +1,72 @@
1
+ from .action_core import *
2
+
3
+
4
+ class ParallelAction(Action):
5
+ def __init__(self, *actions, **kwargs):
6
+ self.actions = actions
7
+ super().__init__(**kwargs)
8
+
9
+ def get_output_expression(self, input_expression=None):
10
+ expr = input_expression
11
+ for action in self.actions:
12
+ expr = action.get_output_expression(expr)
13
+ return expr
14
+
15
+ def get_addressmap(self, input_expression=None):
16
+ return sum([action.get_addressmap(input_expression) for action in self.actions], [])
17
+
18
+ def __or__(self, other):
19
+ if isinstance(other, ParallelAction):
20
+ return ParallelAction(*self.actions, *other.actions)
21
+ elif isinstance(other, Action):
22
+ return ParallelAction(*self.actions, other)
23
+ else:
24
+ raise ValueError("Can only use | with other ParallelAction or Action")
25
+
26
+ def __ror__(self, other):
27
+ if isinstance(other, Action):
28
+ return ParallelAction(other, *self.actions)
29
+ else:
30
+ return NotImplemented
31
+
32
+
33
+
34
+ class SequentialAction(Action):
35
+ def __init__(self, *actions, **kwargs):
36
+ self.actions = list(actions)
37
+ super().__init__(**kwargs)
38
+
39
+ def get_output_expression(self, input_expression=None):
40
+ expr = input_expression
41
+ for action in self.actions:
42
+ expr = action.get_output_expression(expr)
43
+ return expr
44
+
45
+ def get_animations(self):
46
+ return [action.get_animation() for action in self.actions]
47
+
48
+ def get_animation(self, i):
49
+ return self.actions[i].get_animation()
50
+
51
+ def __rshift__(self, other):
52
+ if isinstance(other, SequentialAction):
53
+ return SequentialAction(*self.actions, *other.actions)
54
+ elif isinstance(other, Action):
55
+ return SequentialAction(*self.actions, other)
56
+ else:
57
+ return ValueError("Can only use >> with other SequentialAction or Action")
58
+
59
+ def __rrshift__(self, other):
60
+ if isinstance(other, SequentialAction):
61
+ return SequentialAction(*other.actions, *self.actions)
62
+ elif isinstance(other, Action):
63
+ return SequentialAction(other, *self.actions)
64
+ else:
65
+ return ValueError("Can only use >> with other SequentialAction or Action")
66
+
67
+ def __getitem__(self, key):
68
+ if isinstance(key, int):
69
+ return self.actions[key]
70
+ elif isinstance(key, slice):
71
+ return SequentialAction(*self.actions[key])
72
+
@@ -0,0 +1,8 @@
1
+ from .expression_core import *
2
+ from .operations import *
3
+ from .numbers import *
4
+ from .variables import *
5
+ from .functions import *
6
+ from .sequences import *
7
+ from .relations import *
8
+ from .expression_common import *