sonolus.py 0.3.1__py3-none-any.whl → 0.3.2__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/node.py +13 -5
- sonolus/backend/optimize/allocate.py +41 -4
- sonolus/backend/optimize/flow.py +24 -7
- sonolus/backend/optimize/optimize.py +2 -9
- sonolus/backend/utils.py +6 -1
- sonolus/backend/visitor.py +47 -14
- sonolus/build/cli.py +6 -1
- sonolus/build/engine.py +1 -1
- sonolus/script/archetype.py +27 -17
- sonolus/script/array.py +15 -5
- sonolus/script/array_like.py +5 -3
- sonolus/script/containers.py +3 -3
- sonolus/script/debug.py +66 -8
- sonolus/script/globals.py +17 -0
- sonolus/script/internal/builtin_impls.py +2 -3
- sonolus/script/internal/context.py +50 -0
- sonolus/script/internal/simulation_context.py +131 -0
- sonolus/script/internal/tuple_impl.py +15 -10
- sonolus/script/iterator.py +3 -2
- sonolus/script/num.py +11 -2
- sonolus/script/options.py +24 -1
- sonolus/script/quad.py +2 -0
- sonolus/script/record.py +22 -3
- sonolus/script/runtime.py +383 -0
- sonolus/script/stream.py +124 -16
- sonolus/script/transform.py +289 -0
- sonolus/script/values.py +9 -3
- {sonolus_py-0.3.1.dist-info → sonolus_py-0.3.2.dist-info}/METADATA +1 -1
- {sonolus_py-0.3.1.dist-info → sonolus_py-0.3.2.dist-info}/RECORD +32 -31
- {sonolus_py-0.3.1.dist-info → sonolus_py-0.3.2.dist-info}/WHEEL +0 -0
- {sonolus_py-0.3.1.dist-info → sonolus_py-0.3.2.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.3.1.dist-info → sonolus_py-0.3.2.dist-info}/licenses/LICENSE +0 -0
sonolus/script/runtime.py
CHANGED
|
@@ -259,6 +259,382 @@ class _TutorialRuntimeUi:
|
|
|
259
259
|
instruction: BasicRuntimeUiLayout
|
|
260
260
|
|
|
261
261
|
|
|
262
|
+
class UiLayout[T](Record):
|
|
263
|
+
"""The layout of a UI element."""
|
|
264
|
+
|
|
265
|
+
_underlying: T
|
|
266
|
+
|
|
267
|
+
def update(
|
|
268
|
+
self,
|
|
269
|
+
anchor: Vec2 | None = None,
|
|
270
|
+
pivot: Vec2 | None = None,
|
|
271
|
+
dimensions: Vec2 | None = None,
|
|
272
|
+
rotation: float | None = None,
|
|
273
|
+
alpha: float | None = None,
|
|
274
|
+
horizontal_align: HorizontalAlign | None = None,
|
|
275
|
+
background: bool | None = None,
|
|
276
|
+
):
|
|
277
|
+
"""Update the layout properties if it's available in the current mode and do nothing otherwise."""
|
|
278
|
+
match self._underlying:
|
|
279
|
+
case RuntimeUiLayout():
|
|
280
|
+
self._underlying.update(
|
|
281
|
+
anchor=anchor,
|
|
282
|
+
pivot=pivot,
|
|
283
|
+
dimensions=dimensions,
|
|
284
|
+
rotation=rotation,
|
|
285
|
+
alpha=alpha,
|
|
286
|
+
horizontal_align=horizontal_align,
|
|
287
|
+
background=background,
|
|
288
|
+
)
|
|
289
|
+
case BasicRuntimeUiLayout():
|
|
290
|
+
self._underlying.update(
|
|
291
|
+
anchor=anchor,
|
|
292
|
+
pivot=pivot,
|
|
293
|
+
dimensions=dimensions,
|
|
294
|
+
rotation=rotation,
|
|
295
|
+
alpha=alpha,
|
|
296
|
+
background=background,
|
|
297
|
+
)
|
|
298
|
+
case _:
|
|
299
|
+
pass # do nothing
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def is_available(self) -> bool:
|
|
303
|
+
"""Check if the layout is available in the current mode."""
|
|
304
|
+
return self._underlying is not None
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class UiConfig[T](Record):
|
|
308
|
+
"""The user configuration for a UI element."""
|
|
309
|
+
|
|
310
|
+
_underlying: T
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def scale(self) -> float:
|
|
314
|
+
"""The scale of the UI element."""
|
|
315
|
+
match self._underlying:
|
|
316
|
+
case RuntimeUiConfig():
|
|
317
|
+
return self._underlying.scale
|
|
318
|
+
case _:
|
|
319
|
+
return 1.0 # Default scale if not available
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def alpha(self) -> float:
|
|
323
|
+
"""The alpha (opacity) of the UI element."""
|
|
324
|
+
match self._underlying:
|
|
325
|
+
case RuntimeUiConfig():
|
|
326
|
+
return self._underlying.alpha
|
|
327
|
+
case _:
|
|
328
|
+
return 1.0 # Default alpha if not available
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def is_available(self) -> bool:
|
|
332
|
+
"""Check if the config is available in the current mode."""
|
|
333
|
+
return self._underlying is not None
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class RuntimeUi(Record):
|
|
337
|
+
"""Holds the layouts for different UI elements across all modes."""
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
@meta_fn
|
|
341
|
+
def menu(self) -> UiLayout:
|
|
342
|
+
"""The configuration for the menu UI element.
|
|
343
|
+
|
|
344
|
+
Available in play, watch, preview, and tutorial mode.
|
|
345
|
+
"""
|
|
346
|
+
match ctx().global_state.mode:
|
|
347
|
+
case Mode.PLAY:
|
|
348
|
+
return UiLayout(_PlayRuntimeUi.menu)
|
|
349
|
+
case Mode.WATCH:
|
|
350
|
+
return UiLayout(_WatchRuntimeUi.menu)
|
|
351
|
+
case Mode.PREVIEW:
|
|
352
|
+
return UiLayout(_PreviewRuntimeUi.menu)
|
|
353
|
+
case Mode.TUTORIAL:
|
|
354
|
+
return UiLayout(_TutorialRuntimeUi.menu)
|
|
355
|
+
case _:
|
|
356
|
+
raise RuntimeError("Unsupported mode for menu UI layout")
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
@meta_fn
|
|
360
|
+
def menu_config(self) -> UiConfig:
|
|
361
|
+
"""The configuration for the menu UI element.
|
|
362
|
+
|
|
363
|
+
Available in play, watch, preview, and tutorial mode.
|
|
364
|
+
"""
|
|
365
|
+
match ctx().global_state.mode:
|
|
366
|
+
case Mode.PLAY:
|
|
367
|
+
return UiConfig(_PlayRuntimeUiConfigs.menu)
|
|
368
|
+
case Mode.WATCH:
|
|
369
|
+
return UiConfig(_WatchRuntimeUiConfigs.menu)
|
|
370
|
+
case Mode.PREVIEW:
|
|
371
|
+
return UiConfig(_PreviewRuntimeUiConfigs.menu)
|
|
372
|
+
case Mode.TUTORIAL:
|
|
373
|
+
return UiConfig(_TutorialRuntimeUiConfigs.menu)
|
|
374
|
+
case _:
|
|
375
|
+
raise RuntimeError("Unsupported mode for menu UI configuration")
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
@meta_fn
|
|
379
|
+
def judgment(self) -> UiLayout:
|
|
380
|
+
"""The configuration for the judgment UI element.
|
|
381
|
+
|
|
382
|
+
Available in play and watch mode.
|
|
383
|
+
"""
|
|
384
|
+
match ctx().global_state.mode:
|
|
385
|
+
case Mode.PLAY:
|
|
386
|
+
return UiLayout(_PlayRuntimeUi.judgment)
|
|
387
|
+
case Mode.WATCH:
|
|
388
|
+
return UiLayout(_WatchRuntimeUi.judgment)
|
|
389
|
+
case _:
|
|
390
|
+
return UiLayout(None)
|
|
391
|
+
|
|
392
|
+
@property
|
|
393
|
+
@meta_fn
|
|
394
|
+
def judgment_config(self) -> UiConfig:
|
|
395
|
+
"""The configuration for the judgment UI element.
|
|
396
|
+
|
|
397
|
+
Available in play and watch mode.
|
|
398
|
+
"""
|
|
399
|
+
match ctx().global_state.mode:
|
|
400
|
+
case Mode.PLAY:
|
|
401
|
+
return UiConfig(_PlayRuntimeUiConfigs.judgment)
|
|
402
|
+
case Mode.WATCH:
|
|
403
|
+
return UiConfig(_WatchRuntimeUiConfigs.judgment)
|
|
404
|
+
case _:
|
|
405
|
+
return UiConfig(None)
|
|
406
|
+
|
|
407
|
+
@property
|
|
408
|
+
@meta_fn
|
|
409
|
+
def combo_value(self) -> UiLayout:
|
|
410
|
+
"""The configuration for the combo value UI element.
|
|
411
|
+
|
|
412
|
+
Available in play and watch mode.
|
|
413
|
+
"""
|
|
414
|
+
match ctx().global_state.mode:
|
|
415
|
+
case Mode.PLAY:
|
|
416
|
+
return UiLayout(_PlayRuntimeUi.combo_value)
|
|
417
|
+
case Mode.WATCH:
|
|
418
|
+
return UiLayout(_WatchRuntimeUi.combo_value)
|
|
419
|
+
case _:
|
|
420
|
+
return UiLayout(None)
|
|
421
|
+
|
|
422
|
+
@property
|
|
423
|
+
@meta_fn
|
|
424
|
+
def combo_text(self) -> UiLayout:
|
|
425
|
+
"""The configuration for the combo text UI element.
|
|
426
|
+
|
|
427
|
+
Available in play and watch mode.
|
|
428
|
+
"""
|
|
429
|
+
match ctx().global_state.mode:
|
|
430
|
+
case Mode.PLAY:
|
|
431
|
+
return UiLayout(_PlayRuntimeUi.combo_text)
|
|
432
|
+
case Mode.WATCH:
|
|
433
|
+
return UiLayout(_WatchRuntimeUi.combo_text)
|
|
434
|
+
case _:
|
|
435
|
+
return UiLayout(None)
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
@meta_fn
|
|
439
|
+
def combo_config(self) -> UiConfig:
|
|
440
|
+
"""The configuration for the combo UI element.
|
|
441
|
+
|
|
442
|
+
Available in play and watch mode.
|
|
443
|
+
"""
|
|
444
|
+
match ctx().global_state.mode:
|
|
445
|
+
case Mode.PLAY:
|
|
446
|
+
return UiConfig(_PlayRuntimeUiConfigs.combo)
|
|
447
|
+
case Mode.WATCH:
|
|
448
|
+
return UiConfig(_WatchRuntimeUiConfigs.combo)
|
|
449
|
+
case _:
|
|
450
|
+
return UiConfig(None)
|
|
451
|
+
|
|
452
|
+
@property
|
|
453
|
+
@meta_fn
|
|
454
|
+
def primary_metric_bar(self) -> UiLayout:
|
|
455
|
+
"""The configuration for the primary metric bar UI element.
|
|
456
|
+
|
|
457
|
+
Available in play and watch mode.
|
|
458
|
+
"""
|
|
459
|
+
match ctx().global_state.mode:
|
|
460
|
+
case Mode.PLAY:
|
|
461
|
+
return UiLayout(_PlayRuntimeUi.primary_metric_bar)
|
|
462
|
+
case Mode.WATCH:
|
|
463
|
+
return UiLayout(_WatchRuntimeUi.primary_metric_bar)
|
|
464
|
+
case _:
|
|
465
|
+
return UiLayout(None)
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
@meta_fn
|
|
469
|
+
def primary_metric_value(self) -> UiLayout:
|
|
470
|
+
"""The configuration for the primary metric value UI element.
|
|
471
|
+
|
|
472
|
+
Available in play and watch mode.
|
|
473
|
+
"""
|
|
474
|
+
match ctx().global_state.mode:
|
|
475
|
+
case Mode.PLAY:
|
|
476
|
+
return UiLayout(_PlayRuntimeUi.primary_metric_value)
|
|
477
|
+
case Mode.WATCH:
|
|
478
|
+
return UiLayout(_WatchRuntimeUi.primary_metric_value)
|
|
479
|
+
case _:
|
|
480
|
+
return UiLayout(None)
|
|
481
|
+
|
|
482
|
+
@property
|
|
483
|
+
@meta_fn
|
|
484
|
+
def primary_metric_config(self) -> UiConfig:
|
|
485
|
+
"""The configuration for the primary metric UI element.
|
|
486
|
+
|
|
487
|
+
Available in play and watch mode.
|
|
488
|
+
"""
|
|
489
|
+
match ctx().global_state.mode:
|
|
490
|
+
case Mode.PLAY:
|
|
491
|
+
return UiConfig(_PlayRuntimeUiConfigs.primary_metric)
|
|
492
|
+
case Mode.WATCH:
|
|
493
|
+
return UiConfig(_WatchRuntimeUiConfigs.primary_metric)
|
|
494
|
+
case _:
|
|
495
|
+
return UiConfig(None)
|
|
496
|
+
|
|
497
|
+
@property
|
|
498
|
+
@meta_fn
|
|
499
|
+
def secondary_metric_bar(self) -> UiLayout:
|
|
500
|
+
"""The configuration for the secondary metric bar UI element.
|
|
501
|
+
|
|
502
|
+
Available in play and watch mode.
|
|
503
|
+
"""
|
|
504
|
+
match ctx().global_state.mode:
|
|
505
|
+
case Mode.PLAY:
|
|
506
|
+
return UiLayout(_PlayRuntimeUi.secondary_metric_bar)
|
|
507
|
+
case Mode.WATCH:
|
|
508
|
+
return UiLayout(_WatchRuntimeUi.secondary_metric_bar)
|
|
509
|
+
case _:
|
|
510
|
+
return UiLayout(None)
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
@meta_fn
|
|
514
|
+
def secondary_metric_value(self) -> UiLayout:
|
|
515
|
+
"""The configuration for the secondary metric value UI element.
|
|
516
|
+
|
|
517
|
+
Available in play and watch mode.
|
|
518
|
+
"""
|
|
519
|
+
match ctx().global_state.mode:
|
|
520
|
+
case Mode.PLAY:
|
|
521
|
+
return UiLayout(_PlayRuntimeUi.secondary_metric_value)
|
|
522
|
+
case Mode.WATCH:
|
|
523
|
+
return UiLayout(_WatchRuntimeUi.secondary_metric_value)
|
|
524
|
+
case _:
|
|
525
|
+
return UiLayout(None)
|
|
526
|
+
|
|
527
|
+
@property
|
|
528
|
+
@meta_fn
|
|
529
|
+
def secondary_metric_config(self) -> UiConfig:
|
|
530
|
+
"""The configuration for the secondary metric UI element.
|
|
531
|
+
|
|
532
|
+
Available in play and watch mode.
|
|
533
|
+
"""
|
|
534
|
+
match ctx().global_state.mode:
|
|
535
|
+
case Mode.PLAY:
|
|
536
|
+
return UiConfig(_PlayRuntimeUiConfigs.secondary_metric)
|
|
537
|
+
case Mode.WATCH:
|
|
538
|
+
return UiConfig(_WatchRuntimeUiConfigs.secondary_metric)
|
|
539
|
+
case _:
|
|
540
|
+
return UiConfig(None)
|
|
541
|
+
|
|
542
|
+
@property
|
|
543
|
+
@meta_fn
|
|
544
|
+
def progress(self) -> UiLayout:
|
|
545
|
+
"""The configuration for the progress UI element.
|
|
546
|
+
|
|
547
|
+
Available in watch and preview mode.
|
|
548
|
+
"""
|
|
549
|
+
match ctx().global_state.mode:
|
|
550
|
+
case Mode.WATCH:
|
|
551
|
+
return UiLayout(_WatchRuntimeUi.progress)
|
|
552
|
+
case Mode.PREVIEW:
|
|
553
|
+
return UiLayout(_PreviewRuntimeUi.progress)
|
|
554
|
+
case _:
|
|
555
|
+
return UiLayout(None)
|
|
556
|
+
|
|
557
|
+
@property
|
|
558
|
+
@meta_fn
|
|
559
|
+
def progress_config(self) -> UiConfig:
|
|
560
|
+
"""The configuration for the progress UI element.
|
|
561
|
+
|
|
562
|
+
Available in watch and preview mode.
|
|
563
|
+
"""
|
|
564
|
+
match ctx().global_state.mode:
|
|
565
|
+
case Mode.WATCH:
|
|
566
|
+
return UiConfig(_WatchRuntimeUiConfigs.progress)
|
|
567
|
+
case Mode.PREVIEW:
|
|
568
|
+
return UiConfig(_PreviewRuntimeUiConfigs.progress)
|
|
569
|
+
case _:
|
|
570
|
+
return UiConfig(None)
|
|
571
|
+
|
|
572
|
+
@property
|
|
573
|
+
@meta_fn
|
|
574
|
+
def previous(self) -> UiLayout:
|
|
575
|
+
"""The configuration for the previous navigation UI element.
|
|
576
|
+
|
|
577
|
+
Available in tutorial mode.
|
|
578
|
+
"""
|
|
579
|
+
match ctx().global_state.mode:
|
|
580
|
+
case Mode.TUTORIAL:
|
|
581
|
+
return UiLayout(_TutorialRuntimeUi.previous)
|
|
582
|
+
case _:
|
|
583
|
+
return UiLayout(None)
|
|
584
|
+
|
|
585
|
+
@property
|
|
586
|
+
@meta_fn
|
|
587
|
+
def next(self) -> UiLayout:
|
|
588
|
+
"""The configuration for the next navigation UI element.
|
|
589
|
+
|
|
590
|
+
Available in tutorial mode.
|
|
591
|
+
"""
|
|
592
|
+
match ctx().global_state.mode:
|
|
593
|
+
case Mode.TUTORIAL:
|
|
594
|
+
return UiLayout(_TutorialRuntimeUi.next)
|
|
595
|
+
case _:
|
|
596
|
+
return UiLayout(None)
|
|
597
|
+
|
|
598
|
+
@property
|
|
599
|
+
@meta_fn
|
|
600
|
+
def navigation_config(self) -> UiConfig:
|
|
601
|
+
"""The configuration for the navigation UI element.
|
|
602
|
+
|
|
603
|
+
Available in tutorial mode.
|
|
604
|
+
"""
|
|
605
|
+
match ctx().global_state.mode:
|
|
606
|
+
case Mode.TUTORIAL:
|
|
607
|
+
return UiConfig(_TutorialRuntimeUiConfigs.navigation)
|
|
608
|
+
case _:
|
|
609
|
+
return UiConfig(None)
|
|
610
|
+
|
|
611
|
+
@property
|
|
612
|
+
@meta_fn
|
|
613
|
+
def instruction(self) -> UiLayout:
|
|
614
|
+
"""The configuration for the instruction UI element.
|
|
615
|
+
|
|
616
|
+
Available in tutorial mode.
|
|
617
|
+
"""
|
|
618
|
+
match ctx().global_state.mode:
|
|
619
|
+
case Mode.TUTORIAL:
|
|
620
|
+
return UiLayout(_TutorialRuntimeUi.instruction)
|
|
621
|
+
case _:
|
|
622
|
+
return UiLayout(None)
|
|
623
|
+
|
|
624
|
+
@property
|
|
625
|
+
@meta_fn
|
|
626
|
+
def instruction_config(self) -> UiConfig:
|
|
627
|
+
"""The configuration for the instruction UI element.
|
|
628
|
+
|
|
629
|
+
Available in tutorial mode.
|
|
630
|
+
"""
|
|
631
|
+
match ctx().global_state.mode:
|
|
632
|
+
case Mode.TUTORIAL:
|
|
633
|
+
return UiConfig(_TutorialRuntimeUiConfigs.instruction)
|
|
634
|
+
case _:
|
|
635
|
+
return UiConfig(None)
|
|
636
|
+
|
|
637
|
+
|
|
262
638
|
class Touch(Record):
|
|
263
639
|
"""Data of a touch event."""
|
|
264
640
|
|
|
@@ -733,6 +1109,13 @@ preview_ui_configs = _PreviewRuntimeUiConfigs
|
|
|
733
1109
|
tutorial_ui = _TutorialRuntimeUi
|
|
734
1110
|
tutorial_ui_configs = _TutorialRuntimeUiConfigs
|
|
735
1111
|
|
|
1112
|
+
_runtime_ui = RuntimeUi()
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
def runtime_ui() -> RuntimeUi:
|
|
1116
|
+
"""Get the runtime UI configuration."""
|
|
1117
|
+
return _runtime_ui
|
|
1118
|
+
|
|
736
1119
|
|
|
737
1120
|
def canvas() -> _PreviewRuntimeCanvas:
|
|
738
1121
|
"""Get the preview canvas."""
|
sonolus/script/stream.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import cast, dataclass_transform
|
|
4
4
|
|
|
5
|
-
from sonolus.backend.ir import IRExpr, IRInstr, IRPureInstr
|
|
5
|
+
from sonolus.backend.ir import IRConst, IRExpr, IRInstr, IRPureInstr
|
|
6
6
|
from sonolus.backend.mode import Mode
|
|
7
7
|
from sonolus.backend.ops import Op
|
|
8
8
|
from sonolus.script.internal.context import ctx
|
|
@@ -14,6 +14,7 @@ from sonolus.script.internal.value import BackingValue, Value
|
|
|
14
14
|
from sonolus.script.iterator import SonolusIterator
|
|
15
15
|
from sonolus.script.num import Num
|
|
16
16
|
from sonolus.script.record import Record
|
|
17
|
+
from sonolus.script.runtime import delta_time, time
|
|
17
18
|
from sonolus.script.values import sizeof
|
|
18
19
|
|
|
19
20
|
|
|
@@ -174,10 +175,11 @@ class _SparseStreamBacking(BackingValue):
|
|
|
174
175
|
)
|
|
175
176
|
|
|
176
177
|
|
|
177
|
-
class Stream[T](Record
|
|
178
|
+
class Stream[T](Record):
|
|
178
179
|
"""Represents a stream.
|
|
179
180
|
|
|
180
|
-
Most users should use `@stream
|
|
181
|
+
Most users should use [`@streams`][sonolus.script.stream.streams] to declare streams and stream groups rather than
|
|
182
|
+
using this class directly.
|
|
181
183
|
|
|
182
184
|
If used directly, it is important that streams do not overlap. No other streams should have an offset in
|
|
183
185
|
`range(self.offset, self.offset + max(1, sizeof(self.element_type())))`, or they will overlap and interfere
|
|
@@ -248,6 +250,12 @@ class Stream[T](Record, BackingValue):
|
|
|
248
250
|
_check_can_read_stream()
|
|
249
251
|
return _stream_get_next_key(self.offset, key)
|
|
250
252
|
|
|
253
|
+
def next_key_or_default(self, key: int | float, default: int | float) -> int:
|
|
254
|
+
"""Get the next key, or the default value if there is no next key."""
|
|
255
|
+
_check_can_read_stream()
|
|
256
|
+
next_key = self.next_key(key)
|
|
257
|
+
return next_key if next_key > key else default
|
|
258
|
+
|
|
251
259
|
def previous_key(self, key: int | float) -> int:
|
|
252
260
|
"""Get the previous key, or the key unchanged if it is the first key or the stream is empty.
|
|
253
261
|
|
|
@@ -256,6 +264,12 @@ class Stream[T](Record, BackingValue):
|
|
|
256
264
|
_check_can_read_stream()
|
|
257
265
|
return _stream_get_previous_key(self.offset, key)
|
|
258
266
|
|
|
267
|
+
def previous_key_or_default(self, key: int | float, default: int | float) -> int:
|
|
268
|
+
"""Get the previous key, or the default value if there is no previous key."""
|
|
269
|
+
_check_can_read_stream()
|
|
270
|
+
previous_key = self.previous_key(key)
|
|
271
|
+
return previous_key if previous_key < key else default
|
|
272
|
+
|
|
259
273
|
def has_next_key(self, key: int | float) -> bool:
|
|
260
274
|
"""Check if there is a next key after the given key in the stream."""
|
|
261
275
|
_check_can_read_stream()
|
|
@@ -317,6 +331,22 @@ class Stream[T](Record, BackingValue):
|
|
|
317
331
|
_check_can_read_stream()
|
|
318
332
|
return _StreamAscIterator(self, self.next_key_inclusive(start))
|
|
319
333
|
|
|
334
|
+
def iter_items_since_previous_frame(self) -> SonolusIterator[tuple[int | float, T]]:
|
|
335
|
+
"""Iterate over the items in the stream since the last frame.
|
|
336
|
+
|
|
337
|
+
This is a convenience method that iterates over the items in the stream occurring after the time of the
|
|
338
|
+
previous frame and up to and including the current time.
|
|
339
|
+
|
|
340
|
+
Usage:
|
|
341
|
+
```python
|
|
342
|
+
stream = ...
|
|
343
|
+
for key, value in stream.iter_items_since_previous_frame():
|
|
344
|
+
do_something(key, value)
|
|
345
|
+
```
|
|
346
|
+
"""
|
|
347
|
+
_check_can_read_stream()
|
|
348
|
+
return _StreamBoundedAscIterator(self, self.next_key(time() - delta_time()), time())
|
|
349
|
+
|
|
320
350
|
def iter_items_from_desc(self, start: int | float, /) -> SonolusIterator[tuple[int | float, T]]:
|
|
321
351
|
"""Iterate over the items in the stream in descending order starting from the given key.
|
|
322
352
|
|
|
@@ -347,6 +377,22 @@ class Stream[T](Record, BackingValue):
|
|
|
347
377
|
_check_can_read_stream()
|
|
348
378
|
return _StreamAscKeyIterator(self, self.next_key_inclusive(start))
|
|
349
379
|
|
|
380
|
+
def iter_keys_since_previous_frame(self) -> SonolusIterator[int | float]:
|
|
381
|
+
"""Iterate over the keys in the stream since the last frame.
|
|
382
|
+
|
|
383
|
+
This is a convenience method that iterates over the keys in the stream occurring after the time of the
|
|
384
|
+
previous frame and up to and including the current time.
|
|
385
|
+
|
|
386
|
+
Usage:
|
|
387
|
+
```python
|
|
388
|
+
stream = ...
|
|
389
|
+
for key in stream.iter_keys_since_previous_frame():
|
|
390
|
+
do_something(key)
|
|
391
|
+
```
|
|
392
|
+
"""
|
|
393
|
+
_check_can_read_stream()
|
|
394
|
+
return _StreamBoundedAscKeyIterator(self, self.next_key(time() - delta_time()), time())
|
|
395
|
+
|
|
350
396
|
def iter_keys_from_desc(self, start: int | float, /) -> SonolusIterator[int | float]:
|
|
351
397
|
"""Iterate over the keys in the stream in descending order starting from the given key.
|
|
352
398
|
|
|
@@ -377,6 +423,22 @@ class Stream[T](Record, BackingValue):
|
|
|
377
423
|
_check_can_read_stream()
|
|
378
424
|
return _StreamAscValueIterator(self, self.next_key_inclusive(start))
|
|
379
425
|
|
|
426
|
+
def iter_values_since_previous_frame(self) -> SonolusIterator[T]:
|
|
427
|
+
"""Iterate over the values in the stream since the last frame.
|
|
428
|
+
|
|
429
|
+
This is a convenience method that iterates over the values in the stream occurring after the time of the
|
|
430
|
+
previous frame and up to and including the current time.
|
|
431
|
+
|
|
432
|
+
Usage:
|
|
433
|
+
```python
|
|
434
|
+
stream = ...
|
|
435
|
+
for value in stream.iter_values_since_previous_frame():
|
|
436
|
+
do_something(value)
|
|
437
|
+
```
|
|
438
|
+
"""
|
|
439
|
+
_check_can_read_stream()
|
|
440
|
+
return _StreamBoundedAscValueIterator(self, self.next_key(time() - delta_time()), time())
|
|
441
|
+
|
|
380
442
|
def iter_values_from_desc(self, start: int | float, /) -> SonolusIterator[T]:
|
|
381
443
|
"""Iterate over the values in the stream in descending order starting from the given key.
|
|
382
444
|
|
|
@@ -396,7 +458,8 @@ class Stream[T](Record, BackingValue):
|
|
|
396
458
|
class StreamGroup[T, Size](Record):
|
|
397
459
|
"""Represents a group of streams.
|
|
398
460
|
|
|
399
|
-
Most users should use `@stream
|
|
461
|
+
Most users should use [`@streams`][sonolus.script.stream.streams] to declare stream groups rather than using this
|
|
462
|
+
class directly.
|
|
400
463
|
|
|
401
464
|
Usage:
|
|
402
465
|
Declaring a stream group:
|
|
@@ -444,13 +507,28 @@ class _StreamAscIterator[T](Record, SonolusIterator[tuple[int | float, T]]):
|
|
|
444
507
|
current_key: int | float
|
|
445
508
|
|
|
446
509
|
def has_next(self) -> bool:
|
|
447
|
-
return self.
|
|
510
|
+
return self.current_key in self.stream
|
|
448
511
|
|
|
449
512
|
def get(self) -> tuple[int | float, T]:
|
|
450
513
|
return self.current_key, self.stream[self.current_key]
|
|
451
514
|
|
|
452
515
|
def advance(self):
|
|
453
|
-
self.current_key = self.stream.
|
|
516
|
+
self.current_key = self.stream.next_key_or_default(self.current_key, self.current_key + 1)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
class _StreamBoundedAscIterator[T](Record, SonolusIterator[tuple[int | float, T]]):
|
|
520
|
+
stream: Stream[T]
|
|
521
|
+
current_key: int | float
|
|
522
|
+
end_key: int | float
|
|
523
|
+
|
|
524
|
+
def has_next(self) -> bool:
|
|
525
|
+
return self.current_key in self.stream and self.current_key <= self.end_key
|
|
526
|
+
|
|
527
|
+
def get(self) -> tuple[int | float, T]:
|
|
528
|
+
return self.current_key, self.stream[self.current_key]
|
|
529
|
+
|
|
530
|
+
def advance(self):
|
|
531
|
+
self.current_key = self.stream.next_key_or_default(self.current_key, self.current_key + 1)
|
|
454
532
|
|
|
455
533
|
|
|
456
534
|
class _StreamDescIterator[T](Record, SonolusIterator[tuple[int | float, T]]):
|
|
@@ -458,13 +536,13 @@ class _StreamDescIterator[T](Record, SonolusIterator[tuple[int | float, T]]):
|
|
|
458
536
|
current_key: int | float
|
|
459
537
|
|
|
460
538
|
def has_next(self) -> bool:
|
|
461
|
-
return self.
|
|
539
|
+
return self.current_key in self.stream
|
|
462
540
|
|
|
463
541
|
def get(self) -> tuple[int | float, T]:
|
|
464
542
|
return self.current_key, self.stream[self.current_key]
|
|
465
543
|
|
|
466
544
|
def advance(self):
|
|
467
|
-
self.current_key = self.stream.
|
|
545
|
+
self.current_key = self.stream.previous_key_or_default(self.current_key, self.current_key - 1)
|
|
468
546
|
|
|
469
547
|
|
|
470
548
|
class _StreamAscKeyIterator[T](Record, SonolusIterator[int | float]):
|
|
@@ -472,13 +550,28 @@ class _StreamAscKeyIterator[T](Record, SonolusIterator[int | float]):
|
|
|
472
550
|
current_key: int | float
|
|
473
551
|
|
|
474
552
|
def has_next(self) -> bool:
|
|
475
|
-
return self.
|
|
553
|
+
return self.current_key in self.stream
|
|
554
|
+
|
|
555
|
+
def get(self) -> int | float:
|
|
556
|
+
return self.current_key
|
|
557
|
+
|
|
558
|
+
def advance(self):
|
|
559
|
+
self.current_key = self.stream.next_key_or_default(self.current_key, self.current_key + 1)
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
class _StreamBoundedAscKeyIterator[T](Record, SonolusIterator[int | float]):
|
|
563
|
+
stream: Stream[T]
|
|
564
|
+
current_key: int | float
|
|
565
|
+
end_key: int | float
|
|
566
|
+
|
|
567
|
+
def has_next(self) -> bool:
|
|
568
|
+
return self.current_key in self.stream and self.current_key <= self.end_key
|
|
476
569
|
|
|
477
570
|
def get(self) -> int | float:
|
|
478
571
|
return self.current_key
|
|
479
572
|
|
|
480
573
|
def advance(self):
|
|
481
|
-
self.current_key = self.stream.
|
|
574
|
+
self.current_key = self.stream.next_key_or_default(self.current_key, self.current_key + 1)
|
|
482
575
|
|
|
483
576
|
|
|
484
577
|
class _StreamDescKeyIterator[T](Record, SonolusIterator[int | float]):
|
|
@@ -486,13 +579,13 @@ class _StreamDescKeyIterator[T](Record, SonolusIterator[int | float]):
|
|
|
486
579
|
current_key: int | float
|
|
487
580
|
|
|
488
581
|
def has_next(self) -> bool:
|
|
489
|
-
return self.
|
|
582
|
+
return self.current_key in self.stream
|
|
490
583
|
|
|
491
584
|
def get(self) -> int | float:
|
|
492
585
|
return self.current_key
|
|
493
586
|
|
|
494
587
|
def advance(self):
|
|
495
|
-
self.current_key = self.stream.
|
|
588
|
+
self.current_key = self.stream.previous_key_or_default(self.current_key, self.current_key - 1)
|
|
496
589
|
|
|
497
590
|
|
|
498
591
|
class _StreamAscValueIterator[T](Record, SonolusIterator[T]):
|
|
@@ -500,13 +593,28 @@ class _StreamAscValueIterator[T](Record, SonolusIterator[T]):
|
|
|
500
593
|
current_key: int | float
|
|
501
594
|
|
|
502
595
|
def has_next(self) -> bool:
|
|
503
|
-
return self.
|
|
596
|
+
return self.current_key in self.stream
|
|
597
|
+
|
|
598
|
+
def get(self) -> T:
|
|
599
|
+
return self.stream[self.current_key]
|
|
600
|
+
|
|
601
|
+
def advance(self):
|
|
602
|
+
self.current_key = self.stream.next_key_or_default(self.current_key, self.current_key + 1)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
class _StreamBoundedAscValueIterator[T](Record, SonolusIterator[T]):
|
|
606
|
+
stream: Stream[T]
|
|
607
|
+
current_key: int | float
|
|
608
|
+
end_key: int | float
|
|
609
|
+
|
|
610
|
+
def has_next(self) -> bool:
|
|
611
|
+
return self.current_key in self.stream and self.current_key <= self.end_key
|
|
504
612
|
|
|
505
613
|
def get(self) -> T:
|
|
506
614
|
return self.stream[self.current_key]
|
|
507
615
|
|
|
508
616
|
def advance(self):
|
|
509
|
-
self.current_key = self.stream.
|
|
617
|
+
self.current_key = self.stream.next_key_or_default(self.current_key, self.current_key + 1)
|
|
510
618
|
|
|
511
619
|
|
|
512
620
|
class _StreamDescValueIterator[T](Record, SonolusIterator[T]):
|
|
@@ -514,13 +622,13 @@ class _StreamDescValueIterator[T](Record, SonolusIterator[T]):
|
|
|
514
622
|
current_key: int | float
|
|
515
623
|
|
|
516
624
|
def has_next(self) -> bool:
|
|
517
|
-
return self.
|
|
625
|
+
return self.current_key in self.stream
|
|
518
626
|
|
|
519
627
|
def get(self) -> T:
|
|
520
628
|
return self.stream[self.current_key]
|
|
521
629
|
|
|
522
630
|
def advance(self):
|
|
523
|
-
self.current_key = self.stream.
|
|
631
|
+
self.current_key = self.stream.previous_key_or_default(self.current_key, self.current_key - 1)
|
|
524
632
|
|
|
525
633
|
|
|
526
634
|
@native_function(Op.StreamGetNextKey)
|