sonolus.py 0.8.0__py3-none-any.whl → 0.9.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.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/ir.py +32 -2
- sonolus/backend/optimize/copy_coalesce.py +5 -1
- sonolus/backend/optimize/flow.py +14 -0
- sonolus/backend/optimize/inlining.py +1 -1
- sonolus/backend/optimize/liveness.py +6 -4
- sonolus/build/cli.py +18 -50
- sonolus/build/compile.py +42 -4
- sonolus/build/dev_server.py +222 -0
- sonolus/build/engine.py +23 -2
- sonolus/build/project.py +13 -4
- sonolus/script/array.py +2 -1
- sonolus/script/bucket.py +11 -9
- sonolus/script/debug.py +21 -6
- sonolus/script/internal/impl.py +8 -4
- sonolus/script/interval.py +17 -6
- sonolus/script/num.py +8 -6
- sonolus/script/project.py +15 -2
- sonolus/script/quad.py +2 -0
- sonolus/script/record.py +9 -0
- sonolus/script/runtime.py +3 -2
- sonolus/script/sprite.py +8 -0
- sonolus/script/vec.py +31 -14
- {sonolus_py-0.8.0.dist-info → sonolus_py-0.9.0.dist-info}/METADATA +1 -1
- {sonolus_py-0.8.0.dist-info → sonolus_py-0.9.0.dist-info}/RECORD +27 -26
- {sonolus_py-0.8.0.dist-info → sonolus_py-0.9.0.dist-info}/WHEEL +0 -0
- {sonolus_py-0.8.0.dist-info → sonolus_py-0.9.0.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.8.0.dist-info → sonolus_py-0.9.0.dist-info}/licenses/LICENSE +0 -0
sonolus/build/engine.py
CHANGED
|
@@ -9,7 +9,7 @@ from dataclasses import dataclass
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
11
|
from sonolus.backend.mode import Mode
|
|
12
|
-
from sonolus.build.compile import compile_mode
|
|
12
|
+
from sonolus.build.compile import CompileCache, compile_mode
|
|
13
13
|
from sonolus.script.archetype import _BaseArchetype
|
|
14
14
|
from sonolus.script.bucket import Buckets
|
|
15
15
|
from sonolus.script.effect import Effects
|
|
@@ -68,7 +68,11 @@ def no_gil() -> bool:
|
|
|
68
68
|
def package_engine(
|
|
69
69
|
engine: EngineData,
|
|
70
70
|
config: BuildConfig | None = None,
|
|
71
|
+
cache: CompileCache | None = None,
|
|
71
72
|
):
|
|
73
|
+
if cache is None:
|
|
74
|
+
cache = CompileCache()
|
|
75
|
+
|
|
72
76
|
config = config or BuildConfig()
|
|
73
77
|
rom = ReadOnlyMemory()
|
|
74
78
|
configuration = build_engine_configuration(engine.options, engine.ui)
|
|
@@ -76,7 +80,8 @@ def package_engine(
|
|
|
76
80
|
# process_cpu_count is available in Python 3.13+
|
|
77
81
|
from os import process_cpu_count
|
|
78
82
|
|
|
79
|
-
|
|
83
|
+
# Need a worker for each mode (so +4) plus at least one to do work
|
|
84
|
+
thread_pool = ThreadPoolExecutor(4 + max(1, min(8, (process_cpu_count() or 1))))
|
|
80
85
|
else:
|
|
81
86
|
thread_pool = None
|
|
82
87
|
|
|
@@ -97,6 +102,7 @@ def package_engine(
|
|
|
97
102
|
rom=rom,
|
|
98
103
|
config=config,
|
|
99
104
|
thread_pool=thread_pool,
|
|
105
|
+
cache=cache,
|
|
100
106
|
),
|
|
101
107
|
"watch": thread_pool.submit(
|
|
102
108
|
build_watch_mode,
|
|
@@ -109,6 +115,7 @@ def package_engine(
|
|
|
109
115
|
update_spawn=watch_mode.update_spawn,
|
|
110
116
|
config=config,
|
|
111
117
|
thread_pool=thread_pool,
|
|
118
|
+
cache=cache,
|
|
112
119
|
),
|
|
113
120
|
"preview": thread_pool.submit(
|
|
114
121
|
build_preview_mode,
|
|
@@ -117,6 +124,7 @@ def package_engine(
|
|
|
117
124
|
rom=rom,
|
|
118
125
|
config=config,
|
|
119
126
|
thread_pool=thread_pool,
|
|
127
|
+
cache=cache,
|
|
120
128
|
),
|
|
121
129
|
"tutorial": thread_pool.submit(
|
|
122
130
|
build_tutorial_mode,
|
|
@@ -131,6 +139,7 @@ def package_engine(
|
|
|
131
139
|
rom=rom,
|
|
132
140
|
config=config,
|
|
133
141
|
thread_pool=thread_pool,
|
|
142
|
+
cache=cache,
|
|
134
143
|
),
|
|
135
144
|
}
|
|
136
145
|
|
|
@@ -148,6 +157,7 @@ def package_engine(
|
|
|
148
157
|
rom=rom,
|
|
149
158
|
config=config,
|
|
150
159
|
thread_pool=None,
|
|
160
|
+
cache=cache,
|
|
151
161
|
)
|
|
152
162
|
watch_data = build_watch_mode(
|
|
153
163
|
archetypes=watch_mode.archetypes,
|
|
@@ -159,6 +169,7 @@ def package_engine(
|
|
|
159
169
|
update_spawn=watch_mode.update_spawn,
|
|
160
170
|
config=config,
|
|
161
171
|
thread_pool=None,
|
|
172
|
+
cache=cache,
|
|
162
173
|
)
|
|
163
174
|
preview_data = build_preview_mode(
|
|
164
175
|
archetypes=preview_mode.archetypes,
|
|
@@ -166,6 +177,7 @@ def package_engine(
|
|
|
166
177
|
rom=rom,
|
|
167
178
|
config=config,
|
|
168
179
|
thread_pool=None,
|
|
180
|
+
cache=cache,
|
|
169
181
|
)
|
|
170
182
|
tutorial_data = build_tutorial_mode(
|
|
171
183
|
skin=tutorial_mode.skin,
|
|
@@ -179,6 +191,7 @@ def package_engine(
|
|
|
179
191
|
rom=rom,
|
|
180
192
|
config=config,
|
|
181
193
|
thread_pool=None,
|
|
194
|
+
cache=cache,
|
|
182
195
|
)
|
|
183
196
|
|
|
184
197
|
return PackagedEngine(
|
|
@@ -270,6 +283,7 @@ def build_play_mode(
|
|
|
270
283
|
config: BuildConfig,
|
|
271
284
|
thread_pool: Executor | None = None,
|
|
272
285
|
validate_only: bool = False,
|
|
286
|
+
cache: CompileCache | None = None,
|
|
273
287
|
):
|
|
274
288
|
return {
|
|
275
289
|
**compile_mode(
|
|
@@ -280,6 +294,7 @@ def build_play_mode(
|
|
|
280
294
|
passes=config.passes,
|
|
281
295
|
thread_pool=thread_pool,
|
|
282
296
|
validate_only=validate_only,
|
|
297
|
+
cache=cache,
|
|
283
298
|
),
|
|
284
299
|
"skin": build_skin(skin),
|
|
285
300
|
"effect": build_effects(effects),
|
|
@@ -299,6 +314,7 @@ def build_watch_mode(
|
|
|
299
314
|
config: BuildConfig,
|
|
300
315
|
thread_pool: Executor | None = None,
|
|
301
316
|
validate_only: bool = False,
|
|
317
|
+
cache: CompileCache | None = None,
|
|
302
318
|
):
|
|
303
319
|
return {
|
|
304
320
|
**compile_mode(
|
|
@@ -309,6 +325,7 @@ def build_watch_mode(
|
|
|
309
325
|
passes=config.passes,
|
|
310
326
|
thread_pool=thread_pool,
|
|
311
327
|
validate_only=validate_only,
|
|
328
|
+
cache=cache,
|
|
312
329
|
),
|
|
313
330
|
"skin": build_skin(skin),
|
|
314
331
|
"effect": build_effects(effects),
|
|
@@ -324,6 +341,7 @@ def build_preview_mode(
|
|
|
324
341
|
config: BuildConfig,
|
|
325
342
|
thread_pool: Executor | None = None,
|
|
326
343
|
validate_only: bool = False,
|
|
344
|
+
cache: CompileCache | None = None,
|
|
327
345
|
):
|
|
328
346
|
return {
|
|
329
347
|
**compile_mode(
|
|
@@ -334,6 +352,7 @@ def build_preview_mode(
|
|
|
334
352
|
passes=config.passes,
|
|
335
353
|
thread_pool=thread_pool,
|
|
336
354
|
validate_only=validate_only,
|
|
355
|
+
cache=cache,
|
|
337
356
|
),
|
|
338
357
|
"skin": build_skin(skin),
|
|
339
358
|
}
|
|
@@ -352,6 +371,7 @@ def build_tutorial_mode(
|
|
|
352
371
|
config: BuildConfig,
|
|
353
372
|
thread_pool: Executor | None = None,
|
|
354
373
|
validate_only: bool = False,
|
|
374
|
+
cache: CompileCache | None = None,
|
|
355
375
|
):
|
|
356
376
|
return {
|
|
357
377
|
**compile_mode(
|
|
@@ -366,6 +386,7 @@ def build_tutorial_mode(
|
|
|
366
386
|
passes=config.passes,
|
|
367
387
|
thread_pool=thread_pool,
|
|
368
388
|
validate_only=validate_only,
|
|
389
|
+
cache=cache,
|
|
369
390
|
),
|
|
370
391
|
"skin": build_skin(skin),
|
|
371
392
|
"effect": build_effects(effects),
|
sonolus/build/project.py
CHANGED
|
@@ -3,6 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import cast
|
|
4
4
|
|
|
5
5
|
from sonolus.build.collection import Asset, Collection, Srl
|
|
6
|
+
from sonolus.build.compile import CompileCache
|
|
6
7
|
from sonolus.build.engine import package_engine, unpackage_data
|
|
7
8
|
from sonolus.build.level import package_level_data
|
|
8
9
|
from sonolus.script.engine import Engine
|
|
@@ -19,7 +20,9 @@ BLANK_AUDIO = (
|
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def build_project_to_collection(
|
|
23
|
+
def build_project_to_collection(
|
|
24
|
+
project: Project, config: BuildConfig | None, cache: CompileCache | None = None
|
|
25
|
+
) -> Collection:
|
|
23
26
|
collection = load_resources_files_to_collection(project.resources)
|
|
24
27
|
for src_engine, converter in project.converters.items():
|
|
25
28
|
if src_engine is None:
|
|
@@ -31,7 +34,7 @@ def build_project_to_collection(project: Project, config: BuildConfig | None):
|
|
|
31
34
|
if config.override_resource_level_engines:
|
|
32
35
|
for level in collection.categories.get("levels", {}).values():
|
|
33
36
|
level["item"]["engine"] = project.engine.name
|
|
34
|
-
add_engine_to_collection(collection, project, project.engine, config)
|
|
37
|
+
add_engine_to_collection(collection, project, project.engine, config, cache=cache)
|
|
35
38
|
for level in project.levels:
|
|
36
39
|
add_level_to_collection(collection, project, level)
|
|
37
40
|
collection.name = f"{project.engine.name}"
|
|
@@ -63,8 +66,14 @@ def apply_converter_to_collection(
|
|
|
63
66
|
level["data"] = new_data_srl
|
|
64
67
|
|
|
65
68
|
|
|
66
|
-
def add_engine_to_collection(
|
|
67
|
-
|
|
69
|
+
def add_engine_to_collection(
|
|
70
|
+
collection: Collection,
|
|
71
|
+
project: Project,
|
|
72
|
+
engine: Engine,
|
|
73
|
+
config: BuildConfig | None,
|
|
74
|
+
cache: CompileCache | None = None,
|
|
75
|
+
):
|
|
76
|
+
packaged_engine = package_engine(engine.data, config, cache=cache)
|
|
68
77
|
item = {
|
|
69
78
|
"name": engine.name,
|
|
70
79
|
"version": engine.version,
|
sonolus/script/array.py
CHANGED
|
@@ -11,7 +11,7 @@ from sonolus.script.debug import assert_unreachable
|
|
|
11
11
|
from sonolus.script.internal.context import ctx
|
|
12
12
|
from sonolus.script.internal.error import InternalError
|
|
13
13
|
from sonolus.script.internal.generic import GenericValue
|
|
14
|
-
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
14
|
+
from sonolus.script.internal.impl import meta_fn, perf_meta_fn, validate_value
|
|
15
15
|
from sonolus.script.internal.value import BackingSource, DataValue, Value
|
|
16
16
|
from sonolus.script.num import Num
|
|
17
17
|
|
|
@@ -188,6 +188,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
188
188
|
else:
|
|
189
189
|
return cls._with_value([cls.element_type()._zero_() for _ in range(cls.size())])
|
|
190
190
|
|
|
191
|
+
@perf_meta_fn
|
|
191
192
|
def __len__(self):
|
|
192
193
|
return self.size()
|
|
193
194
|
|
sonolus/script/bucket.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
|
7
7
|
from sonolus.backend.mode import Mode
|
|
8
8
|
from sonolus.backend.ops import Op
|
|
9
9
|
from sonolus.script.internal.context import ctx
|
|
10
|
-
from sonolus.script.internal.impl import meta_fn
|
|
10
|
+
from sonolus.script.internal.impl import meta_fn, perf_meta_fn
|
|
11
11
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
12
12
|
from sonolus.script.internal.native import native_function
|
|
13
13
|
from sonolus.script.interval import Interval
|
|
@@ -72,20 +72,22 @@ class JudgmentWindow(Record):
|
|
|
72
72
|
*self.good.tuple,
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
+
@perf_meta_fn
|
|
75
76
|
def __mul__(self, other: float | int) -> JudgmentWindow:
|
|
76
77
|
"""Multiply the intervals by a scalar."""
|
|
77
|
-
return JudgmentWindow(
|
|
78
|
-
self.perfect * other,
|
|
79
|
-
self.great * other,
|
|
80
|
-
self.good * other,
|
|
78
|
+
return JudgmentWindow._quick_construct(
|
|
79
|
+
perfect=self.perfect * other,
|
|
80
|
+
great=self.great * other,
|
|
81
|
+
good=self.good * other,
|
|
81
82
|
)
|
|
82
83
|
|
|
84
|
+
@perf_meta_fn
|
|
83
85
|
def __add__(self, other: float | int) -> JudgmentWindow:
|
|
84
86
|
"""Add a scalar to the intervals."""
|
|
85
|
-
return JudgmentWindow(
|
|
86
|
-
self.perfect + other,
|
|
87
|
-
self.great + other,
|
|
88
|
-
self.good + other,
|
|
87
|
+
return JudgmentWindow._quick_construct(
|
|
88
|
+
perfect=self.perfect + other,
|
|
89
|
+
great=self.great + other,
|
|
90
|
+
good=self.good + other,
|
|
89
91
|
)
|
|
90
92
|
|
|
91
93
|
@property
|
sonolus/script/debug.py
CHANGED
|
@@ -68,16 +68,31 @@ def debug_pause():
|
|
|
68
68
|
input("[DEBUG] Paused")
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
@meta_fn
|
|
71
72
|
def assert_true(value: int | float | bool, message: str | None = None):
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
if not ctx():
|
|
74
|
+
if not value:
|
|
75
|
+
raise AssertionError(message if message is not None else "Assertion failed")
|
|
76
|
+
return
|
|
77
|
+
value = Num._accept_(validate_value(value))
|
|
78
|
+
message = validate_value(message)
|
|
79
|
+
message = message._as_py_() or "Assertion failed"
|
|
80
|
+
if value._is_py_():
|
|
81
|
+
if value._as_py_():
|
|
82
|
+
return
|
|
83
|
+
else:
|
|
84
|
+
error(message)
|
|
85
|
+
else:
|
|
86
|
+
ctx().test = value.ir()
|
|
87
|
+
t_branch = ctx().branch(None)
|
|
88
|
+
f_branch = ctx().branch(0)
|
|
89
|
+
set_ctx(f_branch)
|
|
90
|
+
error(message) # type: ignore
|
|
91
|
+
set_ctx(t_branch)
|
|
75
92
|
|
|
76
93
|
|
|
77
94
|
def assert_false(value: int | float | bool, message: str | None = None):
|
|
78
|
-
|
|
79
|
-
if value:
|
|
80
|
-
error(message)
|
|
95
|
+
assert_true(not value, message)
|
|
81
96
|
|
|
82
97
|
|
|
83
98
|
def static_assert(value: int | float | bool, message: str | None = None):
|
sonolus/script/internal/impl.py
CHANGED
|
@@ -33,6 +33,10 @@ def meta_fn(fn=None):
|
|
|
33
33
|
return decorator(fn)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
# To indicate this was used for performance reasons rather than functionality
|
|
37
|
+
perf_meta_fn = meta_fn
|
|
38
|
+
|
|
39
|
+
|
|
36
40
|
def validate_value[T](value: T) -> Value | T:
|
|
37
41
|
result = try_validate_value(value)
|
|
38
42
|
if result is None:
|
|
@@ -59,16 +63,16 @@ def try_validate_value(value: Any) -> Value | None:
|
|
|
59
63
|
pass
|
|
60
64
|
|
|
61
65
|
match value:
|
|
62
|
-
case Enum():
|
|
63
|
-
return validate_value(value.value)
|
|
64
66
|
case Value():
|
|
65
67
|
return value
|
|
68
|
+
case int() | float() | bool():
|
|
69
|
+
return Num._accept_(value)
|
|
70
|
+
case Enum():
|
|
71
|
+
return validate_value(value.value)
|
|
66
72
|
case type():
|
|
67
73
|
if value in {int, float, bool}:
|
|
68
74
|
return BasicConstantValue.of(Num)
|
|
69
75
|
return BasicConstantValue.of(value)
|
|
70
|
-
case int() | float() | bool():
|
|
71
|
-
return Num._accept_(value)
|
|
72
76
|
case tuple():
|
|
73
77
|
return TupleImpl._accept_(value)
|
|
74
78
|
case dict():
|
sonolus/script/interval.py
CHANGED
|
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
from sonolus.backend.ops import Op
|
|
4
4
|
from sonolus.script.array_like import ArrayLike
|
|
5
5
|
from sonolus.script.debug import static_error
|
|
6
|
+
from sonolus.script.internal.builtin_impls import _max, _min
|
|
7
|
+
from sonolus.script.internal.impl import perf_meta_fn
|
|
6
8
|
from sonolus.script.internal.native import native_function
|
|
7
9
|
from sonolus.script.internal.range import range_or_tuple
|
|
8
10
|
from sonolus.script.num import Num
|
|
@@ -66,6 +68,7 @@ class Interval(Record):
|
|
|
66
68
|
case _:
|
|
67
69
|
static_error("Invalid type for interval check")
|
|
68
70
|
|
|
71
|
+
@perf_meta_fn
|
|
69
72
|
def __add__(self, other: float | int) -> Interval:
|
|
70
73
|
"""Add a value to both ends of the interval.
|
|
71
74
|
|
|
@@ -75,8 +78,9 @@ class Interval(Record):
|
|
|
75
78
|
Returns:
|
|
76
79
|
A new interval with the value added to both ends.
|
|
77
80
|
"""
|
|
78
|
-
return Interval(self.start + other, self.end + other)
|
|
81
|
+
return Interval._quick_construct(start=self.start + other, end=self.end + other)
|
|
79
82
|
|
|
83
|
+
@perf_meta_fn
|
|
80
84
|
def __sub__(self, other: float | int) -> Interval:
|
|
81
85
|
"""Subtract a value from both ends of the interval.
|
|
82
86
|
|
|
@@ -86,8 +90,9 @@ class Interval(Record):
|
|
|
86
90
|
Returns:
|
|
87
91
|
A new interval with the value subtracted from both ends.
|
|
88
92
|
"""
|
|
89
|
-
return Interval(self.start - other, self.end - other)
|
|
93
|
+
return Interval._quick_construct(start=self.start - other, end=self.end - other)
|
|
90
94
|
|
|
95
|
+
@perf_meta_fn
|
|
91
96
|
def __mul__(self, other: float | int) -> Interval:
|
|
92
97
|
"""Multiply both ends of the interval by a value.
|
|
93
98
|
|
|
@@ -97,8 +102,9 @@ class Interval(Record):
|
|
|
97
102
|
Returns:
|
|
98
103
|
A new interval with both ends multiplied by the value.
|
|
99
104
|
"""
|
|
100
|
-
return Interval(self.start * other, self.end * other)
|
|
105
|
+
return Interval._quick_construct(start=self.start * other, end=self.end * other)
|
|
101
106
|
|
|
107
|
+
@perf_meta_fn
|
|
102
108
|
def __truediv__(self, other: float | int) -> Interval:
|
|
103
109
|
"""Divide both ends of the interval by a value.
|
|
104
110
|
|
|
@@ -108,8 +114,9 @@ class Interval(Record):
|
|
|
108
114
|
Returns:
|
|
109
115
|
A new interval with both ends divided by the value.
|
|
110
116
|
"""
|
|
111
|
-
return Interval(self.start / other, self.end / other)
|
|
117
|
+
return Interval._quick_construct(start=self.start / other, end=self.end / other)
|
|
112
118
|
|
|
119
|
+
@perf_meta_fn
|
|
113
120
|
def __floordiv__(self, other: float | int) -> Interval:
|
|
114
121
|
"""Divide both ends of the interval by a value and floor the result.
|
|
115
122
|
|
|
@@ -119,7 +126,7 @@ class Interval(Record):
|
|
|
119
126
|
Returns:
|
|
120
127
|
A new interval with both ends divided by the value and floored.
|
|
121
128
|
"""
|
|
122
|
-
return Interval(self.start // other, self.end // other)
|
|
129
|
+
return Interval._quick_construct(start=self.start // other, end=self.end // other)
|
|
123
130
|
|
|
124
131
|
def __and__(self, other: Interval) -> Interval:
|
|
125
132
|
"""Get the intersection of two intervals.
|
|
@@ -222,14 +229,17 @@ def _num_lerp_clamped(a, b, x, /):
|
|
|
222
229
|
return a + (b - a) * max(0, min(1, x))
|
|
223
230
|
|
|
224
231
|
|
|
232
|
+
@perf_meta_fn
|
|
225
233
|
def _generic_lerp[T](a: T, b: T, x: float, /) -> T:
|
|
226
234
|
return a + (b - a) * x # type: ignore
|
|
227
235
|
|
|
228
236
|
|
|
237
|
+
@perf_meta_fn
|
|
229
238
|
def _generic_lerp_clamped[T](a: T, b: T, x: float, /) -> T:
|
|
230
|
-
return a + (b - a) *
|
|
239
|
+
return a + (b - a) * _max(0, _min(1, x)) # type: ignore
|
|
231
240
|
|
|
232
241
|
|
|
242
|
+
@perf_meta_fn
|
|
233
243
|
def lerp[T](a: T, b: T, x: float, /) -> T:
|
|
234
244
|
"""Linearly interpolate between two values.
|
|
235
245
|
|
|
@@ -248,6 +258,7 @@ def lerp[T](a: T, b: T, x: float, /) -> T:
|
|
|
248
258
|
return _generic_lerp(a, b, x)
|
|
249
259
|
|
|
250
260
|
|
|
261
|
+
@perf_meta_fn
|
|
251
262
|
def lerp_clamped[T](a: T, b: T, x: float, /) -> T:
|
|
252
263
|
"""Linearly interpolate between two values, clamped to the interval.
|
|
253
264
|
|
sonolus/script/num.py
CHANGED
|
@@ -27,6 +27,8 @@ def _is_num(value: Any) -> TypeGuard[Num]:
|
|
|
27
27
|
|
|
28
28
|
@final
|
|
29
29
|
class _Num(Value, metaclass=_NumMeta):
|
|
30
|
+
__slots__ = ("data",)
|
|
31
|
+
|
|
30
32
|
# This works for ints, floats, and bools
|
|
31
33
|
# Since we don't support complex numbers, real is equal to the original number
|
|
32
34
|
__match_args__ = ("real",)
|
|
@@ -34,16 +36,10 @@ class _Num(Value, metaclass=_NumMeta):
|
|
|
34
36
|
data: DataValue
|
|
35
37
|
|
|
36
38
|
def __init__(self, data: DataValue | IRExpr):
|
|
37
|
-
if isinstance(data, complex):
|
|
38
|
-
raise TypeError("Cannot create a Num from a complex number")
|
|
39
39
|
if isinstance(data, int):
|
|
40
40
|
data = float(data)
|
|
41
41
|
if isinstance(data, IRConst | IRPureInstr | IRGet):
|
|
42
42
|
data = ExprBackingValue(data)
|
|
43
|
-
if _is_num(data):
|
|
44
|
-
raise InternalError("Cannot create a Num from a Num")
|
|
45
|
-
if not isinstance(data, BlockPlace | BackingValue | float | int | bool):
|
|
46
|
-
raise TypeError(f"Cannot create a Num from {type(data)}")
|
|
47
43
|
self.data = data
|
|
48
44
|
|
|
49
45
|
def __str__(self) -> str:
|
|
@@ -451,6 +447,12 @@ class _Num(Value, metaclass=_NumMeta):
|
|
|
451
447
|
return 0
|
|
452
448
|
|
|
453
449
|
|
|
450
|
+
def _create_num_raw(i: int) -> Num:
|
|
451
|
+
result = object.__new__(_Num)
|
|
452
|
+
result.data = float(i)
|
|
453
|
+
return result
|
|
454
|
+
|
|
455
|
+
|
|
454
456
|
if TYPE_CHECKING:
|
|
455
457
|
from typing import Protocol
|
|
456
458
|
|
sonolus/script/project.py
CHANGED
|
@@ -8,6 +8,7 @@ from typing import ClassVar, TypedDict
|
|
|
8
8
|
|
|
9
9
|
from sonolus.backend.optimize import optimize
|
|
10
10
|
from sonolus.backend.optimize.passes import CompilerPass
|
|
11
|
+
from sonolus.build.compile import CompileCache
|
|
11
12
|
from sonolus.script.archetype import ArchetypeSchema
|
|
12
13
|
from sonolus.script.engine import Engine
|
|
13
14
|
from sonolus.script.level import ExternalLevelData, Level, LevelData
|
|
@@ -65,8 +66,20 @@ class Project:
|
|
|
65
66
|
"""
|
|
66
67
|
from sonolus.build.cli import build_collection, run_server
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
if config is None:
|
|
70
|
+
config = BuildConfig()
|
|
71
|
+
|
|
72
|
+
cache = CompileCache()
|
|
73
|
+
build_collection(self, Path(build_dir), config, cache=cache)
|
|
74
|
+
run_server(
|
|
75
|
+
Path(build_dir) / "site",
|
|
76
|
+
port=port,
|
|
77
|
+
project_module_name=None,
|
|
78
|
+
core_module_names=None,
|
|
79
|
+
build_dir=Path(build_dir),
|
|
80
|
+
config=config,
|
|
81
|
+
cache=cache,
|
|
82
|
+
)
|
|
70
83
|
|
|
71
84
|
def build(self, build_dir: PathLike, config: BuildConfig | None = None):
|
|
72
85
|
"""Build the project.
|
sonolus/script/quad.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Protocol, Self, assert_never, overload
|
|
4
4
|
|
|
5
|
+
from sonolus.script.internal.impl import perf_meta_fn
|
|
5
6
|
from sonolus.script.record import Record
|
|
6
7
|
from sonolus.script.values import zeros
|
|
7
8
|
from sonolus.script.vec import Vec2, pnpoly
|
|
@@ -451,6 +452,7 @@ type QuadLike = _QuadLike | Quad
|
|
|
451
452
|
"""A type that can be used as a quad."""
|
|
452
453
|
|
|
453
454
|
|
|
455
|
+
@perf_meta_fn
|
|
454
456
|
def flatten_quad(quad: QuadLike) -> tuple[float, float, float, float, float, float, float, float]:
|
|
455
457
|
bl = quad.bl
|
|
456
458
|
tl = quad.tl
|
sonolus/script/record.py
CHANGED
|
@@ -171,6 +171,15 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
171
171
|
result._value_ = kwargs
|
|
172
172
|
return result
|
|
173
173
|
|
|
174
|
+
@classmethod
|
|
175
|
+
def _quick_construct(cls, **kwargs) -> Self:
|
|
176
|
+
result = object.__new__(cls)
|
|
177
|
+
for k, v in kwargs.items():
|
|
178
|
+
if isinstance(v, int | float):
|
|
179
|
+
kwargs[k] = Num._accept_(v)
|
|
180
|
+
result._value_ = kwargs
|
|
181
|
+
return result
|
|
182
|
+
|
|
174
183
|
@classmethod
|
|
175
184
|
def _size_(cls) -> int:
|
|
176
185
|
return sum(field.type._size_() for field in cls._fields_)
|
sonolus/script/runtime.py
CHANGED
|
@@ -30,7 +30,7 @@ from sonolus.script.globals import (
|
|
|
30
30
|
_watch_runtime_update,
|
|
31
31
|
)
|
|
32
32
|
from sonolus.script.internal.context import ctx
|
|
33
|
-
from sonolus.script.internal.impl import meta_fn
|
|
33
|
+
from sonolus.script.internal.impl import meta_fn, perf_meta_fn
|
|
34
34
|
from sonolus.script.num import Num
|
|
35
35
|
from sonolus.script.quad import Quad, Rect
|
|
36
36
|
from sonolus.script.record import Record
|
|
@@ -1154,9 +1154,10 @@ def canvas() -> _PreviewRuntimeCanvas:
|
|
|
1154
1154
|
return _PreviewRuntimeCanvas # type: ignore
|
|
1155
1155
|
|
|
1156
1156
|
|
|
1157
|
+
@perf_meta_fn
|
|
1157
1158
|
def screen() -> Rect:
|
|
1158
1159
|
"""Get the screen boundaries as a rectangle."""
|
|
1159
|
-
return Rect(t=1, r=aspect_ratio(), b=-1, l=-aspect_ratio())
|
|
1160
|
+
return Rect._quick_construct(t=1, r=aspect_ratio(), b=-1, l=-aspect_ratio())
|
|
1160
1161
|
|
|
1161
1162
|
|
|
1162
1163
|
def level_score() -> _LevelScore:
|
sonolus/script/sprite.py
CHANGED
|
@@ -6,6 +6,7 @@ from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
7
|
from sonolus.script.array_like import ArrayLike
|
|
8
8
|
from sonolus.script.debug import static_error
|
|
9
|
+
from sonolus.script.internal.impl import perf_meta_fn
|
|
9
10
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
10
11
|
from sonolus.script.internal.native import native_function
|
|
11
12
|
from sonolus.script.quad import QuadLike, flatten_quad
|
|
@@ -29,6 +30,7 @@ class Sprite(Record):
|
|
|
29
30
|
"""Check if the sprite is available."""
|
|
30
31
|
return _has_skin_sprite(self.id)
|
|
31
32
|
|
|
33
|
+
@perf_meta_fn
|
|
32
34
|
def draw(self, quad: QuadLike, z: float = 0.0, a: float = 1.0):
|
|
33
35
|
"""Draw the sprite.
|
|
34
36
|
|
|
@@ -39,6 +41,7 @@ class Sprite(Record):
|
|
|
39
41
|
"""
|
|
40
42
|
_draw(self.id, *flatten_quad(quad), z, a)
|
|
41
43
|
|
|
44
|
+
@perf_meta_fn
|
|
42
45
|
def draw_curved_b(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
|
|
43
46
|
"""Draw the sprite with a curved bottom with a quadratic Bézier curve.
|
|
44
47
|
|
|
@@ -51,6 +54,7 @@ class Sprite(Record):
|
|
|
51
54
|
"""
|
|
52
55
|
_draw_curved_b(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
|
|
53
56
|
|
|
57
|
+
@perf_meta_fn
|
|
54
58
|
def draw_curved_t(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
|
|
55
59
|
"""Draw the sprite with a curved top with a quadratic Bézier curve.
|
|
56
60
|
|
|
@@ -63,6 +67,7 @@ class Sprite(Record):
|
|
|
63
67
|
"""
|
|
64
68
|
_draw_curved_t(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
|
|
65
69
|
|
|
70
|
+
@perf_meta_fn
|
|
66
71
|
def draw_curved_l(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
|
|
67
72
|
"""Draw the sprite with a curved left side with a quadratic Bézier curve.
|
|
68
73
|
|
|
@@ -75,6 +80,7 @@ class Sprite(Record):
|
|
|
75
80
|
"""
|
|
76
81
|
_draw_curved_l(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
|
|
77
82
|
|
|
83
|
+
@perf_meta_fn
|
|
78
84
|
def draw_curved_r(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
|
|
79
85
|
"""Draw the sprite with a curved right side with a quadratic Bézier curve.
|
|
80
86
|
|
|
@@ -87,6 +93,7 @@ class Sprite(Record):
|
|
|
87
93
|
"""
|
|
88
94
|
_draw_curved_r(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
|
|
89
95
|
|
|
96
|
+
@perf_meta_fn
|
|
90
97
|
def draw_curved_bt(self, quad: QuadLike, cp1: Vec2, cp2: Vec2, n: float, z: float = 0.0, a: float = 1.0):
|
|
91
98
|
"""Draw the sprite with a curved bottom and top with a cubic Bézier curve.
|
|
92
99
|
|
|
@@ -100,6 +107,7 @@ class Sprite(Record):
|
|
|
100
107
|
"""
|
|
101
108
|
_draw_curved_bt(self.id, *flatten_quad(quad), z, a, n, *cp1.tuple, *cp2.tuple)
|
|
102
109
|
|
|
110
|
+
@perf_meta_fn
|
|
103
111
|
def draw_curved_lr(self, quad: QuadLike, cp1: Vec2, cp2: Vec2, n: float, z: float = 0.0, a: float = 1.0):
|
|
104
112
|
"""Draw the sprite with a curved left and right side with a cubic Bézier curve.
|
|
105
113
|
|