import importlib from itertools import product import numpy as np import pygfx from pygfx import WgpuRenderer, Texture, Renderer from ..utils.gui import BaseRenderCanvas, RenderCanvas try: import imgui_bundle except ImportError: IMGUI = False else: IMGUI = True # number of pixels taken by the imgui toolbar when present IMGUI_TOOLBAR_HEIGHT = 36 def make_canvas_and_renderer( canvas: str | BaseRenderCanvas | Texture | None, renderer: Renderer | None, canvas_kwargs: dict, ): """ Parses arguments and returns the appropriate canvas and renderer instances as a tuple (canvas, renderer) """ if canvas is None: canvas = RenderCanvas(**canvas_kwargs) elif isinstance(canvas, str): import rendercanvas m = importlib.import_module("rendercanvas." + canvas) canvas = m.RenderCanvas(**canvas_kwargs) elif not isinstance(canvas, (BaseRenderCanvas, Texture)): raise TypeError( f"canvas option must either be a valid BaseRenderCanvas implementation, a pygfx Texture" f" or a str with the gui backend name, valid str are: 'qt', 'glfw', 'jupyter', 'wx', and 'offscreen'" ) if renderer is None: renderer = WgpuRenderer(canvas) elif not isinstance(renderer, Renderer): raise TypeError( f"renderer option must be a pygfx.Renderer instance such as pygfx.WgpuRenderer" ) return canvas, renderer def create_camera( camera_type: pygfx.PerspectiveCamera | str, ) -> pygfx.PerspectiveCamera: if isinstance(camera_type, pygfx.PerspectiveCamera): return camera_type elif camera_type == "2d": # use perspective for orthographic, makes it easier to then switch to controllers that make sense with fov > 0 return pygfx.PerspectiveCamera(fov=0) elif camera_type == "3d": return pygfx.PerspectiveCamera() else: raise ValueError( "camera must be one of: '2d', '3d' or an instance of pygfx.PerspectiveCamera" ) controller_types = { "fly": pygfx.FlyController, "panzoom": pygfx.PanZoomController, "trackball": pygfx.TrackballController, "orbit": pygfx.OrbitController, } def create_controller( controller_type: pygfx.Controller | None | str, camera: pygfx.PerspectiveCamera, ) -> pygfx.Controller: """ Creates the controllers and adds the camera to it. """ if isinstance(controller_type, pygfx.Controller): controller_type.add_camera(camera) return controller_type if controller_type is None: # default controllers if camera.fov == 0: # default for orthographic return pygfx.PanZoomController(camera) else: return pygfx.FlyController(camera) if controller_type not in controller_types.keys(): raise KeyError( f"controller must be a valid pygfx.Controller or one of: " f"{list(controller_types.keys())}, you have passed: {controller_type}" ) return controller_types[controller_type](camera) def get_extents_from_grid( shape: tuple[int, int], ) -> list[tuple[float, float, float, float]]: """create fractional extents from a given grid shape""" x_min = np.arange(0, 1, (1 / shape[1])) x_max = x_min + 1 / shape[1] y_min = np.arange(0, 1, (1 / shape[0])) y_max = y_min + 1 / shape[0] extents = list() for row_ix, col_ix in product(range(shape[0]), range(shape[1])): extent = x_min[col_ix], x_max[col_ix], y_min[row_ix], y_max[row_ix] extents.append(extent) return extents