From f5954008f3adc247198d730138250aafabbcbc04 Mon Sep 17 00:00:00 2001 From: Maharshi Gor Date: Fri, 20 Dec 2024 21:15:33 +0530 Subject: [PATCH] RF: Flatten the window folder to the file (#955) * RF: Updated the window file - Flatten the window folder to the file. - Added all the pygfx imports from window file to lib. - Created jupyter-rfb as optional dependency under plot. * RF: dependency update - Optional package used to trigger tripwire. - Added pygfx and glfw to dependencies in pyproject.toml. --- fury/__init__.pyi | 45 ++--- fury/lib.py | 38 +++- fury/{window/show_manager.py => window.py} | 204 +++++++++++++++++++-- fury/window/__init__.py | 3 - fury/window/__init__.pyi | 21 --- fury/window/screen.py | 159 ---------------- pyproject.toml | 4 +- 7 files changed, 237 insertions(+), 237 deletions(-) rename fury/{window/show_manager.py => window.py} (51%) delete mode 100644 fury/window/__init__.py delete mode 100644 fury/window/__init__.pyi delete mode 100644 fury/window/screen.py diff --git a/fury/__init__.pyi b/fury/__init__.pyi index ddd86e647..3ff01f165 100644 --- a/fury/__init__.pyi +++ b/fury/__init__.pyi @@ -30,7 +30,9 @@ __all__ = [ "transform", # "ui", "utils", - # "window", + "window", + "ShowManager", + "Scene", ] # # the explicit definition of `__all__` will enable type inference for engines. @@ -46,8 +48,6 @@ from . import ( # gltf, # interactor, io, - # ui, - # window, lib, # layout, optpkg, @@ -58,6 +58,8 @@ from . import ( testing, transform, utils, + # ui, + window, ) # # from .actor import ( @@ -407,31 +409,18 @@ from .utils import ( tangents_from_direction_of_anisotropy as tangents_from_direction_of_anisotropy, triangle_order as triangle_order, ) - -# from .window import ( -# Scene as Scene, -# ShowManager as ShowManager, -# analyze_scene as analyze_scene, -# analyze_snapshot as analyze_snapshot, -# antialiasing as antialiasing, -# enable_stereo as enable_stereo, -# gl_disable_blend as gl_disable_blend, -# gl_disable_depth as gl_disable_depth, -# gl_enable_blend as gl_enable_blend, -# gl_enable_depth as gl_enable_depth, -# gl_get_current_state as gl_get_current_state, -# gl_reset_blend as gl_reset_blend, -# gl_set_additive_blending as gl_set_additive_blending, -# gl_set_additive_blending_white_background -# as gl_set_additive_blending_white_background, -# gl_set_multiplicative_blending as gl_set_multiplicative_blending, -# gl_set_normal_blending as gl_set_normal_blending, -# gl_set_subtractive_blending as gl_set_subtractive_blending, -# record as record, -# release_context as release_context, -# show as show, -# snapshot as snapshot, -# ) +from .window import ( + Scene as Scene, + Screen as Screen, + ShowManager as ShowManager, + calculate_screen_sizes as calculate_screen_sizes, + create_screen as create_screen, + display as display, + render_screens as render_screens, + snapshot as snapshot, + update_camera as update_camera, + update_viewports as update_viewports, +) __version__: str diff --git a/fury/lib.py b/fury/lib.py index 2ccf35d11..a8986f574 100644 --- a/fury/lib.py +++ b/fury/lib.py @@ -1,14 +1,42 @@ +from typing import TypeAlias + import pygfx as gfx +from wgpu.gui.auto import WgpuCanvas, run +from wgpu.gui.offscreen import WgpuCanvas as OffscreenWgpuCanvas + +from fury.optpkg import optional_package + +jupyter_pckg_msg = ( + "You do not have jupyter-rfb installed. The jupyter widget will not work for " + "you. Please install or upgrade jupyter-rfb using pip install -U jupyter-rfb" +) + +jupyter_rfb, have_jupyter_rfb, _ = optional_package( + "jupyter-rfb", trip_msg=jupyter_pckg_msg +) + +if have_jupyter_rfb: + from wgpu.gui.jupyter import WgpuCanvas as JupyterWgpuCanvas Texture = gfx.Texture AmbientLight = gfx.AmbientLight Background = gfx.Background BackgroundSkyboxMaterial = gfx.BackgroundSkyboxMaterial -Camera = gfx.Camera -Controller = gfx.Controller + +# Classes that needed to be written as types +Camera: TypeAlias = gfx.Camera +Controller: TypeAlias = gfx.Controller +Scene: TypeAlias = gfx.Scene +Viewport: TypeAlias = gfx.Viewport + DirectionalLight = gfx.DirectionalLight OrbitController = gfx.OrbitController PerspectiveCamera = gfx.PerspectiveCamera -GfxScene = gfx.Scene -Viewport = gfx.Viewport -WgpuRenderer = gfx.WgpuRenderer +Renderer = gfx.WgpuRenderer +run = run +Canvas = WgpuCanvas +OffscreenCanvas = OffscreenWgpuCanvas +if have_jupyter_rfb: + JupyterCanvas = JupyterWgpuCanvas +else: + JupyterCanvas = jupyter_rfb diff --git a/fury/window/show_manager.py b/fury/window.py similarity index 51% rename from fury/window/show_manager.py rename to fury/window.py index 2d3fe3491..d6ead20f7 100644 --- a/fury/window/show_manager.py +++ b/fury/window.py @@ -1,21 +1,173 @@ +from dataclasses import dataclass from functools import reduce from PIL.Image import fromarray as image_from_array -from numpy import asarray as np_asarray -from wgpu.gui.auto import WgpuCanvas, run -from wgpu.gui.jupyter import WgpuCanvas as JupyterWgpuCanvas -from wgpu.gui.offscreen import WgpuCanvas as OffscreenWgpuCanvas - -from fury.lib import WgpuRenderer -from fury.window import ( - Scene, - calculate_screen_sizes, - create_screen, - render_screens, - update_viewports, +import numpy as np + +from fury.lib import ( + AmbientLight, + Background, + BackgroundSkyboxMaterial, + Camera, + Canvas, + Controller, + DirectionalLight, + JupyterCanvas, + OffscreenCanvas, + OrbitController, + PerspectiveCamera, + Renderer, + Scene as GfxScene, # type: ignore + Viewport, + run, ) +class Scene(GfxScene): + def __init__( + self, + *, + background=(0, 0, 0, 1), + skybox=None, + lights=None, + ): + super().__init__() + + self._bg_color = background + self._bg_actor = None + + if skybox: + self._bg_actor = self._skybox(skybox) + else: + self._bg_actor = Background.from_color(background) + + self.add(self._bg_actor) + + self.lights = lights + if self.lights is None: + self.lights = [] + self.lights.append(AmbientLight()) + + self.add(*self.lights) + + def _skybox(self, cube_map): + return Background( + geometry=None, material=BackgroundSkyboxMaterial(map=cube_map) + ) + + @property + def background(self): + return self._background_color + + @background.setter + def background(self, value): + self.remove(self._bg_actor) + self._bg_actor = Background.from_color(value) + self.add(self._bg_actor) + + def set_skybox(self, cube_map): + self.remove(self._bg_actor) + self._bg_actor = self._skybox(cube_map) + self.add(self._bg_actor) + + def clear(self): + self.remove(*self.children) + + +@dataclass +class Screen: + viewport: Viewport + scene: Scene + camera: Camera + controller: Controller + + @property + def size(self): + return self.viewport.rect[2:] + + @property + def position(self): + return self.viewport.rect[:2] + + @property + def bounding_box(self): + return self.viewport.rect + + @bounding_box.setter + def bounding_box(self, value): + self.viewport.rect = value + + +def create_screen( + renderer, *, rect=None, scene=None, camera=None, controller=None, camera_light=True +): + vp = Viewport(renderer, rect) + if scene is None: + scene = Scene() + if camera is None: + camera = PerspectiveCamera(50) + if camera_light: + light = DirectionalLight() + camera.add(light) + scene.add(camera) + + if controller is None: + controller = OrbitController(camera, register_events=vp) + + screen = Screen(vp, scene, camera, controller) + update_camera(camera, screen.size, scene) + return screen + + +def update_camera(camera, size, target): + camera.width = size[0] + camera.height = size[1] + + if (isinstance(target, Scene) and len(target.children) > 3) or not isinstance( + target, Scene + ): + camera.show_object(target) + + +def update_viewports(screens, screen_bbs): + for screen, screen_bb in zip(screens, screen_bbs): + screen.bounding_box = screen_bb + update_camera(screen.camera, screen.size, screen.scene) + + +def render_screens(renderer, screens): + for screen in screens: + screen.viewport.render(screen.scene, screen.camera, flush=False) + + renderer.flush() + + +def calculate_screen_sizes(screens, size): + if screens is None: + return [(0, 0, *size)] + + screen_bbs = [] + + v_sections = len(screens) + width = (1 / v_sections) * size[0] + x = 0 + + for h_section in screens: + if h_section == 0: + continue + + height = (1 / h_section) * size[1] + y = 0 + + for _ in range(h_section): + screen_bbs.append((x, y, width, height)) + y += height + + x += width + + return screen_bbs + + class ShowManager: def __init__( self, @@ -44,7 +196,7 @@ def __init__( self._setup_window(window_type) if renderer is None: - renderer = WgpuRenderer(self.window) + renderer = Renderer(self.window) self.renderer = renderer self.renderer.pixel_ratio = pixel_ratio self.renderer.blend_mode = blend_mode @@ -88,13 +240,13 @@ def _screen_setup(self, scene, camera, controller, camera_light): def _setup_window(self, window_type): if window_type == "auto": - self.window = WgpuCanvas(size=self.size, title=self._title) + self.window = Canvas(size=self.size, title=self._title) elif window_type == "jupyter": - self.window = JupyterWgpuCanvas(size=self.size, title=self._title) + self.window = JupyterCanvas(size=self.size, title=self._title) elif window_type == "offscreen": - self.window = OffscreenWgpuCanvas(size=self.size, title=self._title) + self.window = OffscreenCanvas(size=self.size, title=self._title) else: - self.window = WgpuCanvas(size=self.size, title=self._title) + self.window = Canvas(size=self.size, title=self._title) def _calculate_total_screens(self): if self._screen_config is None: @@ -150,8 +302,10 @@ def enable_events(self, value): s.controller.enabled = value def snapshot(self, fname): - img = image_from_array(np_asarray(self.renderer.snapshot())) + arr = np.asarray(self.renderer.snapshot()) + img = image_from_array(arr) img.save(fname) + return arr def render(self): self.window.request_draw(lambda: render_screens(self.renderer, self.screens)) @@ -168,7 +322,14 @@ def resize(self, _event): self.render() -def record(*, scene=None, screen_config=None, fname="output.png", actors=None): +def snapshot( + *, + scene=None, + screen_config=None, + fname="output.png", + actors=None, + return_array=False, +): if actors is not None: scene = Scene() scene.add(*actors) @@ -178,7 +339,10 @@ def record(*, scene=None, screen_config=None, fname="output.png", actors=None): ) show_m.render() show_m.window.draw() - show_m.snapshot(fname) + arr = show_m.snapshot(fname) + + if return_array: + return arr def display(*, actors): diff --git a/fury/window/__init__.py b/fury/window/__init__.py deleted file mode 100644 index 62d86f776..000000000 --- a/fury/window/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import lazy_loader as lazy - -__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) diff --git a/fury/window/__init__.pyi b/fury/window/__init__.pyi deleted file mode 100644 index c7588f690..000000000 --- a/fury/window/__init__.pyi +++ /dev/null @@ -1,21 +0,0 @@ -__all__ = [ - "ShowManager", - "Scene", - "record", - "update_camera", - "update_viewports", - "render_screens", - "calculate_screen_sizes", - "create_screen", - "display", -] - -from .screen import ( - Scene, - calculate_screen_sizes, - create_screen, - render_screens, - update_camera, - update_viewports, -) -from .show_manager import ShowManager, display, record diff --git a/fury/window/screen.py b/fury/window/screen.py deleted file mode 100644 index 77fb19aa8..000000000 --- a/fury/window/screen.py +++ /dev/null @@ -1,159 +0,0 @@ -from dataclasses import dataclass - -from fury.lib import ( - AmbientLight, - Background, - BackgroundSkyboxMaterial, - Camera, - Controller, - DirectionalLight, - GfxScene, - OrbitController, - PerspectiveCamera, - Viewport, -) - - -class Scene(GfxScene): - def __init__( - self, - *, - background=(0, 0, 0, 1), - skybox=None, - lights=None, - ): - super().__init__() - - self._bg_color = background - self._bg_actor = None - - if skybox: - self._bg_actor = self._skybox(skybox) - else: - self._bg_actor = Background.from_color(background) - - self.add(self._bg_actor) - - self.lights = lights - if self.lights is None: - self.lights = [] - self.lights.append(AmbientLight()) - - self.add(*self.lights) - - def _skybox(self, cube_map): - return Background( - geometry=None, material=BackgroundSkyboxMaterial(map=cube_map) - ) - - @property - def background(self): - return self._background_color - - @background.setter - def background(self, value): - self.remove(self._bg_actor) - self._bg_actor = Background.from_color(value) - self.add(self._bg_actor) - - def set_skybox(self, cube_map): - self.remove(self._bg_actor) - self._bg_actor = self._skybox(cube_map) - self.add(self._bg_actor) - - def clear(self): - self.remove(*self.children) - - -@dataclass -class Screen: - viewport: Viewport - scene: Scene - camera: Camera - controller: Controller - - @property - def size(self): - return self.viewport.rect[2:] - - @property - def position(self): - return self.viewport.rect[:2] - - @property - def bounding_box(self): - return self.viewport.rect - - @bounding_box.setter - def bounding_box(self, value): - self.viewport.rect = value - - -def create_screen( - renderer, *, rect=None, scene=None, camera=None, controller=None, camera_light=True -): - vp = Viewport(renderer, rect) - if scene is None: - scene = Scene() - if camera is None: - camera = PerspectiveCamera(50) - if camera_light: - light = DirectionalLight() - camera.add(light) - scene.add(camera) - - if controller is None: - controller = OrbitController(camera, register_events=vp) - - screen = Screen(vp, scene, camera, controller) - update_camera(camera, screen.size, scene) - return screen - - -def update_camera(camera, size, target): - camera.width = size[0] - camera.height = size[1] - - if (isinstance(target, Scene) and len(target.children) > 3) or not isinstance( - target, Scene - ): - camera.show_object(target) - - -def update_viewports(screens, screen_bbs): - for screen, screen_bb in zip(screens, screen_bbs): - screen.bounding_box = screen_bb - update_camera(screen.camera, screen.size, screen.scene) - - -def render_screens(renderer, screens): - for screen in screens: - screen.viewport.render(screen.scene, screen.camera, flush=False) - - renderer.flush() - - -def calculate_screen_sizes(screens, size): - if screens is None: - return [(0, 0, *size)] - - screen_bbs = [] - - v_sections = len(screens) - width = (1 / v_sections) * size[0] - x = 0 - - for h_section in screens: - if h_section == 0: - continue - - height = (1 / h_section) * size[1] - y = 0 - - for _ in range(h_section): - screen_bbs.append((x, y, width, height)) - y += height - - x += width - - return screen_bbs diff --git a/pyproject.toml b/pyproject.toml index be0f0aa3c..158e88189 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,8 @@ dependencies = [ "pillow>=10.4.0", "packaging>=23.0", "lazy_loader>=0.4", + "pygfx>=0.3.0", + "glfw>=2.7.0", ] dynamic = ["version"] @@ -59,7 +61,7 @@ Tracker = "https://github.com/fury-gl/fury/issues" [project.optional-dependencies] all = ["fury[plot, dev, doc, medical, style, test, typing]"] -plot = ["matplotlib>=3.9.0", ] +plot = ["matplotlib>=3.9.0", "jupyter-rfb"] medical = ["dipy", "nibabel"] dev = [ "gitpython",