1- from __future__ import annotations
2-
31import contextlib
42import gc
53import importlib
108from collections .abc import Iterable
119from enum import Enum
1210from pathlib import Path
13- from typing import TYPE_CHECKING , Annotated , Any , Optional , Self , Union
11+ from typing import TYPE_CHECKING , Annotated , Any , Optional , Union
1412
1513import moderngl
1614import numpy as np
3634from shaderflow .variable import ShaderVariable , Uniform
3735
3836if TYPE_CHECKING :
39- from moderngl_window .context .base import BaseWindow as ModernglWindow
37+ from moderngl_window .context .glfw import Window as GlfwWindow
4038
4139# ---------------------------------------------------------------------------- #
4240
@@ -45,17 +43,15 @@ class WindowBackend(Enum):
4543 GLFW = "glfw"
4644
4745 @classmethod
48- def infer (cls ) -> Self :
46+ def infer (cls ) -> "WindowBackend" :
4947
5048 # Optional external user override
5149 if (option := os .getenv ("WINDOW_BACKEND" )):
52- if (value := cls .get (option )) is None :
53- raise ValueError (f"Invalid window backend '{ option } ', options are { cls .values ()} " )
54- return value
50+ return cls (option )
5551
5652 # Infer headless if exporting the scene via cli
5753 if ("main" in sys .argv ) and (args := sys .argv [sys .argv .index ("main" ):]):
58- if any (x in args for x in ("--render" , "-r" , "-- output" , "-o" )):
54+ if any (x in args for x in ("--output" , "-o" )):
5955 return cls .Headless
6056
6157 return cls .GLFW
@@ -68,34 +64,34 @@ class ShaderScene(ShaderModule):
6864 backend : WindowBackend = Factory (WindowBackend .infer )
6965 """ModernGL Window backend, cannot be changed after creation"""
7066
71- window : 'ModernglWindow ' = None
67+ window : 'GlfwWindow ' = None # type: ignore
7268 """ModernGL Window class instance at `moderngl_window.context.<backend>.Window`"""
7369
74- opengl : moderngl .Context = None
70+ opengl : moderngl .Context = None # type: ignore
7571 """ModernGL Context bound to this Scene"""
7672
77- quality : float = field (default = 50.0 , converter = lambda x : min ( max ( 0.0 , float ( x )), 100.0 ) )
73+ quality : float = field (default = 50.0 , converter = float )
7874 """Global quality level, if implemented on the shader/scene"""
7975
8076 # -------------------------------------------|
8177 # Modules
8278
83- ffmpeg : FFmpeg = Factory (FFmpeg )
84- """FFmpeg configuration for exporting videos"""
85-
8679 modules : list [ShaderModule ] = Factory (list )
8780 """List of all Modules in order of addition (including self)"""
8881
89- frametimer : ShaderFrametimer = None
82+ ffmpeg : FFmpeg = Factory (FFmpeg )
83+ """FFmpeg configuration for exporting videos"""
84+
85+ frametimer : ShaderFrametimer = None # type: ignore
9086 """Default Frametimer module"""
9187
92- keyboard : ShaderKeyboard = None
88+ keyboard : ShaderKeyboard = None # type: ignore
9389 """Default Keyboard module"""
9490
95- camera : ShaderCamera = None
91+ camera : ShaderCamera = None # type: ignore
9692 """Default Camera module"""
9793
98- shader : ShaderProgram = None
94+ shader : ShaderProgram = None # type: ignore
9995 """The main shader of the scene"""
10096
10197 def __del__ (self ):
@@ -110,7 +106,7 @@ def __del__(self):
110106 # -------------------------------------------|
111107 # Super Sampling Anti-Aliasing
112108
113- _final : ShaderProgram = None
109+ _final : ShaderProgram = None # type: ignore
114110 """Internal shader used for downsampling final frames"""
115111
116112 @property
@@ -138,7 +134,6 @@ def initialize(self) -> None:
138134 self .camera = ShaderCamera (scene = self )
139135
140136 # Linux: Use EGL for creating a OpenGL context, allows true headless with GPU acceleration
141- # Note: (https://forums.developer.nvidia.com/t/81412) (https://brokensrc.dev/get/docker/)
142137 if (sys .platform == "linux" ) and (os .getenv ("EGL" , "1" ) == "1" ):
143138 backend = "egl"
144139
@@ -265,10 +260,6 @@ def frame(self) -> int:
265260 def frame (self , value : int ):
266261 self .time = (value / self .fps )
267262
268- @property
269- def total_frames (self ) -> int :
270- return max (1 , round (self .runtime * self .fps ))
271-
272263 # Total Duration
273264
274265 @property
@@ -280,7 +271,7 @@ def max_duration(self) -> float:
280271 """The longest module duration"""
281272 return max ((module .duration or 0.0 ) for module in self .modules )
282273
283- def set_duration (self , override : float = None ) -> float :
274+ def set_duration (self , override : Optional [ float ] = None ) -> float :
284275 """Either force the duration, find the longest module or use base duration"""
285276 self .runtime = (override or self .max_duration )
286277 self .runtime /= self .speed
@@ -319,20 +310,6 @@ def _window_proxy(self, attribute, value) -> Any:
319310 # -------------------------------------------------------------------------|
320311 # Resolution
321312
322- # # Scale
323-
324- _scale : float = field (default = 1.0 , converter = lambda x : max (0.01 , x ))
325-
326- @property
327- def scale (self ) -> float :
328- """Resolution scale factor"""
329- return self ._scale
330-
331- @scale .setter
332- def scale (self , value : float ):
333- logger .debug (f"Changing Resolution Scale to ({ value } )" )
334- self .resize (scale = value )
335-
336313 # # Width
337314
338315 _width : int = field (default = 1920 )
@@ -343,7 +320,7 @@ def width(self) -> int:
343320
344321 @width .setter
345322 def width (self , value : int ):
346- self .resize (width = ( value * self . _scale ) )
323+ self .resize (width = value )
347324
348325 # # Height
349326
@@ -355,7 +332,7 @@ def height(self) -> int:
355332
356333 @height .setter
357334 def height (self , value : int ):
358- self .resize (height = ( value * self . _scale ) )
335+ self .resize (height = value )
359336
360337 # # SSAA
361338
@@ -396,15 +373,15 @@ def render_resolution(self) -> tuple[int, int]:
396373
397374 # # Aspect Ratio
398375
399- _aspect_ratio : float = None
376+ _aspect_ratio : Optional [ float ] = None
400377
401378 @property
402379 def aspect_ratio (self ) -> float :
403380 """Either the forced `self._aspect_ratio` or dynamic from `self.width/self.height`"""
404381 return self ._aspect_ratio or (self .width / self .height )
405382
406383 @aspect_ratio .setter
407- def aspect_ratio (self , value : Union [float , str ]):
384+ def aspect_ratio (self , value : Optional [ Union [float , str ] ]):
408385 logger .debug (f"Changing Aspect Ratio to { value } " )
409386
410387 # The aspect ratio can be sent as a fraction or "none", "false"
@@ -418,20 +395,23 @@ def aspect_ratio(self, value: Union[float, str]):
418395 self ._aspect_ratio = value
419396
420397 if (self .backend == WindowBackend .GLFW ) and (self ._aspect_ratio is not None ):
421- __import__ ("glfw" ).set_window_aspect_ratio (self .window ._window , 2 ** 16 , int (2 ** 16 / self ._aspect_ratio ))
398+ import glfw
399+ glfw .set_window_aspect_ratio (
400+ self .window ._window ,
401+ 2 ** 16 , int (2 ** 16 / self ._aspect_ratio )
402+ )
422403
423404 def resize (self ,
424- width : Optional [int | float ] = None ,
425- height : Optional [int | float ] = None ,
405+ width : Optional [int ] = None ,
406+ height : Optional [int ] = None ,
426407 ratio : Optional [float | str ] = None ,
427408 bounds : Optional [tuple [int , int ]] = None ,
428- scale : Optional [float ] = None ,
429409 ssaa : Optional [float ] = None ,
410+ scale : float = 1.0 ,
430411 ) -> tuple [int , int ]:
431412
432413 # Maybe update auxiliary properties
433414 self .aspect_ratio = (ratio or self ._aspect_ratio )
434- self ._scale = (scale or self ._scale )
435415 self ._ssaa = (ssaa or self ._ssaa )
436416
437417 # The parameters aren't trivial. The idea is to fit resolution from the scale-less components,
@@ -441,7 +421,7 @@ def resize(self,
441421 new = (width , height ),
442422 max = bounds ,
443423 ar = self ._aspect_ratio ,
444- scale = self . _scale ,
424+ scale = scale ,
445425 )
446426
447427 # Optimization: Only resize if target is different
@@ -516,9 +496,9 @@ def main(self, *,
516496 help = "Height of the rendering resolution (None to keep or find by --ar aspect ratio)" ,
517497 group = "🔴 Basic" , name = ("height" , "-h" ))] = 1080 ,
518498
519- scale : Annotated [Optional [ float ] , Parameter (
499+ scale : Annotated [float , Parameter (
520500 help = "Post-multiply width and height by a scale factor (None to keep)" ,
521- group = "🔴 Basic" , name = ("scale" , "-S " ))] = None ,
501+ group = "🔴 Basic" , name = ("scale" , "-x " ))] = 1.0 ,
522502
523503 ratio : Annotated [Optional [Union [float , str ]], Parameter (
524504 help = "Force resolution aspect ratio (Examples: '16:9', '16/9', '1.777') (None for dynamic)" ,
@@ -617,7 +597,7 @@ def main(self, *,
617597 if (self .exporting ):
618598 export .ffmpeg_clean ()
619599 export .ffmpeg_sizes (width = _width , height = _height )
620- export .ffmpeg_output (output )
600+ export .ffmpeg_output (output ) # type: ignore
621601 export .make_buffers (buffers )
622602 export .ffhook ()
623603 export .popen ()
0 commit comments