sonolus.py 0.1.4__py3-none-any.whl → 0.1.5__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.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/finalize.py +18 -10
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +24 -0
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/{allocate.py → optimize/allocate.py} +4 -3
- sonolus/backend/{constant_evaluation.py → optimize/constant_evaluation.py} +7 -7
- sonolus/backend/{coalesce.py → optimize/copy_coalesce.py} +3 -3
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/{dominance.py → optimize/dominance.py} +2 -17
- sonolus/backend/{flow.py → optimize/flow.py} +6 -5
- sonolus/backend/{inlining.py → optimize/inlining.py} +4 -17
- sonolus/backend/{liveness.py → optimize/liveness.py} +69 -65
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/{passes.py → optimize/passes.py} +1 -1
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/{ssa.py → optimize/ssa.py} +31 -18
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +10 -0
- sonolus/backend/visitor.py +360 -101
- sonolus/build/compile.py +8 -8
- sonolus/build/engine.py +10 -5
- sonolus/script/archetype.py +419 -137
- sonolus/script/array.py +25 -8
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +73 -11
- sonolus/script/containers.py +234 -51
- sonolus/script/debug.py +8 -8
- sonolus/script/easing.py +147 -105
- sonolus/script/effect.py +60 -0
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +66 -32
- sonolus/script/instruction.py +79 -25
- sonolus/script/internal/builtin_impls.py +138 -27
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +14 -5
- sonolus/script/internal/dict_impl.py +65 -0
- sonolus/script/internal/generic.py +6 -9
- sonolus/script/internal/impl.py +38 -13
- sonolus/script/internal/introspection.py +5 -2
- sonolus/script/{math.py → internal/math_impls.py} +28 -28
- sonolus/script/internal/native.py +3 -3
- sonolus/script/internal/random.py +67 -0
- sonolus/script/internal/range.py +81 -0
- sonolus/script/internal/transient.py +51 -0
- sonolus/script/internal/tuple_impl.py +113 -0
- sonolus/script/interval.py +234 -16
- sonolus/script/iterator.py +120 -167
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +79 -47
- sonolus/script/options.py +78 -12
- sonolus/script/particle.py +37 -4
- sonolus/script/pointer.py +4 -4
- sonolus/script/print.py +22 -1
- sonolus/script/project.py +8 -0
- sonolus/script/{graphics.py → quad.py} +75 -12
- sonolus/script/record.py +44 -13
- sonolus/script/runtime.py +50 -1
- sonolus/script/sprite.py +197 -112
- sonolus/script/text.py +2 -0
- sonolus/script/timing.py +72 -0
- sonolus/script/transform.py +296 -66
- sonolus/script/ui.py +134 -78
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +118 -3
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
- sonolus_py-0.1.5.dist-info/RECORD +89 -0
- sonolus/backend/dead_code.py +0 -80
- sonolus/backend/optimize.py +0 -37
- sonolus/backend/simplify.py +0 -47
- sonolus/script/comptime.py +0 -160
- sonolus/script/random.py +0 -14
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.4.dist-info/RECORD +0 -84
- /sonolus/script/{callbacks.py → internal/callbacks.py} +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +0 -0
sonolus/script/easing.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# ruff: noqa: E501
|
|
1
2
|
import math
|
|
2
3
|
|
|
3
4
|
from sonolus.backend.ops import Op
|
|
@@ -7,6 +8,7 @@ from sonolus.script.interval import clamp
|
|
|
7
8
|
|
|
8
9
|
@native_function(Op.EaseInBack)
|
|
9
10
|
def ease_in_back(x: float) -> float:
|
|
11
|
+
"""Interpolate between 0 and 1, starting slow and ending fast, overshooting below 0 at the start."""
|
|
10
12
|
x = clamp(x, 0, 1)
|
|
11
13
|
c1 = 1.70158
|
|
12
14
|
c3 = c1 + 1
|
|
@@ -15,6 +17,7 @@ def ease_in_back(x: float) -> float:
|
|
|
15
17
|
|
|
16
18
|
@native_function(Op.EaseOutBack)
|
|
17
19
|
def ease_out_back(x: float) -> float:
|
|
20
|
+
"""Interpolate between 0 and 1, starting fast and ending slow, overshooting above 1 at the end."""
|
|
18
21
|
x = clamp(x, 0, 1)
|
|
19
22
|
c1 = 1.70158
|
|
20
23
|
c3 = c1 + 1
|
|
@@ -23,6 +26,7 @@ def ease_out_back(x: float) -> float:
|
|
|
23
26
|
|
|
24
27
|
@native_function(Op.EaseInOutBack)
|
|
25
28
|
def ease_in_out_back(x: float) -> float:
|
|
29
|
+
"""Interpolate between 0 and 1, starting and ending slow with overshooting, fast in the middle."""
|
|
26
30
|
x = clamp(x, 0, 1)
|
|
27
31
|
c1 = 1.70158
|
|
28
32
|
c2 = c1 * 1.525
|
|
@@ -32,20 +36,35 @@ def ease_in_out_back(x: float) -> float:
|
|
|
32
36
|
return ((2 * x - 2) ** 2 * ((c2 + 1) * (2 * x - 2) + c2) + 2) / 2
|
|
33
37
|
|
|
34
38
|
|
|
39
|
+
@native_function(Op.EaseOutInBack)
|
|
40
|
+
def ease_out_in_back(x: float) -> float:
|
|
41
|
+
"""Interpolate between 0 and 1, fast at the start and end, slow in the middle with overshooting."""
|
|
42
|
+
x = clamp(x, 0, 1)
|
|
43
|
+
c1 = 1.70158
|
|
44
|
+
c3 = c1 + 1
|
|
45
|
+
if x < 0.5:
|
|
46
|
+
return (1 + c3 * (2 * x - 1) ** 3 + c1 * (2 * x - 1) ** 2) / 2
|
|
47
|
+
else:
|
|
48
|
+
return (c3 * (2 * x - 1) ** 3 - c1 * (2 * x - 1) ** 2) / 2 + 0.5
|
|
49
|
+
|
|
50
|
+
|
|
35
51
|
@native_function(Op.EaseInCirc)
|
|
36
52
|
def ease_in_circ(x: float) -> float:
|
|
53
|
+
"""Interpolate between 0 and 1, starting slow and ending very fast."""
|
|
37
54
|
x = clamp(x, 0, 1)
|
|
38
55
|
return 1 - math.sqrt(1 - x**2)
|
|
39
56
|
|
|
40
57
|
|
|
41
58
|
@native_function(Op.EaseOutCirc)
|
|
42
59
|
def ease_out_circ(x: float) -> float:
|
|
60
|
+
"""Interpolate between 0 and 1, starting very fast and ending slow."""
|
|
43
61
|
x = clamp(x, 0, 1)
|
|
44
62
|
return math.sqrt(1 - (x - 1) ** 2)
|
|
45
63
|
|
|
46
64
|
|
|
47
65
|
@native_function(Op.EaseInOutCirc)
|
|
48
66
|
def ease_in_out_circ(x: float) -> float:
|
|
67
|
+
"""Interpolate between 0 and 1, starting and ending slow, very fast in the middle."""
|
|
49
68
|
x = clamp(x, 0, 1)
|
|
50
69
|
if x < 0.5:
|
|
51
70
|
return (1 - math.sqrt(1 - (2 * x) ** 2)) / 2
|
|
@@ -53,20 +72,33 @@ def ease_in_out_circ(x: float) -> float:
|
|
|
53
72
|
return (math.sqrt(1 - (2 * x - 2) ** 2) + 1) / 2
|
|
54
73
|
|
|
55
74
|
|
|
75
|
+
@native_function(Op.EaseOutInCirc)
|
|
76
|
+
def ease_out_in_circ(x: float) -> float:
|
|
77
|
+
"""Interpolate between 0 and 1, very fast at the start and end, slow in the middle."""
|
|
78
|
+
x = clamp(x, 0, 1)
|
|
79
|
+
if x < 0.5:
|
|
80
|
+
return math.sqrt(1 - (2 * x - 1) ** 2) / 2
|
|
81
|
+
else:
|
|
82
|
+
return (1 - math.sqrt(1 - (2 * x - 1) ** 2)) / 2 + 0.5
|
|
83
|
+
|
|
84
|
+
|
|
56
85
|
@native_function(Op.EaseInCubic)
|
|
57
86
|
def ease_in_cubic(x: float) -> float:
|
|
87
|
+
"""Interpolate between 0 and 1, starting slow and ending fast with cubic easing."""
|
|
58
88
|
x = clamp(x, 0, 1)
|
|
59
89
|
return x**3
|
|
60
90
|
|
|
61
91
|
|
|
62
92
|
@native_function(Op.EaseOutCubic)
|
|
63
93
|
def ease_out_cubic(x: float) -> float:
|
|
94
|
+
"""Interpolate between 0 and 1, starting fast and ending slow with cubic easing."""
|
|
64
95
|
x = clamp(x, 0, 1)
|
|
65
96
|
return 1 - (1 - x) ** 3
|
|
66
97
|
|
|
67
98
|
|
|
68
99
|
@native_function(Op.EaseInOutCubic)
|
|
69
100
|
def ease_in_out_cubic(x: float) -> float:
|
|
101
|
+
"""Interpolate between 0 and 1, starting and ending slow with cubic easing, fast in the middle."""
|
|
70
102
|
x = clamp(x, 0, 1)
|
|
71
103
|
if x < 0.5:
|
|
72
104
|
return 4 * x**3
|
|
@@ -74,8 +106,19 @@ def ease_in_out_cubic(x: float) -> float:
|
|
|
74
106
|
return 1 - (-2 * x + 2) ** 3 / 2
|
|
75
107
|
|
|
76
108
|
|
|
109
|
+
@native_function(Op.EaseOutInCubic)
|
|
110
|
+
def ease_out_in_cubic(x: float) -> float:
|
|
111
|
+
"""Interpolate between 0 and 1, fast at the start and end, slow in the middle with cubic easing."""
|
|
112
|
+
x = clamp(x, 0, 1)
|
|
113
|
+
if x < 0.5:
|
|
114
|
+
return (1 - (1 - 2 * x) ** 3) / 2
|
|
115
|
+
else:
|
|
116
|
+
return ((2 * x - 1) ** 3) / 2 + 0.5
|
|
117
|
+
|
|
118
|
+
|
|
77
119
|
@native_function(Op.EaseInElastic)
|
|
78
120
|
def ease_in_elastic(x: float) -> float:
|
|
121
|
+
"""Interpolate between 0 and 1 with oscillations, starting slow and ending fast."""
|
|
79
122
|
x = clamp(x, 0, 1)
|
|
80
123
|
c4 = (2 * math.pi) / 3
|
|
81
124
|
if x in {0, 1}:
|
|
@@ -86,6 +129,7 @@ def ease_in_elastic(x: float) -> float:
|
|
|
86
129
|
|
|
87
130
|
@native_function(Op.EaseOutElastic)
|
|
88
131
|
def ease_out_elastic(x: float) -> float:
|
|
132
|
+
"""Interpolate between 0 and 1 with oscillations, starting fast and ending slow."""
|
|
89
133
|
x = clamp(x, 0, 1)
|
|
90
134
|
c4 = (2 * math.pi) / 3
|
|
91
135
|
if x in {0, 1}:
|
|
@@ -96,6 +140,7 @@ def ease_out_elastic(x: float) -> float:
|
|
|
96
140
|
|
|
97
141
|
@native_function(Op.EaseInOutElastic)
|
|
98
142
|
def ease_in_out_elastic(x: float) -> float:
|
|
143
|
+
"""Interpolate between 0 and 1 with oscillations, slow at the start and end, fast in the middle."""
|
|
99
144
|
x = clamp(x, 0, 1)
|
|
100
145
|
c5 = (2 * math.pi) / 4.5
|
|
101
146
|
if x in {0, 1}:
|
|
@@ -106,20 +151,39 @@ def ease_in_out_elastic(x: float) -> float:
|
|
|
106
151
|
return (2 ** (-20 * x + 10) * math.sin((20 * x - 11.125) * c5)) / 2 + 1
|
|
107
152
|
|
|
108
153
|
|
|
154
|
+
@native_function(Op.EaseOutInElastic)
|
|
155
|
+
def ease_out_in_elastic(x: float) -> float:
|
|
156
|
+
"""Interpolate between 0 and 1 with oscillations, fast at the start and end, slow in the middle."""
|
|
157
|
+
x = clamp(x, 0, 1)
|
|
158
|
+
c4 = (2 * math.pi) / 3
|
|
159
|
+
if x < 0.5:
|
|
160
|
+
if x == 0:
|
|
161
|
+
return 0
|
|
162
|
+
else:
|
|
163
|
+
return (2 ** (-20 * x + 10) * math.sin((20 * x - 0.75) * c4)) / 2 + 0.5
|
|
164
|
+
elif x == 1:
|
|
165
|
+
return 1
|
|
166
|
+
else:
|
|
167
|
+
return (-(2 ** (10 * (2 * x - 1) - 10)) * math.sin((20 * x - 10.75) * c4)) / 2 + 0.5
|
|
168
|
+
|
|
169
|
+
|
|
109
170
|
@native_function(Op.EaseInExpo)
|
|
110
171
|
def ease_in_expo(x: float) -> float:
|
|
172
|
+
"""Interpolate between 0 and 1, starting extremely slow and ending extremely fast."""
|
|
111
173
|
x = clamp(x, 0, 1)
|
|
112
174
|
return 0 if x == 0 else 2 ** (10 * x - 10)
|
|
113
175
|
|
|
114
176
|
|
|
115
177
|
@native_function(Op.EaseOutExpo)
|
|
116
178
|
def ease_out_expo(x: float) -> float:
|
|
179
|
+
"""Interpolate between 0 and 1, starting extremely fast and ending extremely slow."""
|
|
117
180
|
x = clamp(x, 0, 1)
|
|
118
181
|
return 1 if x == 1 else 1 - 2 ** (-10 * x)
|
|
119
182
|
|
|
120
183
|
|
|
121
184
|
@native_function(Op.EaseInOutExpo)
|
|
122
185
|
def ease_in_out_expo(x: float) -> float:
|
|
186
|
+
"""Interpolate between 0 and 1, starting and ending extremely slow, fast in the middle."""
|
|
123
187
|
x = clamp(x, 0, 1)
|
|
124
188
|
if x in {0, 1}:
|
|
125
189
|
return x
|
|
@@ -129,195 +193,173 @@ def ease_in_out_expo(x: float) -> float:
|
|
|
129
193
|
return (2 - 2 ** (-20 * x + 10)) / 2
|
|
130
194
|
|
|
131
195
|
|
|
132
|
-
@native_function(Op.
|
|
133
|
-
def
|
|
134
|
-
|
|
135
|
-
if x < 0.5:
|
|
136
|
-
return 2 * x**2
|
|
137
|
-
else:
|
|
138
|
-
return 1 - (-2 * x + 2) ** 2 / 2
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
@native_function(Op.EaseInOutQuart)
|
|
142
|
-
def ease_in_out_quart(x: float) -> float:
|
|
143
|
-
x = clamp(x, 0, 1)
|
|
144
|
-
if x < 0.5:
|
|
145
|
-
return 8 * x**4
|
|
146
|
-
else:
|
|
147
|
-
return 1 - (-2 * x + 2) ** 4 / 2
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
@native_function(Op.EaseInOutQuint)
|
|
151
|
-
def ease_in_out_quint(x: float) -> float:
|
|
196
|
+
@native_function(Op.EaseOutInExpo)
|
|
197
|
+
def ease_out_in_expo(x: float) -> float:
|
|
198
|
+
"""Interpolate between 0 and 1, extremely fast at the start and end, extremely slow in the middle."""
|
|
152
199
|
x = clamp(x, 0, 1)
|
|
153
|
-
if x
|
|
154
|
-
return
|
|
200
|
+
if x in {0, 1}:
|
|
201
|
+
return x
|
|
202
|
+
elif x < 0.5:
|
|
203
|
+
return (1 - 2 ** (-20 * x)) / 2
|
|
155
204
|
else:
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
@native_function(Op.EaseInOutSine)
|
|
160
|
-
def ease_in_out_sine(x: float) -> float:
|
|
161
|
-
x = clamp(x, 0, 1)
|
|
162
|
-
return -(math.cos(math.pi * x) - 1) / 2
|
|
205
|
+
return (2 ** (20 * x - 20)) / 2 + 0.5
|
|
163
206
|
|
|
164
207
|
|
|
165
208
|
@native_function(Op.EaseInQuad)
|
|
166
209
|
def ease_in_quad(x: float) -> float:
|
|
210
|
+
"""Interpolate between 0 and 1, starting slow and ending fast with quadratic easing."""
|
|
167
211
|
x = clamp(x, 0, 1)
|
|
168
212
|
return x**2
|
|
169
213
|
|
|
170
214
|
|
|
171
215
|
@native_function(Op.EaseOutQuad)
|
|
172
216
|
def ease_out_quad(x: float) -> float:
|
|
217
|
+
"""Interpolate between 0 and 1, starting fast and ending slow with quadratic easing."""
|
|
173
218
|
x = clamp(x, 0, 1)
|
|
174
219
|
return 1 - (1 - x) ** 2
|
|
175
220
|
|
|
176
221
|
|
|
177
|
-
@native_function(Op.
|
|
178
|
-
def
|
|
179
|
-
|
|
180
|
-
return x**4
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
@native_function(Op.EaseOutQuart)
|
|
184
|
-
def ease_out_quart(x: float) -> float:
|
|
222
|
+
@native_function(Op.EaseInOutQuad)
|
|
223
|
+
def ease_in_out_quad(x: float) -> float:
|
|
224
|
+
"""Interpolate between 0 and 1, starting and ending slow with quadratic easing, fast in the middle."""
|
|
185
225
|
x = clamp(x, 0, 1)
|
|
186
|
-
|
|
226
|
+
if x < 0.5:
|
|
227
|
+
return 2 * x**2
|
|
228
|
+
else:
|
|
229
|
+
return 1 - (-2 * x + 2) ** 2 / 2
|
|
187
230
|
|
|
188
231
|
|
|
189
|
-
@native_function(Op.
|
|
190
|
-
def
|
|
232
|
+
@native_function(Op.EaseOutInQuad)
|
|
233
|
+
def ease_out_in_quad(x: float) -> float:
|
|
234
|
+
"""Interpolate between 0 and 1, fast at the start and end, slow in the middle with quadratic easing."""
|
|
191
235
|
x = clamp(x, 0, 1)
|
|
192
|
-
|
|
236
|
+
if x < 0.5:
|
|
237
|
+
return (1 - (1 - 2 * x) ** 2) / 2
|
|
238
|
+
else:
|
|
239
|
+
return ((2 * x - 1) ** 2) / 2 + 0.5
|
|
193
240
|
|
|
194
241
|
|
|
195
|
-
@native_function(Op.
|
|
196
|
-
def
|
|
242
|
+
@native_function(Op.EaseInQuart)
|
|
243
|
+
def ease_in_quart(x: float) -> float:
|
|
244
|
+
"""Interpolate between 0 and 1, starting very slow and ending very fast with quartic easing."""
|
|
197
245
|
x = clamp(x, 0, 1)
|
|
198
|
-
return
|
|
246
|
+
return x**4
|
|
199
247
|
|
|
200
248
|
|
|
201
|
-
@native_function(Op.
|
|
202
|
-
def
|
|
249
|
+
@native_function(Op.EaseOutQuart)
|
|
250
|
+
def ease_out_quart(x: float) -> float:
|
|
251
|
+
"""Interpolate between 0 and 1, starting very fast and ending very slow with quartic easing."""
|
|
203
252
|
x = clamp(x, 0, 1)
|
|
204
|
-
return 1 -
|
|
253
|
+
return 1 - (1 - x) ** 4
|
|
205
254
|
|
|
206
255
|
|
|
207
|
-
@native_function(Op.
|
|
208
|
-
def
|
|
256
|
+
@native_function(Op.EaseInOutQuart)
|
|
257
|
+
def ease_in_out_quart(x: float) -> float:
|
|
258
|
+
"""Interpolate between 0 and 1, starting and ending very slow with quartic easing, very fast in the middle."""
|
|
209
259
|
x = clamp(x, 0, 1)
|
|
210
|
-
|
|
260
|
+
if x < 0.5:
|
|
261
|
+
return 8 * x**4
|
|
262
|
+
else:
|
|
263
|
+
return 1 - (-2 * x + 2) ** 4 / 2
|
|
211
264
|
|
|
212
265
|
|
|
213
|
-
@native_function(Op.
|
|
214
|
-
def
|
|
266
|
+
@native_function(Op.EaseOutInQuart)
|
|
267
|
+
def ease_out_in_quart(x: float) -> float:
|
|
268
|
+
"""Interpolate between 0 and 1, very fast at the start and end, very slow in the middle with quartic easing."""
|
|
215
269
|
x = clamp(x, 0, 1)
|
|
216
|
-
c1 = 1.70158
|
|
217
|
-
c3 = c1 + 1
|
|
218
270
|
if x < 0.5:
|
|
219
|
-
return (1
|
|
271
|
+
return (1 - (1 - 2 * x) ** 4) / 2
|
|
220
272
|
else:
|
|
221
|
-
return (
|
|
273
|
+
return ((2 * x - 1) ** 4) / 2 + 0.5
|
|
222
274
|
|
|
223
275
|
|
|
224
|
-
@native_function(Op.
|
|
225
|
-
def
|
|
276
|
+
@native_function(Op.EaseInQuint)
|
|
277
|
+
def ease_in_quint(x: float) -> float:
|
|
278
|
+
"""Interpolate between 0 and 1, starting extremely slow and ending extremely fast with quintic easing."""
|
|
226
279
|
x = clamp(x, 0, 1)
|
|
227
|
-
|
|
228
|
-
return (math.sqrt(1 - (2 * x - 1) ** 2)) / 2
|
|
229
|
-
else:
|
|
230
|
-
return (1 - math.sqrt(1 - (2 * x - 1) ** 2)) / 2 + 0.5
|
|
280
|
+
return x**5
|
|
231
281
|
|
|
232
282
|
|
|
233
|
-
@native_function(Op.
|
|
234
|
-
def
|
|
283
|
+
@native_function(Op.EaseOutQuint)
|
|
284
|
+
def ease_out_quint(x: float) -> float:
|
|
285
|
+
"""Interpolate between 0 and 1, starting extremely fast and ending extremely slow with quintic easing."""
|
|
235
286
|
x = clamp(x, 0, 1)
|
|
236
|
-
|
|
237
|
-
return (1 - (1 - 2 * x) ** 3) / 2
|
|
238
|
-
else:
|
|
239
|
-
return ((2 * x - 1) ** 3) / 2 + 0.5
|
|
287
|
+
return 1 - (1 - x) ** 5
|
|
240
288
|
|
|
241
289
|
|
|
242
|
-
@native_function(Op.
|
|
243
|
-
def
|
|
290
|
+
@native_function(Op.EaseInOutQuint)
|
|
291
|
+
def ease_in_out_quint(x: float) -> float:
|
|
292
|
+
"""Interpolate between 0 and 1, starting and ending extremely slow with quintic easing, extremely fast in the middle."""
|
|
244
293
|
x = clamp(x, 0, 1)
|
|
245
|
-
c4 = (2 * math.pi) / 3
|
|
246
294
|
if x < 0.5:
|
|
247
|
-
|
|
248
|
-
return 0
|
|
249
|
-
else:
|
|
250
|
-
return (2 ** (-20 * x + 10) * math.sin((20 * x - 0.75) * c4)) / 2 + 0.5
|
|
251
|
-
elif x == 1:
|
|
252
|
-
return 1
|
|
295
|
+
return 16 * x**5
|
|
253
296
|
else:
|
|
254
|
-
return
|
|
297
|
+
return 1 - (-2 * x + 2) ** 5 / 2
|
|
255
298
|
|
|
256
299
|
|
|
257
|
-
@native_function(Op.
|
|
258
|
-
def
|
|
300
|
+
@native_function(Op.EaseOutInQuint)
|
|
301
|
+
def ease_out_in_quint(x: float) -> float:
|
|
302
|
+
"""Interpolate between 0 and 1, extremely fast at the start and end, extremely slow in the middle with quintic easing."""
|
|
259
303
|
x = clamp(x, 0, 1)
|
|
260
|
-
if x
|
|
261
|
-
return x
|
|
262
|
-
elif x < 0.5:
|
|
263
|
-
return (1 - 2 ** (-20 * x)) / 2
|
|
304
|
+
if x < 0.5:
|
|
305
|
+
return (1 - (1 - 2 * x) ** 5) / 2
|
|
264
306
|
else:
|
|
265
|
-
return (2
|
|
307
|
+
return ((2 * x - 1) ** 5) / 2 + 0.5
|
|
266
308
|
|
|
267
309
|
|
|
268
|
-
@native_function(Op.
|
|
269
|
-
def
|
|
310
|
+
@native_function(Op.EaseInSine)
|
|
311
|
+
def ease_in_sine(x: float) -> float:
|
|
312
|
+
"""Interpolate between 0 and 1, starting slow and ending fast with sine easing."""
|
|
270
313
|
x = clamp(x, 0, 1)
|
|
271
|
-
|
|
272
|
-
return (1 - (1 - 2 * x) ** 2) / 2
|
|
273
|
-
else:
|
|
274
|
-
return ((2 * x - 1) ** 2) / 2 + 0.5
|
|
314
|
+
return 1 - math.cos((x * math.pi) / 2)
|
|
275
315
|
|
|
276
316
|
|
|
277
|
-
@native_function(Op.
|
|
278
|
-
def
|
|
317
|
+
@native_function(Op.EaseOutSine)
|
|
318
|
+
def ease_out_sine(x: float) -> float:
|
|
319
|
+
"""Interpolate between 0 and 1, starting fast and ending slow with sine easing."""
|
|
279
320
|
x = clamp(x, 0, 1)
|
|
280
|
-
|
|
281
|
-
return (1 - (1 - 2 * x) ** 4) / 2
|
|
282
|
-
else:
|
|
283
|
-
return ((2 * x - 1) ** 4) / 2 + 0.5
|
|
321
|
+
return math.sin((x * math.pi) / 2)
|
|
284
322
|
|
|
285
323
|
|
|
286
|
-
@native_function(Op.
|
|
287
|
-
def
|
|
324
|
+
@native_function(Op.EaseInOutSine)
|
|
325
|
+
def ease_in_out_sine(x: float) -> float:
|
|
326
|
+
"""Interpolate between 0 and 1, starting and ending slow with sine easing, fast in the middle."""
|
|
288
327
|
x = clamp(x, 0, 1)
|
|
289
|
-
|
|
290
|
-
return (1 - (1 - 2 * x) ** 5) / 2
|
|
291
|
-
else:
|
|
292
|
-
return ((2 * x - 1) ** 5) / 2 + 0.5
|
|
328
|
+
return -(math.cos(math.pi * x) - 1) / 2
|
|
293
329
|
|
|
294
330
|
|
|
295
331
|
@native_function(Op.EaseOutInSine)
|
|
296
332
|
def ease_out_in_sine(x: float) -> float:
|
|
333
|
+
"""Interpolate between 0 and 1, fast at the start and end, slow in the middle with sine easing."""
|
|
297
334
|
x = clamp(x, 0, 1)
|
|
298
335
|
if x < 0.5:
|
|
299
|
-
return
|
|
336
|
+
return math.sin(math.pi * x) / 2
|
|
300
337
|
else:
|
|
301
338
|
return (1 - math.cos(math.pi * x)) / 2
|
|
302
339
|
|
|
303
340
|
|
|
304
341
|
def linstep(x: float) -> float:
|
|
342
|
+
"""Linear interpolation between 0 and 1."""
|
|
305
343
|
return clamp(x, 0.0, 1.0)
|
|
306
344
|
|
|
307
345
|
|
|
308
346
|
def smoothstep(x: float) -> float:
|
|
347
|
+
"""Interpolate between 0 and 1 using smoothstep."""
|
|
309
348
|
x = clamp(x, 0.0, 1.0)
|
|
310
349
|
return x * x * (3 - 2 * x)
|
|
311
350
|
|
|
312
351
|
|
|
313
352
|
def smootherstep(x: float) -> float:
|
|
353
|
+
"""Interpolate between 0 and 1 using smootherstep."""
|
|
314
354
|
x = clamp(x, 0.0, 1.0)
|
|
315
355
|
return x * x * x * (x * (x * 6 - 15) + 10)
|
|
316
356
|
|
|
317
357
|
|
|
318
358
|
def step_start(x: float) -> float:
|
|
319
|
-
|
|
359
|
+
"""Step function returning 1.0 if x > 0, otherwise 0.0."""
|
|
360
|
+
return 1.0 if x > 0 else 0.0
|
|
320
361
|
|
|
321
362
|
|
|
322
363
|
def step_end(x: float) -> float:
|
|
364
|
+
"""Step function returning 1.0 if x >= 1, otherwise 0.0."""
|
|
323
365
|
return 1.0 if x >= 1 else 0.0
|
sonolus/script/effect.py
CHANGED
|
@@ -10,35 +10,82 @@ from sonolus.script.record import Record
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class Effect(Record):
|
|
13
|
+
"""Sound effect clip.
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
```python
|
|
17
|
+
Effect(id: int)
|
|
18
|
+
```
|
|
19
|
+
"""
|
|
20
|
+
|
|
13
21
|
id: int
|
|
22
|
+
"""Effect ID."""
|
|
14
23
|
|
|
15
24
|
def is_available(self) -> bool:
|
|
25
|
+
"""Return whether the effect clip is available."""
|
|
16
26
|
return _has_effect_clip(self.id)
|
|
17
27
|
|
|
18
28
|
def play(self, distance: float) -> None:
|
|
29
|
+
"""Play the effect clip.
|
|
30
|
+
|
|
31
|
+
If the clip was already played within the specified distance, it will be skipped.
|
|
32
|
+
|
|
33
|
+
Arguments:
|
|
34
|
+
distance: Minimum time in seconds since the last play for the effect to play.
|
|
35
|
+
"""
|
|
19
36
|
_play(self.id, distance)
|
|
20
37
|
|
|
21
38
|
def schedule(self, time: float, distance: float) -> None:
|
|
39
|
+
"""Schedule the effect clip to play at a specific time.
|
|
40
|
+
|
|
41
|
+
This is not suitable for real-time effects such as responses to user input. Use `play` instead.
|
|
42
|
+
|
|
43
|
+
This may be called in preprocess to schedule effects upfront.
|
|
44
|
+
|
|
45
|
+
If the clip would play within the specified distance of another play, it will be skipped.
|
|
46
|
+
|
|
47
|
+
Arguments:
|
|
48
|
+
time: Time in seconds when the effect should play.
|
|
49
|
+
distance: Minimum time in seconds after a previous play for the effect to play.
|
|
50
|
+
"""
|
|
22
51
|
_play_scheduled(self.id, time, distance)
|
|
23
52
|
|
|
24
53
|
def loop(self) -> LoopedEffectHandle:
|
|
54
|
+
"""Play the effect clip in a loop until stopped.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A handle to stop the loop.
|
|
58
|
+
"""
|
|
25
59
|
return LoopedEffectHandle(_play_looped(self.id))
|
|
26
60
|
|
|
27
61
|
def schedule_loop(self, start_time: float) -> ScheduledLoopedEffectHandle:
|
|
62
|
+
"""Schedule the effect clip to play in a loop until stopped.
|
|
63
|
+
|
|
64
|
+
This is not suitable for real-time effects such as responses to user input. Use `loop` instead.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
A handle to stop the loop.
|
|
68
|
+
"""
|
|
28
69
|
return ScheduledLoopedEffectHandle(_play_looped_scheduled(self.id, start_time))
|
|
29
70
|
|
|
30
71
|
|
|
31
72
|
class LoopedEffectHandle(Record):
|
|
73
|
+
"""Handle to stop a looped effect."""
|
|
74
|
+
|
|
32
75
|
id: int
|
|
33
76
|
|
|
34
77
|
def stop(self) -> None:
|
|
78
|
+
"""Stop the looped effect."""
|
|
35
79
|
_stop_looped(self.id)
|
|
36
80
|
|
|
37
81
|
|
|
38
82
|
class ScheduledLoopedEffectHandle(Record):
|
|
83
|
+
"""Handle to stop a scheduled looped effect."""
|
|
84
|
+
|
|
39
85
|
id: int
|
|
40
86
|
|
|
41
87
|
def stop(self, end_time: float) -> None:
|
|
88
|
+
"""Stop the scheduled looped effect."""
|
|
42
89
|
_stop_looped_scheduled(self.id, end_time)
|
|
43
90
|
|
|
44
91
|
|
|
@@ -83,6 +130,7 @@ class EffectInfo:
|
|
|
83
130
|
|
|
84
131
|
|
|
85
132
|
def effect(name: str) -> Any:
|
|
133
|
+
"""Define a sound effect clip with the given name."""
|
|
86
134
|
return EffectInfo(name)
|
|
87
135
|
|
|
88
136
|
|
|
@@ -91,6 +139,16 @@ type Effects = NewType("Effects", Any)
|
|
|
91
139
|
|
|
92
140
|
@dataclass_transform()
|
|
93
141
|
def effects[T](cls: type[T]) -> T | Effects:
|
|
142
|
+
"""Decorator to define effect clips.
|
|
143
|
+
|
|
144
|
+
Usage:
|
|
145
|
+
```python
|
|
146
|
+
@effects
|
|
147
|
+
class Effects:
|
|
148
|
+
miss: StandardEffect.MISS
|
|
149
|
+
other: Effect = effect("other")
|
|
150
|
+
```
|
|
151
|
+
"""
|
|
94
152
|
if len(cls.__bases__) != 1:
|
|
95
153
|
raise ValueError("Effects class must not inherit from any class (except object)")
|
|
96
154
|
instance = cls()
|
|
@@ -113,6 +171,8 @@ def effects[T](cls: type[T]) -> T | Effects:
|
|
|
113
171
|
|
|
114
172
|
|
|
115
173
|
class StandardEffect:
|
|
174
|
+
"""Standard sound effect clips."""
|
|
175
|
+
|
|
116
176
|
MISS = Annotated[Effect, effect("#MISS")]
|
|
117
177
|
PERFECT = Annotated[Effect, effect("#PERFECT")]
|
|
118
178
|
GREAT = Annotated[Effect, effect("#GREAT")]
|
sonolus/script/engine.py
CHANGED
|
@@ -4,7 +4,7 @@ from collections.abc import Callable
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from sonolus.build.collection import Asset
|
|
7
|
-
from sonolus.script.archetype import
|
|
7
|
+
from sonolus.script.archetype import PlayArchetype, PreviewArchetype, WatchArchetype, _BaseArchetype
|
|
8
8
|
from sonolus.script.bucket import Buckets, EmptyBuckets
|
|
9
9
|
from sonolus.script.effect import Effects, EmptyEffects
|
|
10
10
|
from sonolus.script.instruction import (
|
|
@@ -20,6 +20,21 @@ from sonolus.script.ui import UiConfig
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class Engine:
|
|
23
|
+
"""A Sonolus.py engine.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
name: The name of the engine.
|
|
27
|
+
title: The title of the engine.
|
|
28
|
+
subtitle: The subtitle of the engine.
|
|
29
|
+
author: The author of the engine.
|
|
30
|
+
skin: The default skin for the engine.
|
|
31
|
+
background: The default background for the engine.
|
|
32
|
+
effect: The default effect for the engine.
|
|
33
|
+
particle: The default particle for the engine.
|
|
34
|
+
thumbnail: The thumbnail for the engine.
|
|
35
|
+
data: The engine's modes and configurations.
|
|
36
|
+
"""
|
|
37
|
+
|
|
23
38
|
version = 12
|
|
24
39
|
|
|
25
40
|
def __init__(
|
|
@@ -53,10 +68,20 @@ def default_callback() -> Any:
|
|
|
53
68
|
|
|
54
69
|
|
|
55
70
|
class PlayMode:
|
|
71
|
+
"""A play mode definition.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
archetypes: A list of play archetypes.
|
|
75
|
+
skin: The skin for the play mode.
|
|
76
|
+
effects: The effects for the play mode.
|
|
77
|
+
particles: The particles for the play mode.
|
|
78
|
+
buckets: The buckets for the play mode.
|
|
79
|
+
"""
|
|
80
|
+
|
|
56
81
|
def __init__(
|
|
57
82
|
self,
|
|
58
83
|
*,
|
|
59
|
-
archetypes: list[type[
|
|
84
|
+
archetypes: list[type[_BaseArchetype]] | None = None,
|
|
60
85
|
skin: Skin = EmptySkin,
|
|
61
86
|
effects: Effects = EmptyEffects,
|
|
62
87
|
particles: Particles = EmptyParticles,
|
|
@@ -74,10 +99,21 @@ class PlayMode:
|
|
|
74
99
|
|
|
75
100
|
|
|
76
101
|
class WatchMode:
|
|
102
|
+
"""A watch mode definition.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
archetypes: A list of watch archetypes.
|
|
106
|
+
skin: The skin for the watch mode.
|
|
107
|
+
effects: The effects for the watch mode.
|
|
108
|
+
particles: The particles for the watch mode.
|
|
109
|
+
buckets: The buckets for the watch mode.
|
|
110
|
+
update_spawn: A callback returning the spawn time used by archetypes.
|
|
111
|
+
"""
|
|
112
|
+
|
|
77
113
|
def __init__(
|
|
78
114
|
self,
|
|
79
115
|
*,
|
|
80
|
-
archetypes: list[type[
|
|
116
|
+
archetypes: list[type[_BaseArchetype]] | None = None,
|
|
81
117
|
skin: Skin = EmptySkin,
|
|
82
118
|
effects: Effects = EmptyEffects,
|
|
83
119
|
particles: Particles = EmptyParticles,
|
|
@@ -97,10 +133,17 @@ class WatchMode:
|
|
|
97
133
|
|
|
98
134
|
|
|
99
135
|
class PreviewMode:
|
|
136
|
+
"""A preview mode definition.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
archetypes: A list of preview archetypes.
|
|
140
|
+
skin: The skin for the preview mode.
|
|
141
|
+
"""
|
|
142
|
+
|
|
100
143
|
def __init__(
|
|
101
144
|
self,
|
|
102
145
|
*,
|
|
103
|
-
archetypes: list[type[
|
|
146
|
+
archetypes: list[type[_BaseArchetype]] | None = None,
|
|
104
147
|
skin: Skin = EmptySkin,
|
|
105
148
|
) -> None:
|
|
106
149
|
self.archetypes = archetypes or []
|
|
@@ -112,6 +155,19 @@ class PreviewMode:
|
|
|
112
155
|
|
|
113
156
|
|
|
114
157
|
class TutorialMode:
|
|
158
|
+
"""A tutorial mode definition.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
skin: The skin for the tutorial mode.
|
|
162
|
+
effects: The effects for the tutorial mode.
|
|
163
|
+
particles: The particles for the tutorial mode.
|
|
164
|
+
instructions: The instructions for the tutorial mode.
|
|
165
|
+
instruction_icons: The instruction icons for the tutorial mode.
|
|
166
|
+
preprocess: A callback to be called before the tutorial starts.
|
|
167
|
+
navigate: A callback to be called when the user navigates.
|
|
168
|
+
update: A callback to be called each frame.
|
|
169
|
+
"""
|
|
170
|
+
|
|
115
171
|
def __init__(
|
|
116
172
|
self,
|
|
117
173
|
*,
|
|
@@ -135,6 +191,17 @@ class TutorialMode:
|
|
|
135
191
|
|
|
136
192
|
|
|
137
193
|
class EngineData:
|
|
194
|
+
"""A Sonolus.py engine's modes and configurations.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
ui: The UI configuration.
|
|
198
|
+
options: The options for the engine.
|
|
199
|
+
play: The play mode configuration.
|
|
200
|
+
watch: The watch mode configuration.
|
|
201
|
+
preview: The preview mode configuration.
|
|
202
|
+
tutorial: The tutorial mode configuration.
|
|
203
|
+
"""
|
|
204
|
+
|
|
138
205
|
def __init__(
|
|
139
206
|
self,
|
|
140
207
|
*,
|