diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 852c1c1a..5b628390 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -17,12 +17,9 @@ jobs: run: | python -m pip install --upgrade pip pip install .[dev] - - name: Lint with flake8 + - name: Lint with ruff run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + ruff check - name: Test with pytest run: | # TODO: Figure out if we can run headless tests diff --git a/README.md b/README.md index 84a239be..46b67a60 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ you provide us your `moderngl.Context`. ## Install ```bash -$ pip install moderngl-window +pip install moderngl-window ``` ## Supported Platforms @@ -67,13 +67,13 @@ Test.run() Run the example with different window backends: ```bash -$ python test.py --window pyglet -$ python test.py --window pygame2 -$ python test.py --window glfw -$ python test.py --window sdl2 -$ python test.py --window pyside2 -$ python test.py --window pyqt5 -$ python test.py --window tk +python test.py --window pyglet +python test.py --window pygame2 +python test.py --window glfw +python test.py --window sdl2 +python test.py --window pyside2 +python test.py --window pyqt5 +python test.py --window tk ``` `WindowConfig` classes are the simplest way to get started without knowing @@ -103,8 +103,8 @@ pytest ## Building Docs ```bash -$ pip install -e .[dev] -$ sphinx-build -b html docs docs/_build +pip install -e .[dev] +sphinx-build -b html docs docs/_build ``` ## Contributing @@ -134,6 +134,7 @@ your package provides. ## Citation If you need to cite this repository in academic research: + ```txt @Online{Forselv2020, author = {Einar Forselv}, @@ -147,8 +148,7 @@ If you need to cite this repository in academic research: ``` If commit hash is required this can be found per release here: -https://github.com/moderngl/moderngl-window/releases - + ## Attributions @@ -157,33 +157,32 @@ and resources to make this project possible. ### Windows -* pyglet (https://github.com/pyglet/pyglet) -* pygame (https://github.com/pygame/pygame) -* pyGLFW (https://github.com/FlorianRhiem/pyGLFW) -* PySDL2 (https://github.com/marcusva/py-sdl2) -* PySide2 (https://wiki.qt.io/Qt_for_Python) -* PyQt5 (https://www.riverbankcomputing.com/software/pyqt/intro) -* tkinter (https://github.com/jonwright/pyopengltk) +* pyglet () +* pygame () +* pyGLFW () +* PySDL2 () +* PySide2 () +* PyQt5 () +* tkinter () ### Loaders -* Pillow (https://python-pillow.org/) -* pywavefront (https://github.com/pywavefront/PyWavefront) -* trimesh (https://github.com/mikedh/trimesh) +* Pillow () +* pywavefront () +* trimesh () ### Testing & Utility -* PyGLM (https://github.com/Zuzu-Typ/PyGLM) -* numpy (https://github.com/numpy/numpy) -* pytest (https://docs.pytest.org/en/latest/) -* flake8 (https://gitlab.com/pycqa/flake8) -* coverage (https://github.com/nedbat/coveragepy) -* tox (https://tox.readthedocs.io/en/latest/) +* PyGLM () +* ruff () +* numpy () +* pytest () +* coverage () ## Resources -* NASA 3D Resources (https://github.com/nasa/NASA-3D-Resources) -* glTF Sample Models (https://github.com/KhronosGroup/glTF-Sample-Models) +* NASA 3D Resources () +* glTF Sample Models () ## Some History diff --git a/examples/advanced/fragment_picking.py b/examples/advanced/fragment_picking.py index 57724145..985d3719 100644 --- a/examples/advanced/fragment_picking.py +++ b/examples/advanced/fragment_picking.py @@ -24,17 +24,18 @@ class FragmentPicking(moderngl_window.WindowConfig): The normal is then used to hide points that point away from the camera. """ + title = "Fragment Picking" gl_version = 3, 3 window_size = 1280, 720 aspect_ratio = None resizable = True - resource_dir = (Path(__file__) / '../../resources').resolve() + resource_dir = (Path(__file__) / "../../resources").resolve() def __init__(self, **kwargs): super().__init__(**kwargs) print("window buffer size:", self.wnd.buffer_size) - self.marker_file = Path('markers.bin') + self.marker_file = Path("markers.bin") # Object rotation self.x_rot = 0 self.y_rot = 0 @@ -42,7 +43,7 @@ def __init__(self, **kwargs): self.zoom = 0 # Load scene cached to speed up loading! - self.scene = self.load_scene('scenes/fragment_picking/centered.obj', cache=True) + self.scene = self.load_scene("scenes/fragment_picking/centered.obj", cache=True) # Grab the raw mesh/vertexarray self.mesh = self.scene.root_nodes[0].mesh.vao self.mesh_texture = self.scene.root_nodes[0].mesh.material.mat_texture.texture @@ -58,9 +59,9 @@ def __init__(self, **kwargs): # RGBA color/diffuse layer self.offscreen_diffuse = self.ctx.texture(self.wnd.buffer_size, 4) # Textures for storing normals (16 bit floats) - self.offscreen_normals = self.ctx.texture(self.wnd.buffer_size, 4, dtype='f2') + self.offscreen_normals = self.ctx.texture(self.wnd.buffer_size, 4, dtype="f2") # Texture for storing the view positions rendered to framebuffer - self.offscreen_viewpos = self.ctx.texture(self.wnd.buffer_size, 4, dtype='f4') + self.offscreen_viewpos = self.ctx.texture(self.wnd.buffer_size, 4, dtype="f4") # Texture for storing depth values self.offscreen_depth = self.ctx.depth_texture(self.wnd.buffer_size) # Create a framebuffer we can render to @@ -77,7 +78,7 @@ def __init__(self, **kwargs): # temporary so we can use it as a normal texture self.depth_sampler = self.ctx.sampler( filter=(moderngl.LINEAR, moderngl.LINEAR), - compare_func='', + compare_func="", ) # A fullscreen quad just for rendering offscreen textures to the window @@ -85,37 +86,41 @@ def __init__(self, **kwargs): # --- Shaders # Simple program just rendering texture - self.texture_program = self.load_program('programs/fragment_picking/texture.glsl') - self.texture_program['texture0'].value = 0 + self.texture_program = self.load_program("programs/fragment_picking/texture.glsl") + self.texture_program["texture0"].value = 0 # Geomtry shader writing to two offscreen layers (color, normal) + depth - self.geometry_program = self.load_program('programs/fragment_picking/geometry.glsl') - self.geometry_program['texture0'].value = 0 # use texture channel 0 + self.geometry_program = self.load_program("programs/fragment_picking/geometry.glsl") + self.geometry_program["texture0"].value = 0 # use texture channel 0 # Shader for linearizing depth (debug visualization) - self.linearize_depth_program = self.load_program('programs/linearize_depth.glsl') - self.linearize_depth_program['texture0'].value = 0 - self.linearize_depth_program['near'].value = self.projection.near - self.linearize_depth_program['far'].value = self.projection.far + self.linearize_depth_program = self.load_program("programs/linearize_depth.glsl") + self.linearize_depth_program["texture0"].value = 0 + self.linearize_depth_program["near"].value = self.projection.near + self.linearize_depth_program["far"].value = self.projection.far # Shader for picking the world position of a fragment - self.fragment_picker_program = self.load_program('programs/fragment_picking/picker.glsl') - self.fragment_picker_program['position_texture'].value = 0 # Read from texture channel 0 - self.fragment_picker_program['normal_texture'].value = 1 # Read from texture channel 1 - self.fragment_picker_program['diffuse_texture'].value = 2 # Read from texture channel 2 + self.fragment_picker_program = self.load_program("programs/fragment_picking/picker.glsl") + self.fragment_picker_program["position_texture"].value = 0 # Read from texture channel 0 + self.fragment_picker_program["normal_texture"].value = 1 # Read from texture channel 1 + self.fragment_picker_program["diffuse_texture"].value = 2 # Read from texture channel 2 # Picker geometry - self.marker_byte_size = 7 * 4 # position + normal + temperature (7 x 32bit floats) + self.marker_byte_size = 7 * 4 # position + normal + temperature (7 x 32bit floats) self.picker_output = self.ctx.buffer(reserve=self.marker_byte_size) self.picker_vao = VAO(mode=moderngl.POINTS) # Shader for rendering markers - self.marker_program = self.load_program('programs/fragment_picking/markers.glsl') - self.marker_program['color'].value = 1.0, 0.0, 0.0, 1.0 + self.marker_program = self.load_program("programs/fragment_picking/markers.glsl") + self.marker_program["color"].value = 1.0, 0.0, 0.0, 1.0 # Marker geometry - self.marker_buffer = self.ctx.buffer(reserve=self.marker_byte_size * 1000) # Resever room for 1000 points + self.marker_buffer = self.ctx.buffer( + reserve=self.marker_byte_size * 1000 + ) # Resever room for 1000 points self.marker_vao = VAO(name="markers", mode=moderngl.POINTS) - self.marker_vao.buffer(self.marker_buffer, '3f 3f 1f', ['in_position', 'in_normal', 'temperature']) + self.marker_vao.buffer( + self.marker_buffer, "3f 3f 1f", ["in_position", "in_normal", "temperature"] + ) self.num_markers = 0 # Debug geometry @@ -135,8 +140,8 @@ def render(self, time, frametime): self.offscreen.use() # Render the scene - self.geometry_program['modelview'].write(self.modelview) - self.geometry_program['projection'].write(self.projection.matrix) + self.geometry_program["modelview"].write(self.modelview) + self.geometry_program["projection"].write(self.projection.matrix) self.mesh_texture.use(location=0) # bind texture from obj file to channel 0 self.depth_sampler.use(location=0) self.mesh.render(self.geometry_program) # render mesh @@ -153,8 +158,8 @@ def render(self, time, frametime): # Render markers if self.num_markers > 0: self.ctx.point_size = 6.0 # Specify fragment size of the markers - self.marker_program['modelview'].write(self.modelview) - self.marker_program['projection'].write(self.projection.matrix) + self.marker_program["modelview"].write(self.modelview) + self.marker_program["projection"].write(self.projection.matrix) self.marker_vao.render(self.marker_program, vertices=self.num_markers) self.render_debug() @@ -187,27 +192,31 @@ def mouse_press_event(self, x, y, button): # mouse coordinates starts in upper left corner # pixel positions starts and lower left corner - pos = int(x * self.wnd.pixel_ratio), int(self.wnd.buffer_height - (y * self.wnd.pixel_ratio)) + pos = int(x * self.wnd.pixel_ratio), int( + self.wnd.buffer_height - (y * self.wnd.pixel_ratio) + ) print("Picking mouse position", x, y) print("Viewport position", pos) - self.fragment_picker_program['texel_pos'].value = pos - self.fragment_picker_program['modelview'].write(self.modelview) + self.fragment_picker_program["texel_pos"].value = pos + self.fragment_picker_program["modelview"].write(self.modelview) self.offscreen_viewpos.use(location=0) self.offscreen_normals.use(location=1) self.offscreen_diffuse.use(location=2) self.picker_vao.transform(self.fragment_picker_program, self.picker_output, vertices=1) # Print position - x, y, z, nx, ny, nz, temperature = struct.unpack('7f', self.picker_output.read()) + x, y, z, nx, ny, nz, temperature = struct.unpack("7f", self.picker_output.read()) if z == 0.0: - print('Point is not on the mesh') + print("Point is not on the mesh") return print(f"Position: {x} {y} {z}") print(f"Normal: {nx} {ny} {nz}") print(f"Temperature: {round(temperature * 255)} (byte) {temperature} (float)") - self.marker_buffer.write(self.picker_output.read(), offset=self.marker_byte_size * self.num_markers) + self.marker_buffer.write( + self.picker_output.read(), offset=self.marker_byte_size * self.num_markers + ) self.num_markers += 1 def mouse_scroll_event(self, x_offset, y_offset): @@ -219,11 +228,11 @@ def key_event(self, key, action, modifiers): # Key presses if action == keys.ACTION_PRESS: if key == keys.F1: - print('Loading marker file') + print("Loading marker file") self.load_markers(self.marker_file) if key == keys.F2: - print('Saving marker file') + print("Saving marker file") self.save_markers(self.marker_file) def load_markers(self, path: Path): @@ -231,7 +240,7 @@ def load_markers(self, path: Path): if not self.marker_file.exists(): return - with open(self.marker_file, mode='rb') as fd: + with open(self.marker_file, mode="rb") as fd: size = self.marker_file.stat().st_size self.num_markers = size // self.marker_byte_size self.marker_buffer.write(fd.read(), offset=0) @@ -241,9 +250,9 @@ def save_markers(self, path: Path): if self.num_markers == 0: return - with open('markers.bin', mode='wb') as fd: + with open("markers.bin", mode="wb") as fd: fd.write(self.marker_buffer.read(size=self.num_markers * self.marker_byte_size)) -if __name__ == '__main__': +if __name__ == "__main__": moderngl_window.run_window_config(FragmentPicking) diff --git a/examples/advanced/navier_stokes.py b/examples/advanced/navier_stokes.py index 5a5aad96..7ad220dd 100644 --- a/examples/advanced/navier_stokes.py +++ b/examples/advanced/navier_stokes.py @@ -8,7 +8,6 @@ import random from pathlib import Path -import numpy as np import glm import moderngl_window diff --git a/examples/advanced/pygame2_simple.py b/examples/advanced/pygame2_simple.py index 619d1940..1e3befaa 100644 --- a/examples/advanced/pygame2_simple.py +++ b/examples/advanced/pygame2_simple.py @@ -40,6 +40,7 @@ [-1, 1] for both x and y. * Texture coordinates are in the [0.0, 1.0] range. """ + import math from array import array @@ -53,14 +54,15 @@ class Pygame(moderngl_window.WindowConfig): """ Example drawing a pygame surface with moderngl. """ + title = "Pygame" window_size = 1280, 720 def __init__(self, **kwargs): super().__init__(**kwargs) - if self.wnd.name != 'pygame2': - raise RuntimeError('This example only works with --window pygame2 option') + if self.wnd.name != "pygame2": + raise RuntimeError("This example only works with --window pygame2 option") # The resolution of the pygame surface self.pg_res = 320, 180 @@ -72,7 +74,7 @@ def __init__(self, **kwargs): self.pg_texture.filter = moderngl.NEAREST, moderngl.NEAREST # The pygame surface is stored in BGRA format but RGBA # so we simply change the order of the channels of the texture - self.pg_texture.swizzle = 'BGRA' + self.pg_texture.swizzle = "BGRA" # Let's make a custom texture shader rendering the surface self.texture_program = self.ctx.program( @@ -113,12 +115,13 @@ def __init__(self, **kwargs): ) # Explicitly configure the sampler to read from texture channel 0. # Most hardware today supports 8-16 different channels for multi-texturing. - self.texture_program['surface'] = 0 + self.texture_program["surface"] = 0 # Geometry to render the texture to the screen. # This is simply a "quad" covering the entire screen. # This is rendered as a triangle strip. # NOTE: using array.array is a simple way to create a buffer data + # fmt: off buffer = self.ctx.buffer( data=array('f', [ # Position (x, y) , Texture coordinates (x, y) @@ -128,6 +131,7 @@ def __init__(self, **kwargs): 1.0, -1.0, 1.0, 0.0, # lower right ]) ) + # fmt: on # Create a vertex array describing the buffer layout. # The shader program is also passed in there to sanity check # the attribute names. @@ -137,10 +141,13 @@ def __init__(self, **kwargs): ( # The buffer containing the data buffer, - # Format of the two attributes. 2 floats for position, 2 floats for texture coordinates + # Format of the two attributes. + # - 2 floats for position + # - 2 floats for texture coordinates "2f 2f", # Names of the attributes in the shader program - "in_vert", "in_texcoord", + "in_vert", + "in_texcoord", ) ], ) @@ -155,7 +162,7 @@ def render(self, time: float, frame_time: float): (math.sin(time + 2) + 1.0) / 2, (math.sin(time + 3) + 1.0) / 2, ) - + # Enable blending for transparency self.ctx.enable(moderngl.BLEND) # Bind the texture to texture channel 0 @@ -177,15 +184,16 @@ def render_pygame(self, time: float): ((i * 50) % 255, (i * 100) % 255, (i * 20) % 255), ( math.sin(time + time_offset) * 55 + self.pg_res[0] // 2, - math.cos(time + time_offset) * 55 + self.pg_res[1] // 2), + math.cos(time + time_offset) * 55 + self.pg_res[1] // 2, + ), math.sin(time) * 4 + 15, ) # Get the buffer view of the Surface's pixels # and write this data into the texture - texture_data = self.pg_screen.get_view('1') + texture_data = self.pg_screen.get_view("1") self.pg_texture.write(texture_data) -if __name__ == '__main__': - moderngl_window.run_window_config(Pygame, args=('--window', 'pygame2')) +if __name__ == "__main__": + moderngl_window.run_window_config(Pygame, args=("--window", "pygame2")) diff --git a/examples/advanced/tetrahedral_mesh.py b/examples/advanced/tetrahedral_mesh.py index 32115b07..412dda14 100644 --- a/examples/advanced/tetrahedral_mesh.py +++ b/examples/advanced/tetrahedral_mesh.py @@ -3,7 +3,6 @@ import glm import moderngl -import moderngl_window from moderngl_window.opengl.vao import VAO from moderngl_window import geometry from base import CameraWindow @@ -30,18 +29,19 @@ class VolumetricTetrahedralMesh(CameraWindow): - Press b to toggle blend mode on/off - Mouse wheel to increase or decrease the threshold for a tetra to be alive """ + gl_version = (4, 1) title = "Volumetric Tetrahedra lMesh" aspect_ratio = None - resource_dir = (Path(__file__) / '../../resources').resolve() + resource_dir = (Path(__file__) / "../../resources").resolve() samples = 4 def __init__(self, **kwargs): super().__init__(**kwargs) # Finetune camera self.wnd.mouse_exclusivity = True - self.camera.projection.update(near=.01, far=100) - self.camera.mouse_sensitivity = .5 + self.camera.projection.update(near=0.01, far=100) + self.camera.mouse_sensitivity = 0.5 self.camera.velocity = 2.5 self.camera.projection.update(fov=60) @@ -55,33 +55,33 @@ def __init__(self, **kwargs): self.quad_fs = geometry.quad_fs() # (172575,) | 57,525 vertices - vertices = np.load(self.resource_dir / 'data/tetrahedral_mesh/mesh_nodes.npy') + vertices = np.load(self.resource_dir / "data/tetrahedral_mesh/mesh_nodes.npy") vertices = np.concatenate(vertices) # (259490, 4) (1037960,) indices - indices = np.load(self.resource_dir / 'data/tetrahedral_mesh/element_nodes.npy') + indices = np.load(self.resource_dir / "data/tetrahedral_mesh/element_nodes.npy") indices = np.concatenate(indices) - 1 # Probability of a tetrahedron is still alive w, h = 8192, int(np.ceil(indices.shape[0] / 8192)) self.alive_data = np.random.random_sample(w * h) - self.alive_texture = self.ctx.texture((w, h), 1, dtype='f2') - self.alive_texture.write(self.alive_data.astype('f2')) + self.alive_texture = self.ctx.texture((w, h), 1, dtype="f2") + self.alive_texture.write(self.alive_data.astype("f2")) # Original geometry with indices - self.geometry = VAO(name='geometry_indices') - self.geometry.buffer(vertices, '3f', 'in_position') + self.geometry = VAO(name="geometry_indices") + self.geometry.buffer(vertices, "3f", "in_position") self.geometry.index_buffer(indices, index_element_size=4) - self.prog_background = self.load_program('programs/tetrahedral_mesh/bg.glsl') + self.prog_background = self.load_program("programs/tetrahedral_mesh/bg.glsl") self.prog_gen_tetra = self.load_program( - vertex_shader='programs/tetrahedral_mesh/gen_tetra_vert.glsl', - geometry_shader='programs/tetrahedral_mesh/gen_tetra_geo.glsl', - fragment_shader='programs/tetrahedral_mesh/gen_tetra_frag.glsl', + vertex_shader="programs/tetrahedral_mesh/gen_tetra_vert.glsl", + geometry_shader="programs/tetrahedral_mesh/gen_tetra_geo.glsl", + fragment_shader="programs/tetrahedral_mesh/gen_tetra_frag.glsl", ) self.prog_gen_tetra_lines = self.load_program( - vertex_shader='programs/tetrahedral_mesh/gen_tetra_vert.glsl', - geometry_shader='programs/tetrahedral_mesh/gen_tetra_geo.glsl', - fragment_shader='programs/tetrahedral_mesh/lines_frag.glsl', + vertex_shader="programs/tetrahedral_mesh/gen_tetra_vert.glsl", + geometry_shader="programs/tetrahedral_mesh/gen_tetra_geo.glsl", + fragment_shader="programs/tetrahedral_mesh/lines_frag.glsl", ) # Query object for measuring the rendering call in OpenGL @@ -113,21 +113,21 @@ def render(self, time, frametime): # All render calls inside this context are timed with self.query: self.alive_texture.use(location=0) - self.prog_gen_tetra['alive_texture'].value = 0 - self.prog_gen_tetra['threshold'].value = self.threshold - self.prog_gen_tetra['color'].value = self.mesh_color - self.prog_gen_tetra['m_cam'].write(mat) - self.prog_gen_tetra['m_proj'].write(self.camera.projection.matrix) + self.prog_gen_tetra["alive_texture"].value = 0 + self.prog_gen_tetra["threshold"].value = self.threshold + self.prog_gen_tetra["color"].value = self.mesh_color + self.prog_gen_tetra["m_cam"].write(mat) + self.prog_gen_tetra["m_proj"].write(self.camera.projection.matrix) self.geometry.render(self.prog_gen_tetra, mode=moderngl.LINES_ADJACENCY) # Render lines self.ctx.wireframe = True self.alive_texture.use(location=0) - self.prog_gen_tetra_lines['alive_texture'].value = 0 - self.prog_gen_tetra_lines['threshold'].value = self.threshold - self.prog_gen_tetra_lines['color'].value = self.line_color - self.prog_gen_tetra_lines['m_cam'].write(mat) - self.prog_gen_tetra_lines['m_proj'].write(self.camera.projection.matrix) + self.prog_gen_tetra_lines["alive_texture"].value = 0 + self.prog_gen_tetra_lines["threshold"].value = self.threshold + self.prog_gen_tetra_lines["color"].value = self.line_color + self.prog_gen_tetra_lines["m_cam"].write(mat) + self.prog_gen_tetra_lines["m_proj"].write(self.camera.projection.matrix) self.geometry.render(self.prog_gen_tetra_lines, mode=moderngl.LINES_ADJACENCY) self.total_elapsed = self.query.elapsed @@ -159,11 +159,13 @@ def close(self): # 1 s = 1000000000 ns # 1 s = 1000000 μs avg = self.total_elapsed / self.wnd.frames - print("Average rendering time per frame: {} ns | {} μs".format( - round(avg, 4), # ns - round(avg / 1000, 4), # μs - )) + print( + "Average rendering time per frame: {} ns | {} μs".format( + round(avg, 4), # ns + round(avg / 1000, 4), # μs + ) + ) -if __name__ == '__main__': +if __name__ == "__main__": VolumetricTetrahedralMesh.run() diff --git a/examples/cubes.py b/examples/cubes.py index f6808bc9..06e7af73 100644 --- a/examples/cubes.py +++ b/examples/cubes.py @@ -2,6 +2,7 @@ Cubes of different vertex formats. These formats are unique for wavefront obj files. """ + from pathlib import Path import moderngl @@ -14,31 +15,31 @@ class Cubes(moderngl_window.WindowConfig): title = "Cubes" resizable = True aspect_ratio = None - resource_dir = Path(__file__).parent.resolve() / 'resources' + resource_dir = Path(__file__).parent.resolve() / "resources" def __init__(self, **kwargs): super().__init__(**kwargs) # Load the 6 different boxes with different vertex formats - self.box_v3 = self.load_scene('scenes/box/box-V3F.obj') - self.box_c3_v3 = self.load_scene('scenes/box/box-C3F_V3F.obj') - self.box_n3_v3 = self.load_scene('scenes/box/box-N3F_V3F.obj') - self.box_t2_v3 = self.load_scene('scenes/box/box-T2F_V3F.obj') - self.box_t2_c3_v3 = self.load_scene('scenes/box/box-T2F_C3F_V3F.obj') - self.box_t2_n3_v3 = self.load_scene('scenes/box/box-T2F_N3F_V3F.obj') + self.box_v3 = self.load_scene("scenes/box/box-V3F.obj") + self.box_c3_v3 = self.load_scene("scenes/box/box-C3F_V3F.obj") + self.box_n3_v3 = self.load_scene("scenes/box/box-N3F_V3F.obj") + self.box_t2_v3 = self.load_scene("scenes/box/box-T2F_V3F.obj") + self.box_t2_c3_v3 = self.load_scene("scenes/box/box-T2F_C3F_V3F.obj") + self.box_t2_n3_v3 = self.load_scene("scenes/box/box-T2F_N3F_V3F.obj") self.resize(*self.wnd.size) def render(self, time, frame_time): self.ctx.enable_only(moderngl.DEPTH_TEST | moderngl.CULL_FACE) - rot = glm.mat4(glm.quat(glm.vec3(time, time/2, time/3))) + rot = glm.mat4(glm.quat(glm.vec3(time, time / 2, time / 3))) # Box 1 view = glm.translate(glm.vec3(-5, 2, -10)) self.box_v3.draw(self.projection, view * rot) # Box 2 - view = glm.translate(glm.vec3(0, 2, -10)) + view = glm.translate(glm.vec3(0, 2, -10)) self.box_c3_v3.draw(self.projection, view * rot) # Box 3 @@ -62,5 +63,5 @@ def resize(self, width, height): self.projection = glm.perspective(glm.radians(45), width / height, 1, 50) -if __name__ == '__main__': +if __name__ == "__main__": Cubes.run() diff --git a/examples/geometry_lines.py b/examples/geometry_lines.py index 9039ae1b..91aab6dd 100644 --- a/examples/geometry_lines.py +++ b/examples/geometry_lines.py @@ -4,7 +4,6 @@ import moderngl import moderngl_window -from moderngl_window import geometry from base import CameraWindow @@ -14,19 +13,21 @@ class LinesDemo(CameraWindow): Example is basic and incomplete, but shows how one could use the geometry shader to create thick lines. """ + gl_version = (3, 3) title = "Thick Lines" - resource_dir = (Path(__file__) / '../resources').absolute() + resource_dir = (Path(__file__) / "../resources").absolute() def __init__(self, **kwargs): super().__init__(**kwargs) self.wnd.mouse_exclusivity = True - self.prog = self.load_program('programs/lines/lines.glsl') - self.prog['color'].value = (1.0, 1.0, 1.0, 1.0) - self.prog['m_model'].write(glm.translate(glm.vec3(0.0, 0.0, -3.5))) + self.prog = self.load_program("programs/lines/lines.glsl") + self.prog["color"].value = (1.0, 1.0, 1.0, 1.0) + self.prog["m_model"].write(glm.translate(glm.vec3(0.0, 0.0, -3.5))) N = 10 + # Create lines geometry def gen_lines(): for i in range(N): @@ -39,21 +40,23 @@ def gen_lines(): yield 1.0 - i * 2.0 / N yield 0.0 - buffer = self.ctx.buffer(numpy.fromiter(gen_lines(), dtype='f4', count=N * 6).tobytes()) + buffer = self.ctx.buffer( + numpy.fromiter(gen_lines(), dtype="f4", count=N * 6).tobytes() + ) self.lines = self.ctx.vertex_array( self.prog, [ - (buffer, '3f', 'in_position'), + (buffer, "3f", "in_position"), ], ) def render(self, time, frametime): # self.ctx.enable_only(moderngl.DEPTH_TEST) - self.prog['m_proj'].write(self.camera.projection.matrix) - self.prog['m_cam'].write(self.camera.matrix) + self.prog["m_proj"].write(self.camera.projection.matrix) + self.prog["m_cam"].write(self.camera.matrix) self.lines.render(mode=moderngl.LINES) -if __name__ == '__main__': +if __name__ == "__main__": moderngl_window.run_window_config(LinesDemo) diff --git a/examples/geometry_quad_fs_mouse_scroll.py b/examples/geometry_quad_fs_mouse_scroll.py index a242da74..b2f026e1 100644 --- a/examples/geometry_quad_fs_mouse_scroll.py +++ b/examples/geometry_quad_fs_mouse_scroll.py @@ -4,26 +4,30 @@ from moderngl_window import geometry from moderngl_window import resources -resources.register_dir((Path(__file__).parent / 'resources').resolve()) +resources.register_dir((Path(__file__).parent / "resources").resolve()) class QuadFullscreenScroll(moderngl_window.WindowConfig): """Taking texture offset from mouse""" + aspect_ratio = None def __init__(self, **kwargs): super().__init__(**kwargs) self.quad = geometry.quad_fs() - self.texture = self.load_texture_2d('textures/python-bg.png') - self.prog = self.load_program('programs/texture_mouse_scroll.glsl') + self.texture = self.load_texture_2d("textures/python-bg.png") + self.prog = self.load_program("programs/texture_mouse_scroll.glsl") self.mouse_pos = 0, 0 def render(self, time: float, frame_time: float): self.ctx.clear() self.texture.use(location=0) - self.prog['texture0'] = 0 - self.prog['offset'] = -self.mouse_pos[0] / self.wnd.buffer_width, self.mouse_pos[1] / self.wnd.buffer_height + self.prog["texture0"] = 0 + self.prog["offset"] = ( + -self.mouse_pos[0] / self.wnd.buffer_width, + self.mouse_pos[1] / self.wnd.buffer_height, + ) self.quad.render(self.prog) def mouse_position_event(self, x, y, dx, dy): @@ -31,5 +35,5 @@ def mouse_position_event(self, x, y, dx, dy): print(self.mouse_pos) -if __name__ == '__main__': +if __name__ == "__main__": moderngl_window.run_window_config(QuadFullscreenScroll) diff --git a/examples/gltf_scenes.py b/examples/gltf_scenes.py index b458a3f4..f9e321ba 100644 --- a/examples/gltf_scenes.py +++ b/examples/gltf_scenes.py @@ -26,37 +26,45 @@ def __init__(self, **kwargs): # --- glTF-Sample-Models --- # self.scene = self.load_scene("2CylinderEngine/glTF-Binary/2CylinderEngine.glb") # self.scene = self.load_scene('CesiumMilkTruck/glTF-Embedded/CesiumMilkTruck.gltf') - # self.scene = self.load_scene('CesiumMilkTruck/glTF-Binary/CesiumMilkTruck.glb') + # self.scene = self.load_scene("CesiumMilkTruck/glTF-Binary/CesiumMilkTruck.glb") # self.scene = self.load_scene("CesiumMilkTruck/glTF/CesiumMilkTruck.gltf") - self.scene = self.load_scene("Sponza/glTF/Sponza.gltf") + # self.scene = self.load_scene("Sponza/glTF/Sponza.gltf") # self.scene = self.load_scene("Lantern/glTF-Binary/Lantern.glb") # self.scene = self.load_scene("Buggy/glTF-Binary/Buggy.glb") - # self.scene = self.load_scene("VC/glTF-Binary/VC.glb") + self.scene = self.load_scene("VC/glTF-Binary/VC.glb") # self.scene = self.load_scene('DamagedHelmet/glTF-Binary/DamagedHelmet.glb') - # self.scene = self.load_scene('BoxInterleaved/glTF/BoxInterleaved.gltf') - # self.scene = self.load_scene('OrientationTest/glTF/OrientationTest.gltf') - # self.scene = self.load_scene('AntiqueCamera/glTF/AntiqueCamera.gltf') - # self.scene = self.load_scene('BoomBox/glTF/BoomBox.gltf') + # self.scene = self.load_scene("BoxInterleaved/glTF/BoxInterleaved.gltf") + # self.scene = self.load_scene("OrientationTest/glTF/OrientationTest.gltf") + # self.scene = self.load_scene("AntiqueCamera/glTF/AntiqueCamera.gltf") + # self.scene = self.load_scene("BoomBox/glTF/BoomBox.gltf") # self.scene = self.load_scene('Box/glTF/Box.gltf') - # self.scene = self.load_scene('BoxTextured/glTF/BoxTextured.gltf') - # self.scene = self.load_scene('BoxTexturedNonPowerOfTwo/glTF/BoxTexturedNonPowerOfTwo.gltf') - # self.scene = self.load_scene('BoxVertexColors/glTF/BoxVertexColors.gltf') - # self.scene = self.load_scene('BrainStem/glTF/BrainStem.gltf') - # self.scene = self.load_scene('Corset/glTF/Corset.gltf') - # self.scene = self.load_scene('FlightHelmet/glTF/FlightHelmet.gltf') - # self.scene = self.load_scene('Fox/glTF/Fox.gltf') - # self.scene = self.load_scene('GearboxAssy/glTF/GearboxAssy.gltf') + # self.scene = self.load_scene("BoxTextured/glTF/BoxTextured.gltf") + # self.scene = self.load_scene( + # "BoxTexturedNonPowerOfTwo/glTF/BoxTexturedNonPowerOfTwo.gltf" + # ) + # self.scene = self.load_scene("BoxVertexColors/glTF/BoxVertexColors.gltf") + # self.scene = self.load_scene("BrainStem/glTF/BrainStem.gltf") + # self.scene = self.load_scene("Corset/glTF/Corset.gltf") + # self.scene = self.load_scene("FlightHelmet/glTF/FlightHelmet.gltf") + # self.scene = self.load_scene("Fox/glTF/Fox.gltf") + # self.scene = self.load_scene("GearboxAssy/glTF/GearboxAssy.gltf") # self.scene = self.load_scene("ReciprocatingSaw/glTF/ReciprocatingSaw.gltf") # self.scene = self.load_scene('RiggedFigure/glTF/RiggedFigure.gltf') - # self.scene = self.load_scene('RiggedSimple/glTF/RiggedSimple.gltf') - # self.scene = self.load_scene('SciFiHelmet/glTF/SciFiHelmet.gltf') - # self.scene = self.load_scene('SimpleMeshes/glTF/SimpleMeshes.gltf') - # self.scene = self.load_scene('SimpleSparseAccessor/glTF/SimpleSparseAccessor.gltf') + # self.scene = self.load_scene("RiggedSimple/glTF/RiggedSimple.gltf") + # self.scene = self.load_scene("SciFiHelmet/glTF/SciFiHelmet.gltf") + # self.scene = self.load_scene("SimpleMeshes/glTF/SimpleMeshes.gltf") + # self.scene = self.load_scene( + # "SimpleSparseAccessor/glTF/SimpleSparseAccessor.gltf" + # ) # self.scene = self.load_scene("Suzanne/glTF/Suzanne.gltf") - # self.scene = self.load_scene('TextureCoordinateTest/glTF/TextureCoordinateTest.gltf') - # self.scene = self.load_scene('TextureSettingsTest/glTF/TextureSettingsTest.gltf') - # self.scene = self.load_scene('VertexColorTest/glTF/VertexColorTest.gltf') - # self.scene = self.load_scene('WaterBottle/glTF/WaterBottle.gltf') + # self.scene = self.load_scene( + # "TextureCoordinateTest/glTF/TextureCoordinateTest.gltf" + # ) + # self.scene = self.load_scene( + # "TextureSettingsTest/glTF/TextureSettingsTest.gltf" + # ) + # self.scene = self.load_scene("VertexColorTest/glTF/VertexColorTest.gltf") + # self.scene = self.load_scene("WaterBottle/glTF/WaterBottle.gltf") self.camera = KeyboardCamera( self.wnd.keys, @@ -69,8 +77,8 @@ def __init__(self, **kwargs): self.camera.mouse_sensitivity = 0.25 # Use this for gltf scenes for better camera controls - if self.scene.diagonal_size > 0: - self.camera.velocity = self.scene.diagonal_size / 10.0 + # if self.scene.diagonal_size > 0: + # self.camera.velocity = self.scene.diagonal_size / 5.0 def render(self, time: float, frame_time: float): """Render the scene""" diff --git a/examples/headless.py b/examples/headless.py index 56f953ce..64a3b742 100644 --- a/examples/headless.py +++ b/examples/headless.py @@ -3,45 +3,56 @@ import moderngl import moderngl_window + class HeadlessTest(moderngl_window.WindowConfig): """ Simple one frame renderer writing to png and exit. If you need more fancy stuff, see the custom_config* examples. """ + samples = 0 # Headless is not always happy with multisampling def __init__(self, **kwargs): super().__init__(**kwargs) - if self.wnd.name != 'headless': - raise RuntimeError('This example only works with --window headless option') - - prog = self.ctx.program(vertex_shader=""" - #version 330 - in vec2 in_vert; - in vec3 in_color; - out vec3 color; - void main() { - gl_Position = vec4(in_vert, 0.0, 1.0); - color = in_color; - } - """, - fragment_shader=""" - #version 330 - out vec4 fragColor; - in vec3 color; - void main() { - fragColor = vec4(color, 1.0); - } - """, + if self.wnd.name != "headless": + raise RuntimeError("This example only works with --window headless option") + + prog = self.ctx.program( + vertex_shader=""" + #version 330 + + in vec2 in_vert; + in vec3 in_color; + out vec3 color; + + void main() { + gl_Position = vec4(in_vert, 0.0, 1.0); + color = in_color; + } + """, + fragment_shader=""" + #version 330 + + out vec4 fragColor; + in vec3 color; + + void main() { + fragColor = vec4(color, 1.0); + } + """, ) + # fmt: off vertices = np.array([ -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0], dtype='f4', ) - self.vao = self.ctx.simple_vertex_array(prog, self.ctx.buffer(vertices), 'in_vert', 'in_color') + # fmt: on + self.vao = self.ctx.simple_vertex_array( + prog, self.ctx.buffer(vertices), "in_vert", "in_color" + ) def render(self, time, frame_time): """Render one frame, save to png and close it""" @@ -49,16 +60,16 @@ def render(self, time, frame_time): self.ctx.clear(1, 1, 1, 1) # Render the geometry self.vao.render(mode=moderngl.TRIANGLES) - + # Wait for all rendering calls to finish (Might not be needed) self.ctx.finish() - image = Image.frombytes('RGBA', self.wnd.fbo.size, self.wnd.fbo.read(components=4)) + image = Image.frombytes("RGBA", self.wnd.fbo.size, self.wnd.fbo.read(components=4)) image = image.transpose(Image.FLIP_TOP_BOTTOM) - image.save('triangle.png', format='png') + image.save("triangle.png", format="png") self.wnd.close() - -if __name__ == '__main__': + +if __name__ == "__main__": HeadlessTest.run() diff --git a/examples/integration_imgui_image.py b/examples/integration_imgui_image.py index e8635d9c..61fcb1df 100644 --- a/examples/integration_imgui_image.py +++ b/examples/integration_imgui_image.py @@ -1,4 +1,5 @@ from pathlib import Path + # import imgui from imgui_bundle import imgui import moderngl @@ -7,13 +8,11 @@ from moderngl_window import geometry from moderngl_window.integrations.imgui_bundle import ModernglWindowRenderer -import PIL - class WindowEvents(mglw.WindowConfig): gl_version = (3, 3) title = "imgui Integration" - resource_dir = (Path(__file__).parent / '../examples/resources').resolve() + resource_dir = (Path(__file__).parent / "../examples/resources").resolve() aspect_ratio = None def __init__(self, **kwargs): @@ -23,10 +22,10 @@ def __init__(self, **kwargs): self.imgui = ModernglWindowRenderer(self.wnd) self.cube = geometry.cube(size=(2, 2, 2)) - self.prog = self.load_program('programs/cube_simple.glsl') - self.prog['color'].value = (1.0, 1.0, 1.0, 1.0) - self.prog['m_camera'].write(glm.mat4()) - self.prog['m_proj'].write(glm.perspective(glm.radians(75), 1.0, 1, 100)) + self.prog = self.load_program("programs/cube_simple.glsl") + self.prog["color"].value = (1.0, 1.0, 1.0, 1.0) + self.prog["m_camera"].write(glm.mat4()) + self.prog["m_proj"].write(glm.perspective(glm.radians(75), 1.0, 1, 100)) self.fbo = self.ctx.framebuffer( color_attachments=self.ctx.texture((512, 512), 4), @@ -46,7 +45,7 @@ def render(self, time: float, frametime: float): self.fbo.use() self.fbo.clear() self.ctx.enable(moderngl.DEPTH_TEST | moderngl.CULL_FACE) - self.prog['m_model'].write(model) + self.prog["m_model"].write(model) self.cube.render(self.prog) # Render UI to screen @@ -59,9 +58,7 @@ def render_ui(self): if imgui.begin_main_menu_bar(): if imgui.begin_menu("File", True): - clicked_quit, selected_quit = imgui.menu_item( - "Quit", 'Cmd+Q', False, True - ) + clicked_quit, selected_quit = imgui.menu_item("Quit", "Cmd+Q", False, True) if clicked_quit: exit(1) @@ -73,15 +70,15 @@ def render_ui(self): imgui.begin("Custom window", True) imgui.text("Bar") - imgui.text_colored(imgui.ImVec4(0.2, 1., 0., 1.), "Eggs") + imgui.text_colored(imgui.ImVec4(0.2, 1.0, 0.0, 1.0), "Eggs") imgui.end() # Create window with the framebuffer image imgui.begin("Custom window with Image", True) - # Create an image control by passing in the OpenGL texture ID (glo) + # Create an image control by passing in the OpenGL texture ID (glo) # and pass in the image size as well. # The texture needs to he registered using register_texture for this to work - imgui.image(self.fbo.color_attachments[0].glo, self.fbo.size ) + imgui.image(self.fbo.color_attachments[0].glo, self.fbo.size) imgui.end() imgui.render() @@ -112,5 +109,5 @@ def unicode_char_entered(self, char): self.imgui.unicode_char_entered(char) -if __name__ == '__main__': +if __name__ == "__main__": mglw.run_window_config(WindowEvents) diff --git a/examples/modify_parser.py b/examples/modify_parser.py index 0498a5cf..3efb5a20 100644 --- a/examples/modify_parser.py +++ b/examples/modify_parser.py @@ -15,8 +15,8 @@ python script.py path/to/file --title "Custom Window Title" """ + import math -from pathlib import Path import moderngl_window @@ -27,11 +27,11 @@ class ModifyParser(moderngl_window.WindowConfig): def __init__(self, **kwargs): super().__init__(**kwargs) - print('all arguments :', self.argv) + print("all arguments :", self.argv) # Print our custom argument - print('path :', self.argv.path) - print('wireframe :', self.argv.wireframe) - print('title :', self.argv.title) + print("path :", self.argv.path) + print("wireframe :", self.argv.wireframe) + print("title :", self.argv.title) if self.argv.title: self.wnd.title = self.argv.title @@ -40,20 +40,20 @@ def __init__(self, **kwargs): def add_arguments(cls, parser): # Mandatory positional argument for the file to load parser.add_argument( - 'path', + "path", help="Path to the model to display", ) # Optional flag for rendering the model in wireframe # This is simply enabled by adding "--wireframe" with no arguments parser.add_argument( - '--wireframe', + "--wireframe", action="store_true", default=False, help="Display the model as a wireframe", ) # Optional argument for window title parser.add_argument( - '--title', + "--title", type=str, help="Override the window title", ) @@ -67,5 +67,5 @@ def render(self, time, frame_time): ) -if __name__ == '__main__': +if __name__ == "__main__": ModifyParser.run() diff --git a/moderngl_window/__init__.py b/moderngl_window/__init__.py index 393f5765..fe33d455 100644 --- a/moderngl_window/__init__.py +++ b/moderngl_window/__init__.py @@ -62,9 +62,7 @@ def setup_basic_logging(level: int): logger.setLevel(level) ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) - ch.setFormatter( - logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") - ) + ch.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) logger.addHandler(ch) @@ -206,9 +204,7 @@ def run_window_config(config_cls: WindowConfig, timer=None, args=None) -> None: title=config_cls.title, size=size, fullscreen=config_cls.fullscreen or values.fullscreen, - resizable=( - values.resizable if values.resizable is not None else config_cls.resizable - ), + resizable=(values.resizable if values.resizable is not None else config_cls.resizable), visible=config_cls.visible, gl_version=config_cls.gl_version, aspect_ratio=config_cls.aspect_ratio, @@ -250,11 +246,7 @@ def run_window_config(config_cls: WindowConfig, timer=None, args=None) -> None: _, duration = timer.stop() window.destroy() if duration > 0: - logger.info( - "Duration: {0:.2f}s @ {1:.2f} FPS".format( - duration, window.frames / duration - ) - ) + logger.info("Duration: {0:.2f}s @ {1:.2f} FPS".format(duration, window.frames / duration)) def create_parser(): @@ -351,9 +343,7 @@ def valid_bool(value): if value in OPTIONS_FALSE: return False - raise argparse.ArgumentTypeError( - "Boolean value expected. Options: {}".format(OPTIONS_ALL) - ) + raise argparse.ArgumentTypeError("Boolean value expected. Options: {}".format(OPTIONS_ALL)) def valid_window_size(value): diff --git a/moderngl_window/atlas/simple_atlas.py b/moderngl_window/atlas/simple_atlas.py index 7fd43a11..47db9ee5 100644 --- a/moderngl_window/atlas/simple_atlas.py +++ b/moderngl_window/atlas/simple_atlas.py @@ -22,6 +22,7 @@ class _Row: """ A row in the texture atlas. """ + __slots__ = ("x", "y", "y2", "max_height") def __init__(self, y: int, max_height: int) -> None: @@ -96,6 +97,7 @@ class TextureAtlas: is normally sufficient for dynamic atlases were textures are added on the fly runtime. """ + def __init__( self, ctx: moderngl.Context, @@ -120,9 +122,7 @@ def __init__( self._texture = self._ctx.texture(self.size, components=self._components) # We want to be able to render into the atlas texture - self._fbo = self._fbo = self._ctx.framebuffer( - color_attachments=[self._texture] - ) + self._fbo = self._fbo = self._ctx.framebuffer(color_attachments=[self._texture]) self._allocator = Allocator(width, height) @property diff --git a/moderngl_window/capture/base.py b/moderngl_window/capture/base.py index 2b0baa70..9f055521 100644 --- a/moderngl_window/capture/base.py +++ b/moderngl_window/capture/base.py @@ -8,15 +8,15 @@ class BaseVideoCapture: """ - ``BaseVideoCapture`` is a base class to video capture + ``BaseVideoCapture`` is a base class to video capture - Args: - source (moderngl.Texture, moderngl.Framebuffer): the source of the capture - framerate (int, float) : the framerate of the video, by thefault is 60 fps + Args: + source (moderngl.Texture, moderngl.Framebuffer): the source of the capture + framerate (int, float) : the framerate of the video, by thefault is 60 fps - if the source is texture there are some requirements: - - dtype = 'f1'; - - components >= 3. + if the source is texture there are some requirements: + - dtype = 'f1'; + - components >= 3. """ def __init__( @@ -44,48 +44,48 @@ def __init__( def _dump_frame(self, frame): """ - custom function called during self.save() + custom function called during self.save() - Args: - frame: frame data in bytes + Args: + frame: frame data in bytes """ raise NotImplementedError("override this function") def _start_func(self) -> bool: """ - custom function called during self.start_capture() + custom function called during self.start_capture() - must return a True if this function complete without errors + must return a True if this function complete without errors """ raise NotImplementedError("override this function") def _release_func(self): """ - custom function called during self.realease() + custom function called during self.release() """ raise NotImplementedError("override this function") def _get_wh(self): """ - Return a tuple of the width and the height of the source + Return a tuple of the width and the height of the source """ return self._source.width, self._source.height def _remove_file(self): - """ Remove the filename of the video is it exist """ + """Remove the filename of the video is it exist""" if os.path.exists(self._filename): os.remove(self._filename) def start_capture(self, filename: str = None, framerate: Union[int, float] = 60): """ - Start the capturing process + Start the capturing process - Args: - filename (str): name of the output file - framerate (int, float): framerate of the video + Args: + filename (str): name of the output file + framerate (int, float): framerate of the video - if filename is not specified it will be generated based - on the datetime. + if filename is not specified it will be generated based + on the datetime. """ if self._recording: print("Capturing is already started") @@ -93,7 +93,7 @@ def start_capture(self, filename: str = None, framerate: Union[int, float] = 60) # ensure the texture has the correct dtype and components if isinstance(self._source, moderngl.Texture): - if self._source.dtype != 'f1': + if self._source.dtype != "f1": print("source type: moderngl.Texture must be type `f1` ") return if self._components < 3: @@ -102,7 +102,7 @@ def start_capture(self, filename: str = None, framerate: Union[int, float] = 60) if not filename: now = datetime.datetime.now() - filename = f'video_{now:%Y%m%d_%H%M%S}.mp4' + filename = f"video_{now:%Y%m%d_%H%M%S}.mp4" self._filename = filename @@ -123,12 +123,12 @@ def start_capture(self, filename: str = None, framerate: Union[int, float] = 60) def save(self): """ - Save function to call at the end of render function + Save function to call at the end of render function """ if not self._recording: return - dt = 1. / self._framerate + dt = 1.0 / self._framerate if self._timer.time - self._last_time > dt: diff --git a/moderngl_window/capture/ffmpeg.py b/moderngl_window/capture/ffmpeg.py index 56ff1b2b..159ddd0a 100644 --- a/moderngl_window/capture/ffmpeg.py +++ b/moderngl_window/capture/ffmpeg.py @@ -5,44 +5,44 @@ class FFmpegCapture(BaseVideoCapture): """ - ``FFmpegCapture`` it's an utility class to capture runtime render - and save it as video. + ``FFmpegCapture`` it's an utility class to capture runtime render + and save it as video. - Args: + Args: - Example: + Example: - .. code:: python + .. code:: python - import moderngl_window - from moderngl_window.capture.ffmpeg import FFmpegCapture + import moderngl_window + from moderngl_window.capture.ffmpeg import FFmpegCapture - class CaptureTest(modenrgl_window.WindowConfig): + class CaptureTest(modenrgl_window.WindowConfig): - def __init__(self, **kwargs): - super().__init__(**kwargs) - # do other initialization + def __init__(self, **kwargs): + super().__init__(**kwargs) + # do other initialization - # define VideoCapture class - self.cap = FFmpegCapture(source=self.wnd.fbo) + # define VideoCapture class + self.cap = FFmpegCapture(source=self.wnd.fbo) - # start recording - self.cap.start_capture( - filename="video.mp4", - framerate=30 - ) + # start recording + self.cap.start_capture( + filename="video.mp4", + framerate=30 + ) - def render(self, time, frametime): - # do other render stuff + def render(self, time, frametime): + # do other render stuff - # call record function after - self.cap.save() + # call record function after + self.cap.save() - def close(self): - # if realease func is not called during - # runtime. make sure to do it on the closing event - self.cap.release() + def close(self): + # if realease func is not called during + # runtime. make sure to do it on the closing event + self.cap.release() """ @@ -52,39 +52,44 @@ def __init__(self, **kwargs): def _start_func(self) -> bool: """ - choose the right pixel format based on the number of components - and start a ffmper pipe with a subprocess. + choose the right pixel format based on the number of components + and start a ffmpeg pipe with a subprocess. """ - pix_fmt = 'rgb24' # 3 component, 1 byte per color -> 24 bit + pix_fmt = "rgb24" # 3 component, 1 byte per color -> 24 bit # for the framebuffer is easier because i can read 3 component even if # the color attachment has less components if isinstance(self._source, moderngl.Texture) and self._components == 4: - pix_fmt = 'rgba' # 4 component , 1 byte per color -> 32 bit + pix_fmt = "rgba" # 4 component , 1 byte per color -> 32 bit command = [ - 'ffmpeg', - '-hide_banner', - '-loglevel', 'error', '-stats', # less verbose, only stats of recording - '-y', # (optional) overwrite output file if it exists - '-f', 'rawvideo', - '-vcodec', 'rawvideo', - '-s', f'{self._width}x{self._height}', # size of one frame - '-pix_fmt', pix_fmt, - '-r', f'{self._framerate}', # frames per second - '-i', '-', # The imput comes from a pipe - '-vf', 'vflip', - '-an', # Tells FFMPEG not to expect any audio + "ffmpeg", + "-hide_banner", + "-loglevel", + "error", + "-stats", # less verbose, only stats of recording + "-y", # (optional) overwrite output file if it exists + "-f", + "rawvideo", + "-vcodec", + "rawvideo", + "-s", + f"{self._width}x{self._height}", # size of one frame + "-pix_fmt", + pix_fmt, + "-r", + f"{self._framerate}", # frames per second + "-i", + "-", # The imput comes from a pipe + "-vf", + "vflip", + "-an", # Tells FFMPEG not to expect any audio self._filename, ] # ffmpeg binary need to be on the PATH. try: - self._ffmpeg = subprocess.Popen( - command, - stdin=subprocess.PIPE, - bufsize=0 - ) + self._ffmpeg = subprocess.Popen(command, stdin=subprocess.PIPE, bufsize=0) except FileNotFoundError: print("ffmpeg command not found. Be sure to add it to PATH") return diff --git a/moderngl_window/conf/__init__.py b/moderngl_window/conf/__init__.py index 08e76b1b..ab7eebd8 100644 --- a/moderngl_window/conf/__init__.py +++ b/moderngl_window/conf/__init__.py @@ -1,6 +1,7 @@ """ Bag of settings values """ + # pylint: disable = invalid-name import importlib import types @@ -269,7 +270,8 @@ def apply_from_module_name(self, settings_module_name: str) -> None: except ImportError as ex: raise ImproperlyConfigured( "Settings module '{}' not found. From importlib: {}".format( - settings_module_name, ex, + settings_module_name, + ex, ) ) @@ -321,15 +323,11 @@ def apply_from_cls(self, cls) -> None: """ self.apply_from_iterable(cls.__dict__.items()) - def apply_from_iterable( - self, iterable: Union[Iterable, types.GeneratorType] - ) -> None: + def apply_from_iterable(self, iterable: Union[Iterable, types.GeneratorType]) -> None: """ Apply (key, value) pairs from an interable or generator """ - if not isinstance(iterable, Iterable) and not isinstance( - self, types.GeneratorType - ): + if not isinstance(iterable, Iterable) and not isinstance(self, types.GeneratorType): raise ValueError( "Input value is not a generator or interable, but of type: {}".format( type(iterable) @@ -351,9 +349,7 @@ def to_dict(self): def __repr__(self) -> str: return "\n".join( - "{}={}".format(k, pformat(v, indent=2)) - for k, v in self.__dict__.items() - if k.isupper() + "{}={}".format(k, pformat(v, indent=2)) for k, v in self.__dict__.items() if k.isupper() ) diff --git a/moderngl_window/context/base/keys.py b/moderngl_window/context/base/keys.py index c5e05b78..943a815c 100644 --- a/moderngl_window/context/base/keys.py +++ b/moderngl_window/context/base/keys.py @@ -1,6 +1,7 @@ # flake8: noqa E741 from typing import Any + class KeyModifiers: """Namespace for storing key modifiers""" @@ -12,9 +13,7 @@ def __repr__(self): return str(self) def __str__(self): - return "".format( - self.shift, self.ctrl, self.alt - ) + return "".format(self.shift, self.ctrl, self.alt) class BaseKeys: @@ -36,9 +35,9 @@ class BaseKeys: RIGHT: Any = "undefined" UP: Any = "undefined" DOWN: Any = "undefined" - LEFT_SHIFT: Any = "undefined" - RIGHT_SHIFT: Any = "undefined" - LEFT_CTRL: Any = "undefined" + LEFT_SHIFT: Any = "undefined" + RIGHT_SHIFT: Any = "undefined" + LEFT_CTRL: Any = "undefined" TAB: Any = "undefined" COMMA: Any = "undefined" diff --git a/moderngl_window/context/base/window.py b/moderngl_window/context/base/window.py index b5e4e3c8..ab79035e 100644 --- a/moderngl_window/context/base/window.py +++ b/moderngl_window/context/base/window.py @@ -89,7 +89,7 @@ def __init__( samples=0, cursor=True, backend: Optional[str] = None, - **kwargs + **kwargs, ): """Initialize a window instance. @@ -158,9 +158,7 @@ def __init__( self._resizable = False if not self.keys: - raise ValueError( - "Window class {} missing keys attribute".format(self.__class__) - ) + raise ValueError("Window class {} missing keys attribute".format(self.__class__)) def init_mgl_context(self) -> None: """ @@ -771,9 +769,7 @@ def _set_icon(self, icon_path: str) -> None: A library specific destroy method is required. """ raise NotImplementedError( - "Setting an icon is currently not supported by Window-type: {}".format( - self.name - ) + "Setting an icon is currently not supported by Window-type: {}".format(self.name) ) def _set_fullscreen(self, value: bool) -> None: @@ -781,16 +777,12 @@ def _set_fullscreen(self, value: bool) -> None: A library specific destroy method is required """ raise NotImplementedError( - "Toggling fullscreen is currently not supported by Window-type: {}".format( - self.name - ) + "Toggling fullscreen is currently not supported by Window-type: {}".format(self.name) ) def _set_vsync(self, value: bool) -> None: raise NotImplementedError( - "Toggling vsync is currently not supported by Window-type: {}".format( - self.name - ) + "Toggling vsync is currently not supported by Window-type: {}".format(self.name) ) def destroy(self) -> None: @@ -871,7 +863,10 @@ def _calc_mouse_delta(self, xpos: int, ypos: int) -> Tuple[int, int]: @property def on_generic_event_func(self): - """callable: Get or set the on_generic_event callable used to funnel all non-processed events""" + """ + callable: Get or set the on_generic_event callable + used to funnel all non-processed events + """ return self._mouse_position_event_func @on_generic_event_func.setter @@ -1064,7 +1059,7 @@ def __init__( ctx: moderngl.Context = None, wnd: BaseWindow = None, timer: BaseTimer = None, - **kwargs + **kwargs, ): """Initialize the window config @@ -1081,9 +1076,7 @@ def __init__( resources.register_dir(Path(self.resource_dir).resolve()) if not self.ctx or not isinstance(self.ctx, moderngl.Context): - raise ValueError( - "WindowConfig requires a moderngl context. ctx={}".format(self.ctx) - ) + raise ValueError("WindowConfig requires a moderngl context. ctx={}".format(self.ctx)) if not self.wnd or not isinstance(self.wnd, BaseWindow): raise ValueError("WindowConfig requires a window. wnd={}".format(self.wnd)) @@ -1100,18 +1093,12 @@ def assign_event_callbacks(self): self.wnd.close_func = getattr(self, "close", dummy_func) self.wnd.iconify_func = getattr(self, "iconify", dummy_func) self.wnd.key_event_func = getattr(self, "key_event", dummy_func) - self.wnd.mouse_position_event_func = getattr( - self, "mouse_position_event", dummy_func - ) + self.wnd.mouse_position_event_func = getattr(self, "mouse_position_event", dummy_func) self.wnd.mouse_press_event_func = getattr(self, "mouse_press_event", dummy_func) - self.wnd.mouse_release_event_func = getattr( - self, "mouse_release_event", dummy_func - ) + self.wnd.mouse_release_event_func = getattr(self, "mouse_release_event", dummy_func) self.wnd.mouse_drag_event_func = getattr(self, "mouse_drag_event", dummy_func) self.wnd.mouse_scroll_event_func = getattr(self, "mouse_scroll_event", dummy_func) - self.wnd.unicode_char_entered_func = getattr( - self, "unicode_char_entered", dummy_func - ) + self.wnd.unicode_char_entered_func = getattr(self, "unicode_char_entered", dummy_func) self.wnd.files_dropped_event_func = getattr(self, "files_dropped_event", dummy_func) @@ -1258,7 +1245,7 @@ def load_texture_2d( mipmap=False, mipmap_levels: Optional[Tuple[int, int]] = None, anisotropy=1.0, - **kwargs + **kwargs, ) -> moderngl.Texture: """Loads a 2D texture. @@ -1302,7 +1289,7 @@ def load_texture_array( mipmap=False, mipmap_levels: Optional[Tuple[int, int]] = None, anisotropy=1.0, - **kwargs + **kwargs, ) -> moderngl.TextureArray: """Loads a texture array. @@ -1357,7 +1344,7 @@ def load_texture_cube( mipmap=False, mipmap_levels: Optional[Tuple[int, int]] = None, anisotropy=1.0, - **kwargs + **kwargs, ) -> moderngl.TextureCube: """Loads a texture cube. @@ -1548,7 +1535,11 @@ def load_scene( """ return resources.scenes.load( SceneDescription( - path=path, cache=cache, attr_names=attr_names, kind=kind, **kwargs, + path=path, + cache=cache, + attr_names=attr_names, + kind=kind, + **kwargs, ) ) diff --git a/moderngl_window/context/glfw/window.py b/moderngl_window/context/glfw/window.py index 63076c60..2e871e00 100644 --- a/moderngl_window/context/glfw/window.py +++ b/moderngl_window/context/glfw/window.py @@ -47,9 +47,7 @@ def __init__(self, **kwargs): monitor = None - self._window = glfw.create_window( - self.width, self.height, self.title, monitor, None - ) + self._window = glfw.create_window(self.width, self.height, self.title, monitor, None) self._has_focus = True if self.fullscreen: @@ -61,9 +59,7 @@ def __init__(self, **kwargs): self.cursor = self._cursor - self._buffer_width, self._buffer_height = glfw.get_framebuffer_size( - self._window - ) + self._buffer_width, self._buffer_height = glfw.get_framebuffer_size(self._window) glfw.make_context_current(self._window) if self.vsync: @@ -118,7 +114,7 @@ def _set_fullscreen(self, value: bool) -> None: None, *self._non_fullscreen_position, *self._non_fullscreen_size, - refresh_rate + refresh_rate, ) if self.vsync: @@ -366,9 +362,7 @@ def glfw_window_resize_callback(self, window, width, height): height: New height """ self._width, self._height = width, height - self._buffer_width, self._buffer_height = glfw.get_framebuffer_size( - self._window - ) + self._buffer_width, self._buffer_height = glfw.get_framebuffer_size(self._window) self.set_default_viewport() super().resize(self._buffer_width, self._buffer_height) diff --git a/moderngl_window/context/pygame2/window.py b/moderngl_window/context/pygame2/window.py index 430e61c3..fd51a92e 100644 --- a/moderngl_window/context/pygame2/window.py +++ b/moderngl_window/context/pygame2/window.py @@ -29,12 +29,8 @@ def __init__(self, **kwargs): pygame.display.init() - pygame.display.gl_set_attribute( - pygame.GL_CONTEXT_MAJOR_VERSION, self.gl_version[0] - ) - pygame.display.gl_set_attribute( - pygame.GL_CONTEXT_MINOR_VERSION, self.gl_version[1] - ) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, self.gl_version[0]) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, self.gl_version[1]) pygame.display.gl_set_attribute( pygame.GL_CONTEXT_PROFILE_MASK, pygame.GL_CONTEXT_PROFILE_CORE ) @@ -71,7 +67,10 @@ def __init__(self, **kwargs): def _set_mode(self): self._surface = pygame.display.set_mode( - size=(self._width, self._height), flags=self._flags, depth=self._depth, vsync=self._vsync + size=(self._width, self._height), + flags=self._flags, + depth=self._depth, + vsync=self._vsync, ) def _set_fullscreen(self, value: bool) -> None: @@ -235,11 +234,17 @@ def process_events(self) -> None: self._handle_mods() if self.mouse_states.any: self._mouse_drag_event_func( - event.pos[0], event.pos[1], event.rel[0], event.rel[1], + event.pos[0], + event.pos[1], + event.rel[0], + event.rel[1], ) else: self._mouse_position_event_func( - event.pos[0], event.pos[1], event.rel[0], event.rel[1], + event.pos[0], + event.pos[1], + event.rel[0], + event.rel[1], ) elif event.type == pygame.MOUSEBUTTONDOWN: @@ -248,7 +253,9 @@ def process_events(self) -> None: if button is not None: self._handle_mouse_button_state_change(button, True) self._mouse_press_event_func( - event.pos[0], event.pos[1], button, + event.pos[0], + event.pos[1], + button, ) elif event.type == pygame.MOUSEBUTTONUP: @@ -257,7 +264,9 @@ def process_events(self) -> None: if button is not None: self._handle_mouse_button_state_change(button, False) self._mouse_release_event_func( - event.pos[0], event.pos[1], button, + event.pos[0], + event.pos[1], + button, ) elif event.type in [pygame.KEYDOWN, pygame.KEYUP]: @@ -267,7 +276,11 @@ def process_events(self) -> None: self.close() # Pygame can't do fullscreen yet, but this would toggle it. - if event.type == pygame.KEYUP and self._fs_key is not None and event.key == self._fs_key: + if ( + event.type == pygame.KEYUP + and self._fs_key is not None + and event.key == self._fs_key + ): self.fullscreen = not self.fullscreen if event.type == pygame.KEYDOWN: @@ -309,7 +322,7 @@ def process_events(self) -> None: # print("Window lost focus") # Window iconify state - if getattr(event, 'state', None) == 2: + if getattr(event, "state", None) == 2: if event.gain: self._iconify_func(False) else: diff --git a/moderngl_window/context/pyglet/window.py b/moderngl_window/context/pyglet/window.py index 9de801f2..d0eb04e9 100644 --- a/moderngl_window/context/pyglet/window.py +++ b/moderngl_window/context/pyglet/window.py @@ -62,7 +62,7 @@ def __init__(self, **kwargs): vsync=self._vsync, fullscreen=self._fullscreen, config=config, - file_drops=True and platform.system() != "Darwin" + file_drops=True and platform.system() != "Darwin", ) self.cursor = self._cursor @@ -303,7 +303,9 @@ def on_mouse_press(self, x: int, y: int, button, mods): if button is not None: self._handle_mouse_button_state_change(button, True) self._mouse_press_event_func( - x, self._height - y, button, + x, + self._height - y, + button, ) def on_mouse_release(self, x: int, y: int, button, mods): @@ -319,7 +321,9 @@ def on_mouse_release(self, x: int, y: int, button, mods): if button is not None: self._handle_mouse_button_state_change(button, False) self._mouse_release_event_func( - x, self._height - y, button, + x, + self._height - y, + button, ) def on_mouse_scroll(self, x, y, x_offset: float, y_offset: float): @@ -360,10 +364,10 @@ def on_hide(self): def on_file_drop(self, x, y, paths): """Called when files dropped onto the window - Args: - x (int): X location in window where file was dropped - y (int): Y location in window where file was dropped - paths (list): List of file paths dropped + Args: + x (int): X location in window where file was dropped + y (int): Y location in window where file was dropped + paths (list): List of file paths dropped """ # pyglet coordinate origin is in the bottom left corner of the window # mglw coordinate origin is in the top left corner of the window diff --git a/moderngl_window/context/pyqt5/window.py b/moderngl_window/context/pyqt5/window.py index 2ac5277c..00383b3f 100644 --- a/moderngl_window/context/pyqt5/window.py +++ b/moderngl_window/context/pyqt5/window.py @@ -65,7 +65,8 @@ def __init__(self, **kwargs): if self.resizable: # Ensure a valid resize policy when window is resizable size_policy = QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Expanding, ) self._widget.setSizePolicy(size_policy) self._widget.resize(self.width, self.height) diff --git a/moderngl_window/context/pyside2/window.py b/moderngl_window/context/pyside2/window.py index 4aa5cf59..2320570b 100644 --- a/moderngl_window/context/pyside2/window.py +++ b/moderngl_window/context/pyside2/window.py @@ -65,7 +65,8 @@ def __init__(self, **kwargs): if self.resizable: # Ensure a valid resize policy when window is resizable size_policy = QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Expanding, ) self._widget.setSizePolicy(size_policy) self._widget.resize(self.width, self.height) diff --git a/moderngl_window/context/sdl2/window.py b/moderngl_window/context/sdl2/window.py index 786e7446..87df8d07 100644 --- a/moderngl_window/context/sdl2/window.py +++ b/moderngl_window/context/sdl2/window.py @@ -30,12 +30,8 @@ def __init__(self, **kwargs): if sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) != 0: raise ValueError("Failed to initialize sdl2") - sdl2.video.SDL_GL_SetAttribute( - sdl2.SDL_GL_CONTEXT_MAJOR_VERSION, self.gl_version[0] - ) - sdl2.video.SDL_GL_SetAttribute( - sdl2.SDL_GL_CONTEXT_MINOR_VERSION, self.gl_version[1] - ) + sdl2.video.SDL_GL_SetAttribute(sdl2.SDL_GL_CONTEXT_MAJOR_VERSION, self.gl_version[0]) + sdl2.video.SDL_GL_SetAttribute(sdl2.SDL_GL_CONTEXT_MINOR_VERSION, self.gl_version[1]) sdl2.video.SDL_GL_SetAttribute( sdl2.SDL_GL_CONTEXT_PROFILE_MASK, sdl2.SDL_GL_CONTEXT_PROFILE_CORE ) @@ -260,7 +256,9 @@ def process_events(self) -> None: if button is not None: self._handle_mouse_button_state_change(button, True) self._mouse_press_event_func( - event.motion.x, event.motion.y, button, + event.motion.x, + event.motion.y, + button, ) elif event.type == sdl2.SDL_MOUSEBUTTONUP: @@ -269,19 +267,22 @@ def process_events(self) -> None: if button is not None: self._handle_mouse_button_state_change(button, False) self._mouse_release_event_func( - event.motion.x, event.motion.y, button, + event.motion.x, + event.motion.y, + button, ) elif event.type in [sdl2.SDL_KEYDOWN, sdl2.SDL_KEYUP]: self._handle_mods() - if ( - self._exit_key is not None - and event.key.keysym.sym == self._exit_key - ): + if self._exit_key is not None and event.key.keysym.sym == self._exit_key: self.close() - if self._fs_key is not None and event.key.keysym.sym == self._fs_key and event.type == sdl2.SDL_KEYDOWN: + if ( + self._fs_key is not None + and event.key.keysym.sym == self._fs_key + and event.type == sdl2.SDL_KEYDOWN + ): self.fullscreen = not self.fullscreen if event.type == sdl2.SDL_KEYDOWN: @@ -296,15 +297,16 @@ def process_events(self) -> None: elif event.type == sdl2.SDL_MOUSEWHEEL: self._handle_mods() - self._mouse_scroll_event_func( - float(event.wheel.x), float(event.wheel.y) - ) + self._mouse_scroll_event_func(float(event.wheel.x), float(event.wheel.y)) elif event.type == sdl2.SDL_QUIT: self.close() elif event.type == sdl2.SDL_WINDOWEVENT: - if event.window.event in [sdl2.SDL_WINDOWEVENT_RESIZED, sdl2.SDL_WINDOWEVENT_SIZE_CHANGED]: + if event.window.event in [ + sdl2.SDL_WINDOWEVENT_RESIZED, + sdl2.SDL_WINDOWEVENT_SIZE_CHANGED, + ]: self.resize(event.window.data1, event.window.data2) elif event.window.event == sdl2.SDL_WINDOWEVENT_MINIMIZED: self._iconify_func(True) diff --git a/moderngl_window/context/tk/window.py b/moderngl_window/context/tk/window.py index f45547f4..5cc437e5 100644 --- a/moderngl_window/context/tk/window.py +++ b/moderngl_window/context/tk/window.py @@ -22,9 +22,7 @@ def __init__(self, **kwargs): super().__init__(**kwargs) self._tk = tkinter.Tk() - self._gl_widget = ModernglTkWindow( - self._tk, width=self.width, height=self.height - ) + self._gl_widget = ModernglTkWindow(self._tk, width=self.width, height=self.height) self._gl_widget.pack(fill=tkinter.BOTH, expand=tkinter.YES) self._tk.resizable(self._resizable, self._resizable) diff --git a/moderngl_window/finders/base.py b/moderngl_window/finders/base.py index 6867b93e..9d8bb2af 100644 --- a/moderngl_window/finders/base.py +++ b/moderngl_window/finders/base.py @@ -1,6 +1,7 @@ """ Base finders """ + import functools import logging @@ -25,14 +26,11 @@ class BaseFilesystemFinder: """ def __init__(self): - """Initialize finder class by looking up the paths referenced in ``settings_attr``. - """ + """Initialize finder class by looking up the paths referenced in ``settings_attr``.""" if not hasattr(settings, self.settings_attr): raise ImproperlyConfigured( "Settings doesn't define {}. " - "This is required when using a FileSystemFinder.".format( - self.settings_attr - ) + "This is required when using a FileSystemFinder.".format(self.settings_attr) ) self.paths = getattr(settings, self.settings_attr) @@ -67,9 +65,7 @@ def find(self, path: Path) -> Path: # Keep ensuring all search paths are absolute if not search_path.is_absolute(): - raise ImproperlyConfigured( - "Search search path '{}' is not an absolute path" - ) + raise ImproperlyConfigured("Search search path '{}' is not an absolute path") abspath = search_path / path logger.debug("abspath %s", abspath) @@ -97,9 +93,7 @@ def get_finder(import_path: str): Finder = import_string(import_path) if not issubclass(Finder, BaseFilesystemFinder): raise ImproperlyConfigured( - "Finder {} is not a subclass of .finders.FileSystemFinder".format( - import_path - ) + "Finder {} is not a subclass of .finders.FileSystemFinder".format(import_path) ) return Finder() diff --git a/moderngl_window/geometry/attributes.py b/moderngl_window/geometry/attributes.py index 8abb627a..c2a00622 100644 --- a/moderngl_window/geometry/attributes.py +++ b/moderngl_window/geometry/attributes.py @@ -32,7 +32,7 @@ def __init__( color_0: str = None, joints_0: str = None, weights: str = None, - **kwargs + **kwargs, ): """Override default values. All attributes will be set on the instance as upper case strings diff --git a/moderngl_window/geometry/quad.py b/moderngl_window/geometry/quad.py index 8f2ebabc..8e5e6c61 100644 --- a/moderngl_window/geometry/quad.py +++ b/moderngl_window/geometry/quad.py @@ -18,7 +18,11 @@ def quad_fs(attr_names=AttributeNames, normals=True, uvs=True, name=None) -> VAO A :py:class:`~moderngl_window.opengl.vao.VAO` instance. """ return quad_2d( - size=(2.0, 2.0), normals=normals, uvs=uvs, attr_names=attr_names, name=name, + size=(2.0, 2.0), + normals=normals, + uvs=uvs, + attr_names=attr_names, + name=name, ) diff --git a/moderngl_window/integrations/imgui.py b/moderngl_window/integrations/imgui.py index 0497f846..8acbe83b 100644 --- a/moderngl_window/integrations/imgui.py +++ b/moderngl_window/integrations/imgui.py @@ -24,15 +24,8 @@ def key_event(self, key, action, modifiers): def _mouse_pos_viewport(self, x, y): """Make sure mouse coordinates are correct with black borders""" return ( - int( - x - - (self.wnd.width - self.wnd.viewport_width / self.wnd.pixel_ratio) / 2 - ), - int( - y - - (self.wnd.height - self.wnd.viewport_height / self.wnd.pixel_ratio) - / 2 - ), + int(x - (self.wnd.width - self.wnd.viewport_width / self.wnd.pixel_ratio) / 2), + int(y - (self.wnd.height - self.wnd.viewport_height / self.wnd.pixel_ratio) / 2), ) def mouse_position_event(self, x, y, dx, dy): @@ -221,9 +214,7 @@ def render(self, draw_data): ( "Texture {} is not registered. Please add to renderer using " "register_texture(..). " - "Current textures: {}".format( - command.texture_id, list(self._textures) - ) + "Current textures: {}".format(command.texture_id, list(self._textures)) ) ) @@ -231,9 +222,7 @@ def render(self, draw_data): x, y, z, w = command.clip_rect self.ctx.scissor = int(x), int(fb_height - w), int(z - x), int(w - y) - self._vao.render( - moderngl.TRIANGLES, vertices=command.elem_count, first=idx_pos - ) + self._vao.render(moderngl.TRIANGLES, vertices=command.elem_count, first=idx_pos) idx_pos += command.elem_count self.ctx.scissor = None diff --git a/moderngl_window/integrations/imgui_bundle.py b/moderngl_window/integrations/imgui_bundle.py index b97bfd95..3eda968d 100644 --- a/moderngl_window/integrations/imgui_bundle.py +++ b/moderngl_window/integrations/imgui_bundle.py @@ -20,15 +20,8 @@ def key_event(self, key, action, modifiers): def _mouse_pos_viewport(self, x, y): """Make sure mouse coordinates are correct with black borders""" return ( - int( - x - - (self.wnd.width - self.wnd.viewport_width / self.wnd.pixel_ratio) / 2 - ), - int( - y - - (self.wnd.height - self.wnd.viewport_height / self.wnd.pixel_ratio) - / 2 - ), + int(x - (self.wnd.width - self.wnd.viewport_width / self.wnd.pixel_ratio) / 2), + int(y - (self.wnd.height - self.wnd.viewport_height / self.wnd.pixel_ratio) / 2), ) def mouse_position_event(self, x, y, dx, dy): @@ -252,9 +245,7 @@ def render(self, draw_data: imgui.ImDrawData): ( "Texture {} is not registered. Please add to renderer using " "register_texture(..). " - "Current textures: {}".format( - command.texture_id, list(self._textures) - ) + "Current textures: {}".format(command.texture_id, list(self._textures)) ) ) @@ -262,9 +253,7 @@ def render(self, draw_data: imgui.ImDrawData): x, y, z, w = command.clip_rect self.ctx.scissor = int(x), int(fb_height - w), int(z - x), int(w - y) - self._vao.render( - moderngl.TRIANGLES, vertices=command.elem_count, first=idx_pos - ) + self._vao.render(moderngl.TRIANGLES, vertices=command.elem_count, first=idx_pos) idx_pos += command.elem_count self.ctx.scissor = None @@ -311,5 +300,5 @@ def _init_key_maps(self): keys.SPACE: imgui.Key.space, keys.BACKSPACE: imgui.Key.backspace, keys.ENTER: imgui.Key.enter, - keys.ESCAPE: imgui.Key.escape + keys.ESCAPE: imgui.Key.escape, } diff --git a/moderngl_window/loaders/base.py b/moderngl_window/loaders/base.py index 7ab49e63..c1ada3b0 100644 --- a/moderngl_window/loaders/base.py +++ b/moderngl_window/loaders/base.py @@ -11,6 +11,7 @@ class BaseLoader: """Base loader class for all resources""" + kind = "unknown" """ The kind of resource this loaded supports. diff --git a/moderngl_window/loaders/data/binary.py b/moderngl_window/loaders/data/binary.py index ae8a1a9c..9b66a6f1 100644 --- a/moderngl_window/loaders/data/binary.py +++ b/moderngl_window/loaders/data/binary.py @@ -18,9 +18,7 @@ def load(self) -> bytes: self.meta.resolved_path = self.find_data(self.meta.path) if not self.meta.resolved_path: - raise ImproperlyConfigured( - "Data file '{}' not found".format(self.meta.path) - ) + raise ImproperlyConfigured("Data file '{}' not found".format(self.meta.path)) logger.info("Loading: %s", self.meta.path) diff --git a/moderngl_window/loaders/data/json.py b/moderngl_window/loaders/data/json.py index 3a0d69c7..c8d11901 100644 --- a/moderngl_window/loaders/data/json.py +++ b/moderngl_window/loaders/data/json.py @@ -22,9 +22,7 @@ def load(self) -> dict: self.meta.resolved_path = self.find_data(self.meta.path) if not self.meta.resolved_path: - raise ImproperlyConfigured( - "Data file '{}' not found".format(self.meta.path) - ) + raise ImproperlyConfigured("Data file '{}' not found".format(self.meta.path)) logger.info("Loading: %s", self.meta.path) diff --git a/moderngl_window/loaders/data/text.py b/moderngl_window/loaders/data/text.py index 294baa1d..ea4a23e2 100644 --- a/moderngl_window/loaders/data/text.py +++ b/moderngl_window/loaders/data/text.py @@ -21,9 +21,7 @@ def load(self) -> str: self.meta.resolved_path = self.find_data(self.meta.path) if not self.meta.resolved_path: - raise ImproperlyConfigured( - "Data file '{}' not found".format(self.meta.path) - ) + raise ImproperlyConfigured("Data file '{}' not found".format(self.meta.path)) logger.info("Loading: %s", self.meta.path) diff --git a/moderngl_window/loaders/program/separate.py b/moderngl_window/loaders/program/separate.py index 667d8e98..19efa804 100644 --- a/moderngl_window/loaders/program/separate.py +++ b/moderngl_window/loaders/program/separate.py @@ -28,9 +28,7 @@ def load( geo_source = self._load_shader("geometry", self.meta.geometry_shader) fs_source = self._load_shader("fragment", self.meta.fragment_shader) tc_source = self._load_shader("tess_control", self.meta.tess_control_shader) - te_source = self._load_shader( - "tess_evaluation", self.meta.tess_evaluation_shader - ) + te_source = self._load_shader("tess_evaluation", self.meta.tess_evaluation_shader) cs_source = self._load_shader("compute", self.meta.compute_shader) if vs_source: @@ -65,9 +63,7 @@ def _load_shader(self, shader_type: str, path: str): if path: resolved_path = self.find_program(path) if not resolved_path: - raise ImproperlyConfigured( - "Cannot find {} shader '{}'".format(shader_type, path) - ) + raise ImproperlyConfigured("Cannot find {} shader '{}'".format(shader_type, path)) logger.info("Loading: %s", resolved_path) diff --git a/moderngl_window/loaders/scene/gltf2.py b/moderngl_window/loaders/scene/gltf2.py index f2379387..cc5d361f 100644 --- a/moderngl_window/loaders/scene/gltf2.py +++ b/moderngl_window/loaders/scene/gltf2.py @@ -144,9 +144,7 @@ def load_glb(self): magic = fd.read(4) if magic != GLTF_MAGIC_HEADER: raise ValueError( - "{} has incorrect header {} != {}".format( - self.path, magic, GLTF_MAGIC_HEADER - ) + "{} has incorrect header {} != {}".format(self.path, magic, GLTF_MAGIC_HEADER) ) version = struct.unpack(" Scene: vao = VAO("mesh", mode=moderngl.TRIANGLES) vao.buffer(numpy.array(stl_mesh.vertices, dtype="f4"), "3f", ["in_position"]) - vao.buffer( - numpy.array(stl_mesh.vertex_normals, dtype="f4"), "3f", ["in_normal"] - ) + vao.buffer(numpy.array(stl_mesh.vertex_normals, dtype="f4"), "3f", ["in_normal"]) vao.index_buffer(numpy.array(stl_mesh.faces, dtype="u4")) scene_mesh.vao = vao scene_mesh.add_attribute("POSITION", "in_position", 3) diff --git a/moderngl_window/loaders/scene/wavefront.py b/moderngl_window/loaders/scene/wavefront.py index ebf5ba82..52de1499 100644 --- a/moderngl_window/loaders/scene/wavefront.py +++ b/moderngl_window/loaders/scene/wavefront.py @@ -101,9 +101,7 @@ def load(self): VAOCacheLoader.attr_names = self.meta.attr_names - data = pywavefront.Wavefront( - str(path), create_materials=True, cache=self.meta.cache - ) + data = pywavefront.Wavefront(str(path), create_materials=True, cache=self.meta.cache) scene = Scene(self.meta.resolved_path) texture_cache = {} @@ -159,7 +157,8 @@ def load(self): texture_cache[rel_path] = texture mesh.material.mat_texture = MaterialTexture( - texture=texture, sampler=None, + texture=texture, + sampler=None, ) node = Node(mesh=mesh) diff --git a/moderngl_window/loaders/texture/array.py b/moderngl_window/loaders/texture/array.py index 59c2886d..6c566664 100644 --- a/moderngl_window/loaders/texture/array.py +++ b/moderngl_window/loaders/texture/array.py @@ -27,7 +27,11 @@ def load(self): ) components, data = image_data(self.image) - texture = self.ctx.texture_array((width, height, depth), components, data,) + texture = self.ctx.texture_array( + (width, height, depth), + components, + data, + ) texture.extra = {"meta": self.meta} if self.meta.mipmap_levels is not None: diff --git a/moderngl_window/loaders/texture/cube.py b/moderngl_window/loaders/texture/cube.py index 715079af..8605d5ff 100644 --- a/moderngl_window/loaders/texture/cube.py +++ b/moderngl_window/loaders/texture/cube.py @@ -59,9 +59,7 @@ def _load_face(self, path: str, face_name: str = None): image = self._load_texture(path) components, data = image_data(image) - return FaceInfo( - width=image.size[0], height=image.size[1], data=data, components=components - ) + return FaceInfo(width=image.size[0], height=image.size[1], data=data, components=components) def _validate(self, faces): """Validates each face ensuring components and size it the same""" @@ -73,8 +71,6 @@ def _validate(self, faces): "Cubemap face textures have different number of components" ) if len(face.data) != data_size: - raise ImproperlyConfigured( - "Cubemap face textures must all have the same size" - ) + raise ImproperlyConfigured("Cubemap face textures must all have the same size") return components diff --git a/moderngl_window/loaders/texture/icon.py b/moderngl_window/loaders/texture/icon.py index 263adeff..04320f4c 100644 --- a/moderngl_window/loaders/texture/icon.py +++ b/moderngl_window/loaders/texture/icon.py @@ -19,7 +19,5 @@ def find_icon(self): """ abs_path = self._find(Path(self.meta.path), texture.get_finders()) if abs_path is None: - raise ValueError( - "Could not find the icon specified. {}".format(self.meta.path) - ) + raise ValueError("Could not find the icon specified. {}".format(self.meta.path)) return abs_path diff --git a/moderngl_window/loaders/texture/pillow.py b/moderngl_window/loaders/texture/pillow.py index 60c6548a..52913588 100644 --- a/moderngl_window/loaders/texture/pillow.py +++ b/moderngl_window/loaders/texture/pillow.py @@ -31,9 +31,7 @@ def _open_image(self): self.meta.resolved_path = self.find_texture(self.meta.path) logger.info("loading %s", self.meta.resolved_path) if not self.meta.resolved_path: - raise ImproperlyConfigured( - "Cannot find texture: {}".format(self.meta.path) - ) + raise ImproperlyConfigured("Cannot find texture: {}".format(self.meta.path)) self.image = Image.open(self.meta.resolved_path) diff --git a/moderngl_window/loaders/texture/t2d.py b/moderngl_window/loaders/texture/t2d.py index 44c5555b..37783b79 100644 --- a/moderngl_window/loaders/texture/t2d.py +++ b/moderngl_window/loaders/texture/t2d.py @@ -18,7 +18,11 @@ def load(self): components, data = image_data(self.image) - texture = self.ctx.texture(self.image.size, components, data,) + texture = self.ctx.texture( + self.image.size, + components, + data, + ) texture.extra = {"meta": self.meta} if self.meta.mipmap_levels is not None: diff --git a/moderngl_window/meta/base.py b/moderngl_window/meta/base.py index 3a43502d..84c1185f 100644 --- a/moderngl_window/meta/base.py +++ b/moderngl_window/meta/base.py @@ -3,7 +3,7 @@ class ResourceDescription: - """ Description of any resource. + """Description of any resource. Resource descriptions are required to load a resource. This class can be extended to add more specific properties. """ diff --git a/moderngl_window/meta/data.py b/moderngl_window/meta/data.py index 751fa93c..b3a5ba00 100644 --- a/moderngl_window/meta/data.py +++ b/moderngl_window/meta/data.py @@ -37,7 +37,5 @@ def __init__(self, path=None, kind=None, **kwargs): kind (str): The resource kind deciding loader class **kwargs: Additional custom attributes """ - kwargs.update( - {"path": path, "kind": kind} - ) + kwargs.update({"path": path, "kind": kind}) super().__init__(**kwargs) diff --git a/moderngl_window/meta/program.py b/moderngl_window/meta/program.py index 5e12c482..70de57bf 100644 --- a/moderngl_window/meta/program.py +++ b/moderngl_window/meta/program.py @@ -40,7 +40,7 @@ def __init__( compute_shader: Optional[str] = None, defines: Optional[dict] = None, varyings: Optional[List] = None, - **kwargs + **kwargs, ): """Create a program description diff --git a/moderngl_window/meta/scene.py b/moderngl_window/meta/scene.py index 878c47c0..fe264e2f 100644 --- a/moderngl_window/meta/scene.py +++ b/moderngl_window/meta/scene.py @@ -31,9 +31,7 @@ class SceneDescription(ResourceDescription): default_kind = None resource_type = "scenes" - def __init__( - self, path=None, kind=None, cache=False, attr_names=AttributeNames, **kwargs - ): + def __init__(self, path=None, kind=None, cache=False, attr_names=AttributeNames, **kwargs): """Create a scene description. Keyword Args: @@ -46,9 +44,7 @@ def __init__( if attr_names is None: attr_names = AttributeNames - kwargs.update( - {"path": path, "kind": kind, "cache": cache, "attr_names": attr_names} - ) + kwargs.update({"path": path, "kind": kind, "cache": cache, "attr_names": attr_names}) super().__init__(**kwargs) @property diff --git a/moderngl_window/meta/texture.py b/moderngl_window/meta/texture.py index e4a3cfc2..5bb3003f 100644 --- a/moderngl_window/meta/texture.py +++ b/moderngl_window/meta/texture.py @@ -41,7 +41,7 @@ def __init__( neg_x: str = None, neg_y: str = None, neg_z: str = None, - **kwargs + **kwargs, ): """Describes a texture resource diff --git a/moderngl_window/opengl/program.py b/moderngl_window/opengl/program.py index bd220938..859ea391 100644 --- a/moderngl_window/opengl/program.py +++ b/moderngl_window/opengl/program.py @@ -1,6 +1,7 @@ """ Helper classes for loading shader """ + from typing import List, Tuple, Union, Optional import re @@ -132,9 +133,7 @@ def from_separate( return instance @classmethod - def compute_shader( - cls, meta: ProgramDescription, compute_shader_source: str = None - ): + def compute_shader(cls, meta: ProgramDescription, compute_shader_source: str = None): instance = cls(meta) instance.compute_shader_source = ShaderSource( COMPUTE_SHADER, @@ -168,18 +167,14 @@ def create(self): program = self.ctx.program( vertex_shader=self.vertex_source.source, - geometry_shader=self.geometry_source.source - if self.geometry_source - else None, - fragment_shader=self.fragment_source.source - if self.fragment_source - else None, - tess_control_shader=self.tess_control_source.source - if self.tess_control_source - else None, - tess_evaluation_shader=self.tess_evaluation_source.source - if self.tess_evaluation_source - else None, + geometry_shader=(self.geometry_source.source if self.geometry_source else None), + fragment_shader=(self.fragment_source.source if self.fragment_source else None), + tess_control_shader=( + self.tess_control_source.source if self.tess_control_source else None + ), + tess_evaluation_shader=( + self.tess_evaluation_source.source if self.tess_evaluation_source else None + ), varyings=out_attribs, ) program.extra = {"meta": self.meta} @@ -227,12 +222,17 @@ def __init__( """Create shader source. Args: - shader_type (str): A preprocessor name for setting the shader type - name (str): A string (usually the path) so we can give useful error messages to the user - source (str): The raw source for the shader + shader_type (str): + A preprocessor name for setting the shader type + name (str): + A string (usually the path) so we can give useful error messages to the user + source (str): + The raw source for the shader Keyword Args: - id (int): The source number. Used when shader consists of multiple sources through includes - root (bool): If this shader source is the root shader (Not an include) + id (int): + The source number. Used when shader consists of multiple sources through includes + root (bool): + If this shader source is the root shader (Not an include) """ self._id = id self._root = root @@ -315,7 +315,7 @@ def handle_includes(self, load_source_func, depth=0, source_id=0): for nr, line in enumerate(self._lines): line = line.strip() if line.startswith("#include"): - path = re.search(r'#include\s+"?([^"]+)',line)[1] + path = re.search(r'#include\s+"?([^"]+)', line)[1] current_id += 1 _, source = load_source_func(path) source = ShaderSource( @@ -326,10 +326,8 @@ def handle_includes(self, load_source_func, depth=0, source_id=0): id=current_id, root=False, ) - source.handle_includes( - load_source_func, depth=depth + 1, source_id=current_id - ) - self._lines = self.lines[:nr] + source.lines + self.lines[nr + 1:] + source.handle_includes(load_source_func, depth=depth + 1, source_id=current_id) + self._lines = self.lines[:nr] + source.lines + self.lines[nr + 1 :] self._source_list += source.source_list current_id = self._source_list[-1].id break @@ -363,9 +361,7 @@ def find_out_attribs(self) -> List[str]: """ names = [] for line in self.lines: - res = re.match( - r"(layout(.+)\))?(\s+)?(out)(\s+)(\w+)(\s+)(\w+)", line.strip() - ) + res = re.match(r"(layout(.+)\))?(\s+)?(out)(\s+)(\w+)(\s+)(\w+)", line.strip()) if res: names.append(res.groups()[-1]) @@ -416,9 +412,7 @@ def _members(self): def ctx(self) -> moderngl.Context: return self.program.ctx - def __getitem__( - self, key - ) -> Union[ + def __getitem__(self, key) -> Union[ moderngl.Uniform, moderngl.UniformBlock, moderngl.Subroutine, @@ -450,7 +444,7 @@ def glo(self) -> int: @property def subroutines(self) -> Tuple[str, ...]: """ - tuple: The subroutine uniforms. + tuple: The subroutine uniforms. """ return self.program.subroutines diff --git a/moderngl_window/opengl/types.py b/moderngl_window/opengl/types.py index 652d2ca8..55bd6ff2 100644 --- a/moderngl_window/opengl/types.py +++ b/moderngl_window/opengl/types.py @@ -13,6 +13,7 @@ (self.pos_scale_buffer, '2f 1f/i', 'in_pos', 'in_scale'), ] """ + import re from functools import lru_cache from typing import List @@ -91,7 +92,7 @@ def attribute_format(attr_format: str) -> BufferFormat: components = 1 if parts[0].isalnum(): components = int(parts[0]) - bformat = fmt[len(parts[0]):] + bformat = fmt[len(parts[0]) :] else: bformat = fmt @@ -121,9 +122,7 @@ def buffer_format(frmt: str) -> BufferFormat: return BUFFER_FORMATS[frmt] except KeyError: raise ValueError( - "Buffer format '{}' unknown. Valid formats: {}".format( - frmt, BUFFER_FORMATS.keys() - ) + "Buffer format '{}' unknown. Valid formats: {}".format(frmt, BUFFER_FORMATS.keys()) ) diff --git a/moderngl_window/opengl/vao.py b/moderngl_window/opengl/vao.py index d6a7e7ce..84fabd17 100644 --- a/moderngl_window/opengl/vao.py +++ b/moderngl_window/opengl/vao.py @@ -137,9 +137,7 @@ def __init__(self, name="", mode=moderngl.TRIANGLES): try: DRAW_MODES[self.mode] except KeyError: - raise VAOError( - "Invalid draw mode. Options are {}".format(DRAW_MODES.values()) - ) + raise VAOError("Invalid draw mode. Options are {}".format(DRAW_MODES.values())) self._buffers = [] self._index_buffer = None @@ -153,9 +151,7 @@ def ctx(self): """moderngl.Context: The actite moderngl context""" return mglw.ctx() - def render( - self, program: moderngl.Program, mode=None, vertices=-1, first=0, instances=1 - ): + def render(self, program: moderngl.Program, mode=None, vertices=-1, first=0, instances=1): """Render the VAO. An internal ``moderngl.VertexBuffer`` with compatible buffer bindings @@ -176,11 +172,13 @@ def render( vao.render(mode, vertices=vertices, first=first, instances=instances) - def render_indirect( - self, program: moderngl.Program, buffer, mode=None, count=-1, *, first=0 - ): - """The render primitive (mode) must be the same as the input primitive of the GeometryShader. - The draw commands are 5 integers: (count, instanceCount, firstIndex, baseVertex, baseInstance). + def render_indirect(self, program: moderngl.Program, buffer, mode=None, count=-1, *, first=0): + """ + The render primitive (mode) must be the same as the input primitive of the + GeometryShader. + + The draw commands are 5 integers: + (count, instanceCount, firstIndex, baseVertex, baseInstance). Args: program: The ``moderngl.Program`` @@ -222,27 +220,29 @@ def transform( if mode is None: mode = self.mode - vao.transform( - buffer, mode=mode, vertices=vertices, first=first, instances=instances - ) + vao.transform(buffer, mode=mode, vertices=vertices, first=first, instances=instances) def buffer(self, buffer, buffer_format: str, attribute_names: List[str]): """Register a buffer/vbo for the VAO. This can be called multiple times. adding multiple buffers (interleaved or not). Args: - buffer: The buffer data. Can be ``numpy.array``, ``moderngl.Buffer`` or ``bytes``. - buffer_format (str): The format of the buffer. (eg. ``3f 3f`` for interleaved positions and normals). - attribute_names: A list of attribute names this buffer should map to. + buffer: + The buffer data. Can be ``numpy.array``, ``moderngl.Buffer`` or ``bytes``. + buffer_format (str): + The format of the buffer. (eg. ``3f 3f`` for interleaved positions and normals). + attribute_names: + A list of attribute names this buffer should map to. Returns: - The ``moderngl.Buffer`` instance object. This is handy when providing ``bytes`` and ``numpy.array``. + The ``moderngl.Buffer`` instance object. This is handy when providing ``bytes`` + and ``numpy.array``. """ if not isinstance(attribute_names, list): attribute_names = [ attribute_names, ] - if not type(buffer) in [moderngl.Buffer, numpy.ndarray, bytes]: + if type(buffer) not in [moderngl.Buffer, numpy.ndarray, bytes]: raise VAOError( ( "buffer parameter must be a moderngl.Buffer, numpy.ndarray or bytes instance" @@ -259,9 +259,7 @@ def buffer(self, buffer, buffer_format: str, attribute_names: List[str]): formats = buffer_format.split() if len(formats) != len(attribute_names): raise VAOError( - "Format '{}' does not describe attributes {}".format( - buffer_format, attribute_names - ) + "Format '{}' does not describe attributes {}".format(buffer_format, attribute_names) ) self._buffers.append(BufferInfo(buffer, buffer_format, attribute_names)) @@ -277,7 +275,7 @@ def index_buffer(self, buffer, index_element_size=4): Keyword Args: index_element_size (int): Byte size of each element. 1, 2 or 4 """ - if not type(buffer) in [moderngl.Buffer, numpy.ndarray, bytes]: + if type(buffer) not in [moderngl.Buffer, numpy.ndarray, bytes]: raise VAOError( "buffer parameter must be a moderngl.Buffer, numpy.ndarray or bytes instance" ) @@ -342,15 +340,16 @@ def instance(self, program: moderngl.Program) -> moderngl.VertexArray: # Any attribute left is not accounted for if program_attributes: raise VAOError( - "Did not find a buffer mapping for {}".format( - [n for n in program_attributes] - ) + "Did not find a buffer mapping for {}".format([n for n in program_attributes]) ) # Create the vao if self._index_buffer: vao = self.ctx.vertex_array( - program, vao_content, self._index_buffer, self._index_element_size, + program, + vao_content, + self._index_buffer, + self._index_element_size, ) else: vao = self.ctx.vertex_array(program, vao_content) diff --git a/moderngl_window/resources/base.py b/moderngl_window/resources/base.py index 99671811..7a6d6622 100644 --- a/moderngl_window/resources/base.py +++ b/moderngl_window/resources/base.py @@ -1,6 +1,7 @@ """ Base registry class """ + import inspect from functools import lru_cache from typing import Any, Generator, Tuple @@ -84,7 +85,8 @@ def resolve_loader(self, meta: ResourceDescription) -> None: Attempts to assign a loader class to a ResourceDescription. Args: - meta (:py:class:`~moderngl_window.meta.base.ResourceDescription`): The resource description instance + meta (:py:class:`~moderngl_window.meta.base.ResourceDescription`): + The resource description instance """ # Get loader using kind if specified if meta.kind: diff --git a/moderngl_window/resources/data.py b/moderngl_window/resources/data.py index e6ce056f..95b6a3b9 100644 --- a/moderngl_window/resources/data.py +++ b/moderngl_window/resources/data.py @@ -1,6 +1,7 @@ """ Registry general data files """ + from typing import Any from moderngl_window.resources.base import BaseRegistry from moderngl_window.meta import DataDescription diff --git a/moderngl_window/resources/programs.py b/moderngl_window/resources/programs.py index f1143277..852adc76 100644 --- a/moderngl_window/resources/programs.py +++ b/moderngl_window/resources/programs.py @@ -26,7 +26,8 @@ def load(self, meta: ProgramDescription) -> moderngl.Program: """Loads a shader program with the configured loaders Args: - meta (:py:class:`~moderngl_window.meta.program.ProgramDescription`): The resource description + meta (:py:class:`~moderngl_window.meta.program.ProgramDescription`): + The resource description Returns: moderngl.Program: The shader program """ diff --git a/moderngl_window/resources/scenes.py b/moderngl_window/resources/scenes.py index b2b847fc..942dcae8 100644 --- a/moderngl_window/resources/scenes.py +++ b/moderngl_window/resources/scenes.py @@ -1,6 +1,7 @@ """ Scene Registry """ + from moderngl_window.resources.base import BaseRegistry from moderngl_window.scene import Scene from moderngl_window.meta import SceneDescription @@ -15,7 +16,8 @@ def load(self, meta: SceneDescription) -> Scene: """Load a scene with the configured loaders. Args: - meta (:py:class:`~moderngl_window.meta.scene.SceneDescription`): The resource description + meta (:py:class:`~moderngl_window.meta.scene.SceneDescription`): + The resource description Returns: :py:class:`~moderngl_window.scene.Scene`: The loaded scene """ diff --git a/moderngl_window/resources/textures.py b/moderngl_window/resources/textures.py index 518c6144..39e2f2eb 100644 --- a/moderngl_window/resources/textures.py +++ b/moderngl_window/resources/textures.py @@ -1,6 +1,7 @@ """ Shader Registry """ + from typing import Union import moderngl from moderngl_window.resources.base import BaseRegistry @@ -12,13 +13,17 @@ class Textures(BaseRegistry): settings_attr = "TEXTURE_LOADERS" - def load( - self, meta: TextureDescription - ) -> Union[moderngl.Texture, moderngl.TextureArray, moderngl.TextureCube, moderngl.Texture3D]: + def load(self, meta: TextureDescription) -> Union[ + moderngl.Texture, + moderngl.TextureArray, + moderngl.TextureCube, + moderngl.Texture3D, + ]: """Loads a texture with the configured loaders. Args: - meta (:py:class:`~moderngl_window.meta.texture.TextureDescription`): The resource description + meta (:py:class:`~moderngl_window.meta.texture.TextureDescription`): + The resource description Returns: moderngl.Texture: 2d texture Returns: diff --git a/moderngl_window/resources/tracks.py b/moderngl_window/resources/tracks.py index f343f855..d997fbf5 100644 --- a/moderngl_window/resources/tracks.py +++ b/moderngl_window/resources/tracks.py @@ -1,6 +1,7 @@ """ Registry for rocket tracks """ + from rocket.tracks import Track diff --git a/moderngl_window/scene/camera.py b/moderngl_window/scene/camera.py index 79fd4636..b6b77732 100644 --- a/moderngl_window/scene/camera.py +++ b/moderngl_window/scene/camera.py @@ -205,7 +205,15 @@ class KeyboardCamera(Camera): camera.projection.tobytes() """ - def __init__(self, keys: BaseKeys, keymap: KeyMapFactory = QWERTY, fov=60.0, aspect_ratio=1.0, near=1.0, far=100.0): + def __init__( + self, + keys: BaseKeys, + keymap: KeyMapFactory = QWERTY, + fov=60.0, + aspect_ratio=1.0, + near=1.0, + far=100.0, + ): """Initialize the camera Args: @@ -446,8 +454,8 @@ def matrix(self) -> numpy.ndarray: class OrbitCamera(Camera): """Camera controlled by the mouse to pan around the target. - The functions :py:function:`~camera.OrbitCamera.rot_state` and :py:function:`~camera.OrbitCamera.rot_state` - are used to update the rotation and zoom. + The functions :py:function:`~camera.OrbitCamera.rot_state` and + :py:function:`~camera.OrbitCamera.rot_state` are used to update the rotation and zoom. Creating a orbit camera: @@ -474,9 +482,7 @@ class OrbitCamera(Camera): camera.projection.tobytes() """ - def __init__( - self, target=(0.0, 0.0, 0.0), radius=2.0, angles=(45.0, -45.0), **kwargs - ): + def __init__(self, target=(0.0, 0.0, 0.0), radius=2.0, angles=(45.0, -45.0), **kwargs): """Initialize the camera Keyword Args: diff --git a/moderngl_window/scene/material.py b/moderngl_window/scene/material.py index 0f36eb66..10484692 100644 --- a/moderngl_window/scene/material.py +++ b/moderngl_window/scene/material.py @@ -7,9 +7,7 @@ class MaterialTexture: Contains a texture and a sampler object. """ - def __init__( - self, texture: moderngl.Texture = None, sampler: moderngl.Sampler = None - ): + def __init__(self, texture: moderngl.Texture = None, sampler: moderngl.Sampler = None): """Initialize instance. Args: diff --git a/moderngl_window/scene/mesh.py b/moderngl_window/scene/mesh.py index b53599a6..5e823ff7 100644 --- a/moderngl_window/scene/mesh.py +++ b/moderngl_window/scene/mesh.py @@ -40,9 +40,7 @@ def __init__( self.bbox_max = bbox_max self.mesh_program = None - def draw( - self, projection_matrix=None, model_matrix=None, camera_matrix=None, time=0.0 - ): + def draw(self, projection_matrix=None, model_matrix=None, camera_matrix=None, time=0.0): """Draw the mesh using the assigned mesh program Keyword Args: @@ -79,9 +77,9 @@ def draw_bbox(self, proj_matrix, model_matrix, cam_matrix, program, vao): def draw_wireframe(self, proj_matrix, model_matrix, program): """Render the mesh as wireframe. - proj_matrix: Projection matrix - model_matrix: View/model matrix - program: The moderngl.Program rendering the wireframe + proj_matrix: Projection matrix + model_matrix: View/model matrix + program: The moderngl.Program rendering the wireframe """ program["m_proj"].write(proj_matrix) program["m_model"].write(model_matrix) diff --git a/moderngl_window/scene/node.py b/moderngl_window/scene/node.py index 19940b37..9174b5d7 100644 --- a/moderngl_window/scene/node.py +++ b/moderngl_window/scene/node.py @@ -126,7 +126,11 @@ def draw(self, projection_matrix: glm.mat4, camera_matrix: glm.mat4, time=0): ) def draw_bbox( - self, projection_matrix: glm.mat4, camera_matrix: glm.mat4, program: mod, vao + self, + projection_matrix: glm.mat4, + camera_matrix: glm.mat4, + program: moderngl.Program, + vao, ): """Draw bounding box around the node and children. @@ -170,9 +174,7 @@ def calc_global_bbox(self, view_matrix: glm.mat4, bbox_min, bbox_max) -> tuple: view_matrix = self._matrix * view_matrix if self._mesh: - bbox_min, bbox_max = self._mesh.calc_global_bbox( - view_matrix, bbox_min, bbox_max - ) + bbox_min, bbox_max = self._mesh.calc_global_bbox(view_matrix, bbox_min, bbox_max) for child in self._children: bbox_min, bbox_max = child.calc_global_bbox(view_matrix, bbox_min, bbox_max) diff --git a/moderngl_window/scene/programs.py b/moderngl_window/scene/programs.py index 75aeed7c..0f5317b4 100644 --- a/moderngl_window/scene/programs.py +++ b/moderngl_window/scene/programs.py @@ -75,9 +75,7 @@ class VertexColorProgram(MeshProgram): def __init__(self, program=None, **kwargs) -> None: super().__init__(program=None) - self.program = programs.load( - ProgramDescription(path="scene_default/vertex_color.glsl") - ) + self.program = programs.load(ProgramDescription(path="scene_default/vertex_color.glsl")) def draw( self, @@ -110,9 +108,7 @@ class ColorLightProgram(MeshProgram): def __init__(self, program=None, **kwargs) -> None: super().__init__(program=None) - self.program = programs.load( - ProgramDescription(path="scene_default/color_light.glsl") - ) + self.program = programs.load(ProgramDescription(path="scene_default/color_light.glsl")) def draw( self, @@ -153,9 +149,7 @@ class TextureProgram(MeshProgram): def __init__(self, program=None, **kwargs) -> None: super().__init__(program=None) - self.program = programs.load( - ProgramDescription(path="scene_default/texture.glsl") - ) + self.program = programs.load(ProgramDescription(path="scene_default/texture.glsl")) def draw( self, @@ -239,9 +233,7 @@ class TextureLightProgram(MeshProgram): def __init__(self, program: moderngl.Program | None = None, **kwargs) -> None: super().__init__(program=None) - self.program = programs.load( - ProgramDescription(path="scene_default/texture_light.glsl") - ) + self.program = programs.load(ProgramDescription(path="scene_default/texture_light.glsl")) def draw( self, @@ -290,9 +282,7 @@ class FallbackProgram(MeshProgram): def __init__(self, program: moderngl.Program | None = None, **kwargs) -> None: super().__init__(program=None) - self.program = programs.load( - ProgramDescription(path="scene_default/fallback.glsl") - ) + self.program = programs.load(ProgramDescription(path="scene_default/fallback.glsl")) def draw( self, diff --git a/moderngl_window/scene/scene.py b/moderngl_window/scene/scene.py index dafed522..4e33a87a 100644 --- a/moderngl_window/scene/scene.py +++ b/moderngl_window/scene/scene.py @@ -1,6 +1,7 @@ """ Wrapper for a loaded scene with properties. """ + from typing import TYPE_CHECKING import logging import numpy @@ -146,9 +147,7 @@ def draw_bbox( # Draw bounding box for children for node in self.root_nodes: - node.draw_bbox( - projection_matrix, camera_matrix, self.bbox_program, self.bbox_vao - ) + node.draw_bbox(projection_matrix, camera_matrix, self.bbox_program, self.bbox_vao) def draw_wireframe( self, projection_matrix=None, camera_matrix=None, color=(0.75, 0.75, 0.75, 1.0) @@ -173,9 +172,7 @@ def draw_wireframe( self.ctx.wireframe = True for node in self.root_nodes: - node.draw_wireframe( - projection_matrix, camera_matrix, self.wireframe_program - ) + node.draw_wireframe(projection_matrix, camera_matrix, self.wireframe_program) self.ctx.wireframe = False @@ -227,9 +224,7 @@ def calc_scene_bbox(self) -> None: """Calculate scene bbox""" bbox_min, bbox_max = None, None for node in self.root_nodes: - bbox_min, bbox_max = node.calc_global_bbox( - glm.mat4(), bbox_min, bbox_max - ) + bbox_min, bbox_max = node.calc_global_bbox(glm.mat4(), bbox_min, bbox_max) self.bbox_min = bbox_min self.bbox_max = bbox_max diff --git a/moderngl_window/screenshot.py b/moderngl_window/screenshot.py index d31d1c25..ab542c66 100644 --- a/moderngl_window/screenshot.py +++ b/moderngl_window/screenshot.py @@ -34,18 +34,14 @@ def create( dest = "" if settings.SCREENSHOT_PATH: if not os.path.exists(str(settings.SCREENSHOT_PATH)): - logger.debug( - "SCREENSHOT_PATH does not exist. creating: %s", settings.SCREENSHOT_PATH - ) + logger.debug("SCREENSHOT_PATH does not exist. creating: %s", settings.SCREENSHOT_PATH) os.makedirs(str(settings.SCREENSHOT_PATH)) dest = settings.SCREENSHOT_PATH else: logger.info("SCREENSHOT_PATH not defined in settings. Using cwd as fallback.") if not name: - name = "{}.{}".format( - datetime.now().strftime("%Y-%m-%d-%H-%M-%S-%f"), file_format - ) + name = "{}.{}".format(datetime.now().strftime("%Y-%m-%d-%H-%M-%S-%f"), file_format) logger.debug( "Creating screenshot: source=%s file_format=%s name=%s mode=%s alignment=%s", @@ -70,9 +66,7 @@ def create( TEXTURE_MODES[source.components], source.size, source.read(alignment=1) ) else: - raise ValueError( - "Source needs to be a FrameBuffer or Texture, not a %s", type(source) - ) + raise ValueError("Source needs to be a FrameBuffer or Texture, not a %s", type(source)) image = image.transpose(Image.FLIP_TOP_BOTTOM) dest = os.path.join(str(dest), name) diff --git a/moderngl_window/text/bitmapped/base.py b/moderngl_window/text/bitmapped/base.py index 3d824940..14d98b63 100644 --- a/moderngl_window/text/bitmapped/base.py +++ b/moderngl_window/text/bitmapped/base.py @@ -1,4 +1,3 @@ - import moderngl_window @@ -7,12 +6,12 @@ class FontMeta: def __init__(self, meta): self._meta = meta - self.characters = self._meta['characters'] - self.character_ranges = self._meta['character_ranges'] - self.character_height = self._meta['character_height'] - self.character_width = self._meta['character_width'] - self.atlas_height = self._meta['atlas_height'] - self.atlas_width = self._meta['atlas_width'] + self.characters = self._meta["characters"] + self.character_ranges = self._meta["character_ranges"] + self.character_height = self._meta["character_height"] + self.character_width = self._meta["character_width"] + self.atlas_height = self._meta["atlas_height"] + self.atlas_width = self._meta["atlas_width"] @property def char_aspect_wh(self): @@ -35,7 +34,7 @@ def draw(self, *args, **kwargs): def _translate_string(self, data): """Translate string into character texture positions""" - data = data.encode('iso-8859-1', errors='replace') + data = data.encode("iso-8859-1", errors="replace") for index, char in enumerate(data): yield self._meta.characters - 1 - self._ct[char] @@ -53,6 +52,6 @@ def _generate_character_map(self): self._ct = [-1] * 256 index = 0 for c_range in self._meta.character_ranges: - for c_pos in range(c_range['min'], c_range['max'] + 1): + for c_pos in range(c_range["min"], c_range["max"] + 1): self._ct[c_pos] = index index += 1 diff --git a/moderngl_window/text/bitmapped/text_2d.py b/moderngl_window/text/bitmapped/text_2d.py index 41b69c5f..87bf0510 100644 --- a/moderngl_window/text/bitmapped/text_2d.py +++ b/moderngl_window/text/bitmapped/text_2d.py @@ -23,9 +23,7 @@ class TextWriter2D(BaseText): def __init__(self): super().__init__() - meta = FontMeta( - resources.data.load(DataDescription(path="bitmapped/text/meta.json")) - ) + meta = FontMeta(resources.data.load(DataDescription(path="bitmapped/text/meta.json"))) self._texture = resources.textures.load( TextureDescription( path="bitmapped/textures/VeraMono.png", diff --git a/moderngl_window/utils/keymaps.py b/moderngl_window/utils/keymaps.py index 357737df..c9df315b 100644 --- a/moderngl_window/utils/keymaps.py +++ b/moderngl_window/utils/keymaps.py @@ -9,9 +9,9 @@ # induced by the window software used. # Therefore, the factory takes as a parameter the keys used, and return a keymap instance. KeyMapFactory = Callable[[BaseKeys], KeyMap] -AZERTY: KeyMapFactory = lambda keys: KeyMap( +AZERTY: KeyMapFactory = lambda keys: KeyMap( # noqa UP=keys.A, DOWN=keys.E, LEFT=keys.Q, RIGHT=keys.D, FORWARD=keys.Z, BACKWARD=keys.S ) -QWERTY: KeyMapFactory = lambda keys: KeyMap( +QWERTY: KeyMapFactory = lambda keys: KeyMap( # noqa UP=keys.Q, DOWN=keys.E, LEFT=keys.A, RIGHT=keys.D, FORWARD=keys.W, BACKWARD=keys.S ) diff --git a/moderngl_window/utils/module_loading.py b/moderngl_window/utils/module_loading.py index 7f1d82a3..a5bdf8f7 100644 --- a/moderngl_window/utils/module_loading.py +++ b/moderngl_window/utils/module_loading.py @@ -23,6 +23,5 @@ def import_string(dotted_path): return getattr(module, class_name) except AttributeError as err: raise ImportError( - 'Module "%s" does not define a "%s" attribute/class' - % (module_path, class_name) + 'Module "%s" does not define a "%s" attribute/class' % (module_path, class_name) ) from err diff --git a/moderngl_window/utils/scheduler.py b/moderngl_window/utils/scheduler.py index ec840bda..2014c52f 100644 --- a/moderngl_window/utils/scheduler.py +++ b/moderngl_window/utils/scheduler.py @@ -15,9 +15,7 @@ def __init__(self, timer: BaseTimer): """ if not isinstance(timer, BaseTimer): raise ValueError( - "timer, {}, has to be a instance of BaseTimer or a callable!".format( - timer - ) + "timer, {}, has to be a instance of BaseTimer or a callable!".format(timer) ) self._events = dict() @@ -31,11 +29,16 @@ def run_once( """Schedule a function for execution after a delay. Args: - action (callable): function to be called. - delay (float): delay in seconds. - priority (int, optional): priority for this event, lower is more important. Defaults to 1. - arguments (tuple, optional): arguments for the action. Defaults to (). - kwargs (dict, optional): keyword arguments for the action. Defaults to dict(). + action (callable): + function to be called. + delay (float): + delay in seconds. + priority (int, optional): + priority for this event, lower is more important. Defaults to 1. + arguments (tuple, optional): + arguments for the action. Defaults to (). + kwargs (dict, optional): + keyword arguments for the action. Defaults to dict(). Returns: int: event id that can be canceled. @@ -45,17 +48,20 @@ def run_once( self._event_id += 1 return self._event_id - 1 - def run_at( - self, action, time: float, *, priority: int = 1, arguments=(), kwargs=dict() - ) -> int: + def run_at(self, action, time: float, *, priority: int = 1, arguments=(), kwargs=dict()) -> int: """Schedule a function to be executed at a certain time. Args: - action (callable): function to be called. - time (float): epoch time at which the function should be called. - priority (int, optional): priority for this event, lower is more important. Defaults to 1. - arguments (tuple, optional): arguments for the action. Defaults to (). - kwargs (dict, optional): keyword arguments for the action. Defaults to dict(). + action (callable): + function to be called. + time (float): + epoch time at which the function should be called. + priority (int, optional): + priority for this event, lower is more important. Defaults to 1. + arguments (tuple, optional): + arguments for the action. Defaults to (). + kwargs (dict, optional): + keyword arguments for the action. Defaults to dict(). Returns: int: event id that can be canceled. @@ -73,17 +79,23 @@ def run_every( priority: int = 1, initial_delay: float = 0.0, arguments=(), - kwargs=dict() + kwargs=dict(), ) -> int: """Schedule a recurring function to be called every `delay` seconds after a initial delay. Args: - action (callable): function to be called. - delay (float): delay in seconds. - priority (int, optional): priority for this event, lower is more important. Defaults to 1. - initial_delay (float, optional): initial delay in seconds before executing for the first time. - Defaults to 0. arguments (tuple, optional): arguments for the action. Defaults to (). - kwargs (dict, optional): keyword arguments for the action. Defaults to dict(). + action (callable): + function to be called. + delay (float): + delay in seconds. + priority (int, optional): + priority for this event, lower is more important. Defaults to 1. + initial_delay (float, optional): + initial delay in seconds before executing for the first time. + Defaults to 0. arguments (tuple, optional): + arguments for the action. Defaults to (). + kwargs (dict, optional): + keyword arguments for the action. Defaults to dict(). Returns: int: event id that can be canceled. @@ -96,9 +108,7 @@ def run_every( self._event_id += 1 return self._event_id - 1 - def _recurring_event_factory( - self, function, arguments, kwargs, scheduling_info, id - ): + def _recurring_event_factory(self, function, arguments, kwargs, scheduling_info, id): """Factory for creating recurring events that will reschedule themselves. Args: @@ -117,8 +127,7 @@ def _f(): return _f def execute(self) -> None: - """Run the scheduler without blocking and execute any expired events. - """ + """Run the scheduler without blocking and execute any expired events.""" self._scheduler.run(blocking=False) def cancel(self, event_id: int, delay: float = 0) -> None: @@ -135,8 +144,6 @@ def cancel(self, event_id: int, delay: float = 0) -> None: def _cancel(self, event_id: int): if event_id not in self._events: - raise ValueError( - "Recurring event with id {} does not exist".format(event_id) - ) + raise ValueError("Recurring event with id {} does not exist".format(event_id)) event = self._events.pop(event_id) self._scheduler.cancel(event) diff --git a/pyproject.toml b/pyproject.toml index df6b456c..053d50a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dev = [ "trimesh", "scipy", "build", - "flake8", + "ruff", ] docs = ["Sphinx~=7.2.6 ", "sphinx-rtd-theme~=1.3.0 ", "doc8"] imgui = ["imgui-bundle"] # 1.6.0 originally @@ -68,18 +68,38 @@ source = ["moderngl_window"] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" -[tool.flake8] -ignore = ["W503", "C901"] -max-line-length = 120 -max-complexity = 10 -exclude = ["tests/", "docs/", "temp/"] - [tool.ruff] +exclude = [ + "docs", + "tests", + "moderngl_window/context/tk/keys.py", + "moderngl_window/context/sdl2/keys.py", + "moderngl_window/context/pyside2/keys.py", + "moderngl_window/context/pyqt5/keys.py", + "moderngl_window/context/pyglet/keys.py", + "moderngl_window/context/pygame2/keys.py", + "moderngl_window/context/glfw/keys.py", + "moderngl_window/context/base/keys.py", +] line-length = 100 output-format = "full" +# lint.ignore = ["E731", "E741"] +lint.select = [ + "E", + "F", + # Whitespace linting must be re-enabled manually for ruff + # see https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml + "W", +] [tool.black] line-length = 100 +force-exclude = ''' +( + docs + | tests +) +''' [tool.mypy] disable_error_code = "annotation-unchecked" diff --git a/tests/test_vao.py b/tests/test_vao.py index 16826041..97dfe936 100644 --- a/tests/test_vao.py +++ b/tests/test_vao.py @@ -36,9 +36,13 @@ def createProgram(self): def test_create(self): mesh = VAO("test", mode=moderngl.LINES) - mesh.buffer(numpy.array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0], dtype='f4'), '3f', 'position') - mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0, 0.0, 1.0], dtype='f4'), '3f', 'normal') - mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0], dtype='f4'), '2f', 'uv') + mesh.buffer( + numpy.array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0], dtype="f4"), "3f", "position" + ) + mesh.buffer( + numpy.array([0.0, 0.0, 1.0, 1.0, 0.0, 1.0], dtype="f4"), "3f", "normal" + ) + mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0], dtype="f4"), "2f", "uv") # Ensure basic properties are correct self.assertEqual(mesh.name, "test") @@ -46,10 +50,10 @@ def test_create(self): self.assertEqual(mesh.mode, moderngl.LINES) # Ensure buffers are present - self.assertIsInstance(mesh.get_buffer_by_name('position'), BufferInfo) - self.assertIsInstance(mesh.get_buffer_by_name('normal'), BufferInfo) - self.assertIsInstance(mesh.get_buffer_by_name('uv'), BufferInfo) - self.assertEqual(mesh.get_buffer_by_name('something'), None) + self.assertIsInstance(mesh.get_buffer_by_name("position"), BufferInfo) + self.assertIsInstance(mesh.get_buffer_by_name("normal"), BufferInfo) + self.assertIsInstance(mesh.get_buffer_by_name("uv"), BufferInfo) + self.assertEqual(mesh.get_buffer_by_name("something"), None) # Create a progam using a subset of the buffers prog = self.createProgram() @@ -69,15 +73,19 @@ def test_add_illegal_buffer(self): """Attempt to add illegal buffer""" vao = VAO() with self.assertRaises(VAOError): - vao.buffer("stuff", '1f', 'in_position') + vao.buffer("stuff", "1f", "in_position") def test_normalized_types(self): """Ensure VAO wrapper can handle normalized types""" - prog = self.createProgram() + # prog = self.createProgram() mesh = VAO("test", mode=moderngl.LINES) - mesh.buffer(numpy.array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0], dtype='i4'), '3ni', 'position') - mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0, 0.0, 1.0], dtype='f4'), '3nf', 'normal') - mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0], dtype='f4'), '2f', 'uv') + mesh.buffer( + numpy.array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0], dtype="i4"), "3ni", "position" + ) + mesh.buffer( + numpy.array([0.0, 0.0, 1.0, 1.0, 0.0, 1.0], dtype="f4"), "3nf", "normal" + ) + mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0], dtype="f4"), "2f", "uv") # Uncomment in moderngl 5.6+ # mesh.instance(prog) @@ -85,15 +93,23 @@ def test_normalized_types(self): def test_divisors(self): """Test defining buffers with different divisor types""" mesh = VAO("test", mode=moderngl.LINES) - mesh.buffer(numpy.array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0], dtype='f4'), '3f/v', 'position') - mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0, 0.0, 1.0], dtype='f4'), '3f/r', 'normal') - mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0], dtype='f4'), '2f/i', 'uv') - - buffer1 = mesh.get_buffer_by_name('position') - buffer2 = mesh.get_buffer_by_name('normal') - buffer3 = mesh.get_buffer_by_name('uv') - - attributes = ['position', 'normal', 'uv'] - self.assertEqual(buffer1.content(attributes), (buffer1.buffer, '3f/v', 'position')) - self.assertEqual(buffer2.content(attributes), (buffer2.buffer, '3f/r', 'normal')) - self.assertEqual(buffer3.content(attributes), (buffer3.buffer, '2f/i', 'uv')) + mesh.buffer( + numpy.array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0], dtype="f4"), "3f/v", "position" + ) + mesh.buffer( + numpy.array([0.0, 0.0, 1.0, 1.0, 0.0, 1.0], dtype="f4"), "3f/r", "normal" + ) + mesh.buffer(numpy.array([0.0, 0.0, 1.0, 1.0], dtype="f4"), "2f/i", "uv") + + buffer1 = mesh.get_buffer_by_name("position") + buffer2 = mesh.get_buffer_by_name("normal") + buffer3 = mesh.get_buffer_by_name("uv") + + attributes = ["position", "normal", "uv"] + self.assertEqual( + buffer1.content(attributes), (buffer1.buffer, "3f/v", "position") + ) + self.assertEqual( + buffer2.content(attributes), (buffer2.buffer, "3f/r", "normal") + ) + self.assertEqual(buffer3.content(attributes), (buffer3.buffer, "2f/i", "uv"))