Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RF: Flatten the window folder to the file #955

Merged
merged 2 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 17 additions & 28 deletions fury/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ __all__ = [
"transform",
# "ui",
"utils",
# "window",
"window",
"ShowManager",
"Scene",
]

# # the explicit definition of `__all__` will enable type inference for engines.
Expand All @@ -46,8 +48,6 @@ from . import (
# gltf,
# interactor,
io,
# ui,
# window,
lib,
# layout,
optpkg,
Expand All @@ -58,6 +58,8 @@ from . import (
testing,
transform,
utils,
# ui,
window,
)

# # from .actor import (
Expand Down Expand Up @@ -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

Expand Down
38 changes: 33 additions & 5 deletions fury/lib.py
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +26 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you need the TypeAlias here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It gives pre-commit error as these classes are used to either extend the class or used as type in a dataclass.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strange, ok, I look into it later, merging

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skoudoro you can take a look at this. The pre-commit points to this resource when it gives the error. https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases


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
204 changes: 184 additions & 20 deletions fury/window/show_manager.py → fury/window.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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))
Expand All @@ -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)
Expand All @@ -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):
Expand Down
3 changes: 0 additions & 3 deletions fury/window/__init__.py

This file was deleted.

21 changes: 0 additions & 21 deletions fury/window/__init__.pyi

This file was deleted.

Loading
Loading